a76cb2e19a1438310e38939571546a29cc1569cb
[rmac] / riscasm.c
1 //
2 // RMAC - Reboot's Macro Assembler for all Atari computers
3 // RISCA.C - GPU/DSP Assembler
4 // Copyright (C) 199x Landon Dyer, 2011-2017 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
7 //
8
9 #include "riscasm.h"
10 #include "amode.h"
11 #include "direct.h"
12 #include "error.h"
13 #include "expr.h"
14 #include "mark.h"
15 #include "procln.h"
16 #include "sect.h"
17 #include "token.h"
18
19 #define DEF_MR                          // Declare keyword values
20 #include "risckw.h"                     // Incl. generated risc keywords
21
22 #define DEF_KW                          // Declare keyword values
23 #include "kwtab.h"                      // Incl. generated keyword tables & defs
24
25
26 unsigned altbankok = 0;         // Ok to use alternate register bank
27 unsigned orgactive = 0;         // RISC/6502 org directive active
28 unsigned orgaddr = 0;           // Org'd address
29 unsigned orgwarning = 0;        // Has an ORG warning been issued
30 int lastOpcode = -1;            // Last RISC opcode assembled
31 uint8_t riscImmTokenSeen;       // The '#' (immediate) token was seen
32
33 const char reg_err[] = "missing register R0...R31";
34
35 // Jaguar jump condition names
36 const 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",
39         "PL", "MI", "F"
40 };
41
42 // Jaguar jump condition numbers
43 const char condnumber[] = {
44         1, 2, 4, 5, 6, 8, 9, 10, 20, 21, 22, 24, 25, 26,
45         0, 0, 1, 2, 4, 4, 5,  8,  8, 20, 24, 31
46 };
47
48 const struct opcoderecord roptbl[] = {
49         { MR_ADD,     RI_TWO,    0 },
50         { MR_ADDC,    RI_TWO,    1 },
51         { MR_ADDQ,    RI_NUM_32, 2 },
52         { MR_ADDQT,   RI_NUM_32, 3 },
53         { MR_SUB,     RI_TWO,    4 },
54         { MR_SUBC,    RI_TWO,    5 },
55         { MR_SUBQ,    RI_NUM_32, 6 },
56         { MR_SUBQT,   RI_NUM_32, 7 },
57         { MR_NEG,     RI_ONE,    8 },
58         { MR_AND,     RI_TWO,    9 },
59         { MR_OR,      RI_TWO,    10 },
60         { MR_XOR,     RI_TWO,    11 },
61         { MR_NOT,     RI_ONE,    12 },
62         { MR_BTST,    RI_NUM_31, 13 },
63         { MR_BSET,    RI_NUM_31, 14 },
64         { MR_BCLR,    RI_NUM_31, 15 },
65         { MR_MULT,    RI_TWO,    16 },
66         { MR_IMULT,   RI_TWO,    17 },
67         { MR_IMULTN,  RI_TWO,    18 },
68         { MR_RESMAC,  RI_ONE,    19 },
69         { MR_IMACN,   RI_TWO,    20 },
70         { MR_DIV,     RI_TWO,    21 },
71         { MR_ABS,     RI_ONE,    22 },
72         { MR_SH,      RI_TWO,    23 },
73         { MR_SHLQ,    RI_NUM_32, 24 + SUB32 },
74         { MR_SHRQ,    RI_NUM_32, 25 },
75         { MR_SHA,     RI_TWO,    26 },
76         { MR_SHARQ,   RI_NUM_32, 27 },
77         { MR_ROR,     RI_TWO,    28 },
78         { MR_RORQ,    RI_NUM_32, 29 },
79         { MR_ROLQ,    RI_NUM_32, 29 + SUB32 },
80         { MR_CMP,     RI_TWO,    30 },
81         { MR_CMPQ,    RI_NUM_15, 31 },
82         { MR_SAT8,    RI_ONE,    32 + GPUONLY },
83         { MR_SUBQMOD, RI_NUM_32, 32 + DSPONLY },
84         { MR_SAT16,   RI_ONE,    33 + GPUONLY },
85         { MR_SAT16S,  RI_ONE,    33 + DSPONLY },
86         { MR_MOVEQ,   RI_NUM_31, 35 },
87         { MR_MOVETA,  RI_TWO,    36 },
88         { MR_MOVEFA,  RI_TWO,    37 },
89         { MR_MOVEI,   RI_MOVEI,  38 },
90         { MR_LOADB,   RI_LOADN,  39 },
91         { MR_LOADW,   RI_LOADN,  40 },
92         { MR_LOADP,   RI_LOADN,  42 + GPUONLY },
93         { MR_SAT32S,  RI_ONE,    42 + DSPONLY },
94         { MR_STOREB,  RI_STOREN, 45 },
95         { MR_STOREW,  RI_STOREN, 46 },
96         { MR_STOREP,  RI_STOREN, 48 + GPUONLY },
97         { MR_MIRROR,  RI_ONE,    48 + DSPONLY },
98         { MR_JUMP,    RI_JUMP,   52 },
99         { MR_JR,      RI_JR,     53 },
100         { MR_MMULT,   RI_TWO,    54 },
101         { MR_MTOI,    RI_TWO,    55 },
102         { MR_NORMI,   RI_TWO,    56 },
103         { MR_NOP,     RI_NONE,   57 },
104         { MR_SAT24,   RI_ONE,    62 },
105         { MR_UNPACK,  RI_ONE,    63 + GPUONLY | (0 << 6) },
106         { MR_PACK,    RI_ONE,    63 + GPUONLY | (1 << 6) },
107         { MR_ADDQMOD, RI_NUM_32, 63 + DSPONLY },
108         { MR_MOVE,    RI_MOVE,   0 },
109         { MR_LOAD,    RI_LOAD,   0 },
110         { MR_STORE,   RI_STORE,  0 }
111 };
112
113
114 //
115 // Convert a string to uppercase
116 //
117 void strtoupper(char * s)
118 {
119         while (*s)
120                 *s++ &= 0xDF;
121 }
122
123
124 //
125 // Function to return "malformed expression" error
126 // This is done mainly to remove a bunch of GOTO statements in the parser
127 //
128 static inline int MalformedOpcode(int signal)
129 {
130         return error("Malformed opcode [internal $%02X]", signal);
131 }
132
133
134 //
135 // Function to return "Illegal Indexed Register" error
136 // Anyone trying to index something other than R14 or R15
137 //
138 static inline int IllegalIndexedRegister(int reg)
139 {
140         return error("Attempted index reference with non-indexable register (r%d)", reg - KW_R0);
141 }
142
143
144 //
145 // Function to return "Illegal Indexed Register" error for EQUR scenarios
146 // Trying to use register value within EQUR that isn't 14 or 15
147 //
148 static inline int IllegalIndexedRegisterEqur(SYM * sy)
149 {
150         return error("Attempted index reference with non-indexable register within EQUR (%s = r%d)", sy->sname, sy->svalue);
151 }
152
153
154 //
155 // Build RISC instruction word
156 //
157 void BuildRISCIntructionWord(unsigned short opcode, int reg1, int reg2)
158 {
159         // Check for absolute address setting
160         if (!orgwarning && !orgactive)
161         {
162                 warn("RISC code generated with no origin defined");
163                 orgwarning = 1;
164         }
165
166         int value = ((opcode & 0x3F) << 10) + ((reg1 & 0x1F) << 5) + (reg2 & 0x1F);
167         D_word(value);
168 //printf("BuildRISC: opcode=$%X, reg1=$%X, reg2=$%X, final=$%04X\n", opcode, reg1, reg2, value);
169 }
170
171
172 //
173 // Get a RISC register
174 //
175 int GetRegister(WORD rattr)
176 {
177         uint64_t 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
182         // Evaluate what's in the global "tok" buffer
183         if (expr((TOKENPTR)r_expr, &eval, &eattr, &esym) != OK)
184                 return ERROR;
185
186         if ((challoc - ch_size) < 4)
187                 chcheck(4L);
188
189         if (!(eattr & DEFINED))
190         {
191                 AddFixup((WORD)(FU_WORD | rattr), sloc, (TOKENPTR)r_expr);
192                 return 0;
193         }
194
195         // If we got a register in range (0-31), return it
196         if ((eval >= 0) && (eval <= 31))
197                 return (int)eval;
198
199         // Otherwise, it's out of range & we flag an error
200         return error(reg_err);
201 }
202
203
204 //
205 // Do RISC code generation
206 //
207 int GenerateRISCCode(int state)
208 {
209         int reg1;                                       // Register 1
210         int reg2;                                       // Register 2
211         int val = 0;                            // Constructed value
212         char scratch[80];
213         SYM * ccsym;
214         SYM * sy;
215         int i, commaFound;
216         TOKEN * t;
217         WORD attrflg;
218         int indexed;                            // Indexed register flag
219
220         uint64_t eval;                          // Expression value
221         WORD eattr;                                     // Expression attributes
222         SYM * esym;                                     // External symbol involved in expr.
223         TOKEN r_expr[EXPRSIZE];         // Expression token list
224
225         // Get opcode parameter and type
226         unsigned short parm = (WORD)(roptbl[state - 3000].parm);
227         unsigned type = roptbl[state - 3000].typ;
228         riscImmTokenSeen = 0;           // Set to "token not seen yet"
229
230         // Detect whether the opcode parmeter passed determines that the opcode is
231         // specific to only one of the RISC processors and ensure it is legal in
232         // the current code section. If not then show error and return.
233         if (((parm & GPUONLY) && rdsp) || ((parm & DSPONLY) && rgpu))
234                 return error("Opcode is not valid in this code section");
235
236         // Process RISC opcode
237         switch (type)
238         {
239         // No operand instructions
240         // NOP (57)
241         case RI_NONE:
242                 BuildRISCIntructionWord(parm, 0, 0);
243                 break;
244
245         // Single operand instructions (Rd)
246         // ABS, MIRROR, NEG, NOT, PACK, RESMAC, SAT8, SAT16, SAT16S, SAT24, SAT32S,
247         // UNPACK
248         case RI_ONE:
249                 reg2 = GetRegister(FU_REGTWO);
250                 at_eol();
251                 BuildRISCIntructionWord(parm, parm >> 6, reg2);
252                 break;
253
254         // Two operand instructions (Rs,Rd)
255         // ADD, ADDC, AND, CMP, DIV, IMACN, IMULT, IMULTN, MOVEFA, MOVETA, MULT,
256         // MMULT, MTOI, NORMI, OR, ROR, SH, SHA, SUB, SUBC, XOR
257         case RI_TWO:
258                 if (parm == 37)
259                         altbankok = 1;                      // MOVEFA
260
261                 reg1 = GetRegister(FU_REGONE);
262                 CHECK_COMMA;
263
264                 if (parm == 36)
265                         altbankok = 1;                      // MOVETA
266
267                 reg2 = GetRegister(FU_REGTWO);
268                 at_eol();
269                 BuildRISCIntructionWord(parm, reg1, reg2);
270                 break;
271
272         // Numeric operand (n,Rd) where n = -16..+15
273         // CMPQ
274         case RI_NUM_15:
275
276         // Numeric operand (n,Rd) where n = 0..31
277         // BCLR, BSET, BTST, MOVEQ
278         case RI_NUM_31:
279
280         // Numeric operand (n,Rd) where n = 1..32
281         // ADDQ, ADDQMOD, ADDQT, SHARQ, SHLQ, SHRQ, SUBQ, SUBQMOD, SUBQT, ROLQ,
282         // RORQ
283         case RI_NUM_32:
284                 switch (type)
285                 {
286                 case RI_NUM_15:
287                         reg1 = -16; reg2 = 15; attrflg = FU_NUM15;
288                         break;
289                 default:
290                 case RI_NUM_31:
291                         reg1 =   0; reg2 = 31; attrflg = FU_NUM31;
292                         break;
293                 case RI_NUM_32:
294                         reg1 =   1; reg2 = 32; attrflg = FU_NUM32;
295                         break;
296                 }
297
298                 if (parm & SUB32)
299                         attrflg |= FU_SUB32;
300
301                 if (*tok.u32 != '#')
302                         return MalformedOpcode(0x01);
303
304                 tok.u32++;
305                 riscImmTokenSeen = 1;
306
307                 if (expr((TOKENPTR)r_expr, &eval, &eattr, &esym) != OK)
308                         return MalformedOpcode(0x02);
309
310                 if ((challoc - ch_size) < 4)
311                         chcheck(4L);
312
313                 if (!(eattr & DEFINED))
314                 {
315                         AddFixup((WORD)(FU_WORD | attrflg), sloc, (TOKENPTR)r_expr);
316                         reg1 = 0;
317                 }
318                 else
319                 {
320                         if ((int)eval < reg1 || (int)eval > reg2)
321                                 return error("constant out of range");
322
323                         if (parm & SUB32)
324                                 reg1 = 32 - (int)eval;
325                         else if (type == RI_NUM_32)
326                                 reg1 = (reg1 == 32 ? 0 : (int)eval);
327                         else
328                                 reg1 = (int)eval;
329                 }
330
331                 CHECK_COMMA;
332                 reg2 = GetRegister(FU_REGTWO);
333                 at_eol();
334                 BuildRISCIntructionWord(parm, reg1, reg2);
335                 break;
336
337         // Move Immediate--n,Rn--n in Second Word
338         case RI_MOVEI:
339                 if (*tok.u32 != '#')
340                         return MalformedOpcode(0x03);
341
342                 tok.u32++;
343                 riscImmTokenSeen = 1;
344
345                 // Check for equated register after # and return error if so
346                 if (*tok.u32 == SYMBOL)
347                 {
348                         sy = lookup(string[tok.u32[1]], LABEL, 0);
349
350                         if (sy && (sy->sattre & EQUATEDREG))
351                                 return error("equated register in 1st operand of MOVEI instruction");
352                 }
353
354                 if (expr((TOKENPTR)r_expr, &eval, &eattr, &esym) != OK)
355                         return MalformedOpcode(0x04);
356
357                 if (lastOpcode == RI_JUMP || lastOpcode == RI_JR)
358                 {
359                         if (legacy_flag)
360                         {
361                                 // User doesn't care, emit a NOP to fix
362                                 BuildRISCIntructionWord(57, 0, 0);
363                                 warn("MOVEI following JUMP, inserting NOP to fix your BROKEN CODE");
364                         }
365                         else
366                                 warn("MOVEI immediately follows JUMP");
367                 }
368
369                 if ((challoc - ch_size) < 4)
370                         chcheck(4L);
371
372                 if (!(eattr & DEFINED))
373                 {
374                         AddFixup(FU_LONG | FU_MOVEI, sloc + 2, (TOKENPTR)r_expr);
375                         eval = 0;
376                 }
377                 else
378                 {
379                         if (eattr & TDB)
380 //{
381 //printf("RISCASM: Doing MarkRelocatable for RI_MOVEI (tdb=$%X)...\n", eattr & TDB);
382                                 MarkRelocatable(cursect, sloc + 2, (eattr & TDB), (MLONG | MMOVEI), NULL);
383 //}
384                 }
385
386 //              val = ((eval >> 16) & 0x0000FFFF) | ((eval << 16) & 0xFFFF0000);
387                 val = WORDSWAP32(eval);
388                 CHECK_COMMA;
389                 reg2 = GetRegister(FU_REGTWO);
390                 at_eol();
391                 D_word((((parm & 0x3F) << 10) + reg2));
392                 D_long(val);
393                 break;
394
395         // PC,Rd or Rs,Rd
396         case RI_MOVE:
397                 if (*tok.u32 == KW_PC)
398                 {
399                         parm = 51;
400                         reg1 = 0;
401                         tok.u32++;
402                 }
403                 else
404                 {
405                         parm = 34;
406                         reg1 = GetRegister(FU_REGONE);
407                 }
408
409                 CHECK_COMMA;
410                 reg2 = GetRegister(FU_REGTWO);
411                 at_eol();
412                 BuildRISCIntructionWord(parm, reg1, reg2);
413                 break;
414
415         // (Rn),Rn = 41 / (R14/R15+n),Rn = 43/44 / (R14/R15+Rn),Rn = 58/59
416         case RI_LOAD:
417                 indexed = 0;
418                 parm = 41;
419
420                 if (*tok.u32 != '(')
421                         return MalformedOpcode(0x05);
422
423                 tok.u32++;
424
425         if ((*(tok.u32 + 1) == '+') || (*(tok.u32 + 1) == '-')) {
426             // Trying to make indexed call
427             if ((*tok.u32 == KW_R14 || *tok.u32 == KW_R15)) {
428                 indexed = (*tok.u32 - KW_R0);
429             } else {
430                 return IllegalIndexedRegister(*tok.u32);
431             }
432         }
433
434                 if (*tok.u32 == SYMBOL)
435                 {
436 //                      sy = lookup((char *)tok.u32[1], LABEL, 0);
437                         sy = lookup(string[tok.u32[1]], LABEL, 0);
438
439                         if (!sy)
440                         {
441                                 error(reg_err);
442                                 return ERROR;
443                         }
444
445                         if (sy->sattre & EQUATEDREG)
446                         {
447                                 if ((*(tok.u32 + 2) == '+') || (*(tok.u32 + 2) == '-')) {
448                                     if ((sy->svalue & 0x1F) == 14 || (sy->svalue & 0x1F) == 15) {
449                                         indexed = (sy->svalue & 0x1F);
450                         tok.u32++;
451                                     } else {
452                                         return IllegalIndexedRegisterEqur(sy);
453                                     }
454                                 }
455                         }
456                 }
457
458                 if (!indexed)
459                 {
460                         reg1 = GetRegister(FU_REGONE);
461                 }
462                 else
463                 {
464                         reg1 = indexed;
465                         indexed = 0;
466                         tok.u32++;
467
468                         if (*tok.u32 == '+')
469                         {
470                                 parm = (WORD)(reg1 - 14 + 58);
471                                 tok.u32++;
472
473                                 if (*tok.u32 >= KW_R0 && *tok.u32 <= KW_R31)
474                                         indexed = 1;
475
476                                 if (*tok.u32 == SYMBOL)
477                                 {
478 //                                      sy = lookup((char *)tok.u32[1], LABEL, 0);
479                                         sy = lookup(string[tok.u32[1]], LABEL, 0);
480
481                                         if (!sy)
482                                         {
483                                                 error(reg_err);
484                                                 return ERROR;
485                                         }
486
487                                         if (sy->sattre & EQUATEDREG)
488                                                 indexed = 1;
489                                 }
490
491                                 if (indexed)
492                                 {
493                                         reg1 = GetRegister(FU_REGONE);
494                                 }
495                                 else
496                                 {
497                                         if (expr((TOKENPTR)r_expr, &eval, &eattr, &esym) != OK)
498                                                 return MalformedOpcode(0x06);
499
500                                         if ((challoc - ch_size) < 4)
501                                                 chcheck(4L);
502
503                                         if (!(eattr & DEFINED))
504                                                 return error("constant expected after '+'");
505
506                                         reg1 = (int)eval;
507
508                                         if (reg1 == 0)
509                                         {
510                                                 reg1 = 14 + (parm - 58);
511                                                 parm = 41;
512                                                 warn("NULL offset in LOAD ignored");
513                                         }
514                                         else
515                                         {
516                                                 if (reg1 < 1 || reg1 > 32)
517                                                         return error("constant in LOAD out of range");
518
519                                                 if (reg1 == 32)
520                                                         reg1 = 0;
521
522                                                 parm = (WORD)(parm - 58 + 43);
523                                         }
524                                 }
525                         }
526                         else
527                         {
528                                 reg1 = GetRegister(FU_REGONE);
529                         }
530                 }
531
532                 if (*tok.u32 != ')')
533                         return MalformedOpcode(0x07);
534
535                 tok.u32++;
536                 CHECK_COMMA;
537                 reg2 = GetRegister(FU_REGTWO);
538                 at_eol();
539                 BuildRISCIntructionWord(parm, reg1, reg2);
540                 break;
541
542         // Rn,(Rn) = 47 / Rn,(R14/R15+n) = 49/50 / Rn,(R14/R15+Rn) = 60/61
543         case RI_STORE:
544                 parm = 47;
545                 reg1 = GetRegister(FU_REGONE);
546                 CHECK_COMMA;
547
548                 if (*tok.u32 != '(')
549                         return MalformedOpcode(0x08);
550
551                 tok.u32++;
552                 indexed = 0;
553
554                 if ((*tok.u32 == KW_R14 || *tok.u32 == KW_R15) && (*(tok.u32 + 1) != ')'))
555                         indexed = (*tok.u32 - KW_R0);
556
557                 if (*tok.u32 == SYMBOL)
558                 {
559                         sy = lookup(string[tok.u32[1]], LABEL, 0);
560
561                         if (!sy)
562                         {
563                                 error(reg_err);
564                                 return ERROR;
565                         }
566
567                         if (sy->sattre & EQUATEDREG)
568                         {
569                                 if (((sy->svalue & 0x1F) == 14 || (sy->svalue & 0x1F) == 15)
570                                         && (*(tok.u32 + 2) != ')'))
571                                 {
572                                         indexed = (sy->svalue & 0x1F);
573                                         tok.u32++;
574                                 }
575                         }
576                 }
577
578                 if (!indexed)
579                 {
580                         reg2 = GetRegister(FU_REGTWO);
581                 }
582                 else
583                 {
584                         reg2 = indexed;
585                         indexed = 0;
586                         tok.u32++;
587
588                         if (*tok.u32 == '+')
589                         {
590                                 parm = (WORD)(reg2 - 14 + 60);
591                                 tok.u32++;
592
593                                 if (*tok.u32 >= KW_R0 && *tok.u32 <= KW_R31)
594                                         indexed = 1;
595
596                                 if (*tok.u32 == SYMBOL)
597                                 {
598                                         sy = lookup(string[tok.u32[1]], LABEL, 0);
599
600                                         if (!sy)
601                                         {
602                                                 error(reg_err);
603                                                 return ERROR;
604                                         }
605
606                                         if (sy->sattre & EQUATEDREG)
607                                                 indexed = 1;
608                                 }
609
610                                 if (indexed)
611                                 {
612                                         reg2 = GetRegister(FU_REGTWO);
613                                 }
614                                 else
615                                 {
616                                         if (expr((TOKENPTR)r_expr, &eval, &eattr, &esym) != OK)
617                                                 return MalformedOpcode(0x09);
618
619                                         if ((challoc - ch_size) < 4)
620                                                 chcheck(4L);
621
622                                         if (!(eattr & DEFINED))
623                                         {
624                                                 AddFixup(FU_WORD | FU_REGTWO, sloc, (TOKENPTR)r_expr);
625                                                 reg2 = 0;
626                                         }
627                                         else
628                                         {
629                                                 reg2 = (int)eval;
630
631                                                 if (reg2 == 0)
632                                                 {
633                                                         reg2 = 14 + (parm - 60);
634                                                         parm = 47;
635                                                         warn("NULL offset in STORE ignored");
636                                                 }
637                                                 else
638                                                 {
639                                                         if (reg2 < 1 || reg2 > 32)
640                                                                 return error("constant in STORE out of range");
641
642                                                         if (reg2 == 32)
643                                                                 reg2 = 0;
644
645                                                         parm = (WORD)(parm - 60 + 49);
646                                                 }
647                                         }
648                                 }
649                         }
650                         else
651                         {
652                                 reg2 = GetRegister(FU_REGTWO);
653                         }
654                 }
655
656                 if (*tok.u32 != ')')
657                         return MalformedOpcode(0x0A);
658
659                 tok.u32++;
660                 at_eol();
661                 BuildRISCIntructionWord(parm, reg2, reg1);
662                 break;
663
664         // LOADB/LOADP/LOADW (Rn),Rn
665         case RI_LOADN:
666                 if (*tok.u32 != '(')
667                         return MalformedOpcode(0x0B);
668
669                 tok.u32++;
670                 reg1 = GetRegister(FU_REGONE);
671
672                 if (*tok.u32 != ')')
673                         return MalformedOpcode(0x0C);
674
675                 tok.u32++;
676                 CHECK_COMMA;
677                 reg2 = GetRegister(FU_REGTWO);
678                 at_eol();
679                 BuildRISCIntructionWord(parm, reg1, reg2);
680                 break;
681
682         // STOREB/STOREP/STOREW Rn,(Rn)
683         case RI_STOREN:
684                 reg1 = GetRegister(FU_REGONE);
685                 CHECK_COMMA;
686
687                 if (*tok.u32 != '(')
688                         return MalformedOpcode(0x0D);
689
690                 tok.u32++;
691                 reg2 = GetRegister(FU_REGTWO);
692
693                 if (*tok.u32 != ')')
694                         return MalformedOpcode(0x0E);
695
696                 tok.u32++;
697                 at_eol();
698                 BuildRISCIntructionWord(parm, reg2, reg1);
699                 break;
700
701         // Jump Relative - cc,n - n=-16..+15 words, reg2=cc
702         case RI_JR:
703
704         // Jump Absolute - cc,(Rs) - reg2=cc
705         case RI_JUMP:
706                 // Check to see if there is a comma in the token string. If not then
707                 // the JR or JUMP should default to 0, Jump Always
708                 commaFound = 0;
709
710                 for(t=tok.u32; *t!=EOL; t++)
711                 {
712                         if (*t == ',')
713                         {
714                                 commaFound = 1;
715                                 break;
716                         }
717                 }
718
719                 if (commaFound)
720                 {
721                         if (*tok.u32 == CONST)
722                         {
723                                 // CC using a constant number
724                                 tok.u32++;
725                                 uint64_t *tok64 = (uint64_t *)tok.u32;
726                                 val = (int)*tok64++;
727                                 tok.u32 = (uint32_t *)tok64;
728                                 CHECK_COMMA;
729                         }
730                         else if (*tok.u32 == SYMBOL)
731                         {
732                                 val = 99;
733 //                              strcpy(scratch, (char *)tok.u32[1]);
734                                 strcpy(scratch, string[tok.u32[1]]);
735                                 strtoupper(scratch);
736
737                                 for(i=0; i<MAXINTERNCC; i++)
738                                 {
739                                         // Look for the condition code & break if found
740                                         if (strcmp(condname[i], scratch) == 0)
741                                         {
742                                                 val = condnumber[i];
743                                                 break;
744                                         }
745                                 }
746
747                                 // Standard CC was not found, look for an equated one
748                                 if (val == 99)
749                                 {
750 //                                      ccsym = lookup((char *)tok.u32[1], LABEL, 0);
751                                         ccsym = lookup(string[tok.u32[1]], LABEL, 0);
752
753                                         if (ccsym && (ccsym->sattre & EQUATEDCC) && !(ccsym->sattre & UNDEF_CC))
754                                                 val = ccsym->svalue;
755                                         else
756                                                 return error("unknown condition code");
757                                 }
758
759                                 tok.u32 += 2;
760                                 CHECK_COMMA;
761                         }
762                         else if (*tok.u32 == '(')
763                         {
764                                 // Set CC to "Jump Always"
765                                 val = 0;
766                         }
767                 }
768                 else
769                 {
770                         // Set CC to "Jump Always"
771                         val = 0;
772                 }
773
774                 if (val < 0 || val > 31)
775                         return error("condition constant out of range");
776
777                 // Store condition code
778                 reg1 = val;
779
780                 if (type == RI_JR)
781                 {
782                         // JR cc,n
783                         if (expr((TOKENPTR)r_expr, &eval, &eattr, &esym) != OK)
784                                 return MalformedOpcode(0x0F);
785
786                         if ((challoc - ch_size) < 4)
787                                 chcheck(4L);
788
789                         if (!(eattr & DEFINED))
790                         {
791                                 AddFixup(FU_WORD | FU_JR, sloc, (TOKENPTR)r_expr);
792                                 reg2 = 0;
793                         }
794                         else
795                         {
796                                 reg2 = ((int)(eval - ((orgactive ? orgaddr : sloc) + 2))) / 2;
797
798                                 if ((reg2 < -16) || (reg2 > 15))
799                                         error("PC relative overflow (outside of -16 to 15)");
800                         }
801
802                         BuildRISCIntructionWord(parm, reg2, reg1);
803                 }
804                 else
805                 {
806                         // JUMP cc, (Rn)
807                         if (*tok.u32 != '(')
808                                 return MalformedOpcode(0x10);
809
810                         tok.u32++;
811                         reg2 = GetRegister(FU_REGTWO);
812
813                         if (*tok.u32 != ')')
814                                 return MalformedOpcode(0x11);
815
816                         tok.u32++;
817                         at_eol();
818                         BuildRISCIntructionWord(parm, reg2, reg1);
819                 }
820
821                 break;
822
823         // Should never get here :-D
824         default:
825                 return error("Unknown RISC opcode type");
826         }
827
828         lastOpcode = type;
829         return 0;
830 }
831