]> Shamusworld >> Repos - rmac/blob - 6502.c
Fix for #159: Split register sets according to architecture into different tables...
[rmac] / 6502.c
1 //
2 // RMAC - Renamed Macro Assembler for all Atari computers
3 // 6502.C - 6502 Assembler
4 // Copyright (C) 199x Landon Dyer, 2011-2021 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 //    Init6502  initialization
9 //    d_6502    handle ".6502" directive
10 //    m6502cg   generate code for a 6502 mnemonic
11 //    d_org     handle 6502 section's ".org" directive
12 //    m6502obj  generate 6502 object file
13 //
14 #include "direct.h"
15 #include "expr.h"
16 #include "error.h"
17 #include "mach.h"
18 #include "procln.h"
19 #include "riscasm.h"
20 #include "rmac.h"
21 #include "sect.h"
22 #include "token.h"
23
24 #define DEF_REG65
25 #define DECL_REG65
26 #include "6502regs.h"
27
28 #define UPSEG_SIZE      0x10010L // size of 6502 code buffer, 64K+16bytes
29
30 // Internal vars
31 static uint16_t orgmap[1024][2];                // Mark all 6502 org changes
32
33 // Exported vars
34 const char in_6502mode[] = "directive illegal in .6502 section";
35 uint16_t * currentorg = &orgmap[0][0];  // Current org range
36 char strtoa8[128];      // ASCII to Atari 800 internal conversion table
37
38 //
39 // 6502 addressing modes;
40 // DO NOT CHANGE THESE VALUES.
41 //
42 #define A65_ABS         0
43 #define A65_ABSX        1
44 #define A65_ABSY        2
45 #define A65_IMPL        3
46 #define A65_IMMED       4
47 #define A65_INDX        5
48 #define A65_INDY        6
49 #define A65_IND         7
50 #define A65_REL         8
51 #define A65_ZP          9
52 #define A65_ZPX         10
53 #define A65_ZPY         11
54 #define A65_IMMEDH  12
55 #define A65_IMMEDL  13
56
57 #define NMACHOPS        56              // Number of machine ops
58 #define NMODES          14              // Number of addressing modes
59 #define NOP                     0xEA    // 6502 NOP instruction
60 #define ILLEGAL         0xFF    // 'Illegal instr' marker
61 #define END65           0xFF    // End-of-an-instr-list
62
63 static char imodes[] =
64 {
65         A65_IMMED, 0x69, A65_ABS, 0x6D, A65_ZP, 0x65, A65_INDX, 0x61, A65_INDY, 0x71,
66         A65_ZPX, 0x75, A65_ABSX, 0x7D, A65_ABSY, 0x79, END65,
67         A65_IMMED, 0x29, A65_ABS, 0x2D, A65_ZP, 0x25, A65_INDX, 0x21, A65_INDY, 0x31,
68         A65_ZPX, 0x35, A65_ABSX, 0x3D, A65_ABSY, 0x39, END65,
69         A65_ABS, 0x0E, A65_ZP, 0x06, A65_IMPL, 0x0A, A65_ZPX, 0x16, A65_ABSX,
70         0x1E, END65,
71         A65_REL, 0x90, END65,
72         A65_REL, 0xB0, END65,
73         A65_REL, 0xF0, END65,
74         A65_REL, 0xD0, END65,
75         A65_REL, 0x30, END65,
76         A65_REL, 0x10, END65,
77         A65_REL, 0x50, END65,
78         A65_REL, 0x70, END65,
79         A65_ABS, 0x2C, A65_ZP, 0x24, END65,
80         A65_IMPL, 0x00, END65,
81         A65_IMPL, 0x18, END65,
82         A65_IMPL, 0xD8, END65,
83         A65_IMPL, 0x58, END65,
84         A65_IMPL, 0xB8, END65,
85         A65_IMMED, 0xC9, A65_ABS, 0xCD, A65_ZP, 0xC5, A65_INDX, 0xC1, A65_INDY, 0xD1,
86         A65_ZPX, 0xD5, A65_ABSX, 0xDD, A65_ABSY, 0xD9, END65,
87         A65_IMMED, 0xE0, A65_ABS, 0xEC, A65_ZP, 0xE4, END65,
88         A65_IMMED, 0xC0, A65_ABS, 0xCC, A65_ZP, 0xC4, END65,
89         A65_ABS, 0xCE, A65_ZP, 0xC6, A65_ZPX, 0xD6, A65_ABSX, 0xDE, END65,
90         A65_IMPL, 0xCA, END65,
91         A65_IMPL, 0x88, END65,
92         A65_IMMED, 0x49, A65_ABS, 0x4D, A65_ZP, 0x45, A65_INDX, 0x41, A65_INDY, 0x51,
93         A65_ZPX, 0x55, A65_ABSX, 0x5D, A65_ABSY, 0x59, END65,
94         A65_ABS, 0xEE, A65_ZP, 0xE6, A65_ZPX, 0xF6, A65_ABSX, 0xFE, END65,
95         A65_IMPL, 0xE8, END65,
96         A65_IMPL, 0xC8, END65,
97         A65_ABS, 0x4C, A65_IND, 0x6C, END65,
98         A65_ABS, 0x20, END65,
99         A65_IMMED, 0xA9, A65_ABS, 0xAD, A65_ZP, 0xA5, A65_INDX, 0xA1, A65_INDY, 0xB1,
100         A65_ZPX, 0xB5, A65_ABSX, 0xBD, A65_ABSY, 0xB9, A65_IMMEDH, 0xA9, A65_IMMEDL, 0xA9, END65,
101         A65_IMMED, 0xA2, A65_ABS, 0xAE, A65_ZP, 0xA6, A65_ABSY, 0xBE,
102         A65_ZPY, 0xB6, A65_IMMEDH, 0xA2, A65_IMMEDL, 0xA2, END65,
103         A65_IMMED, 0xA0, A65_ABS, 0xAC, A65_ZP, 0xA4, A65_ZPX, 0xB4,
104         A65_ABSX, 0xBC, A65_IMMEDH, 0xA0, A65_IMMEDL, 0xA0, END65,
105         A65_ABS, 0x4E, A65_ZP, 0x46, A65_IMPL, 0x4A, A65_ZPX, 0x56,
106         A65_ABSX, 0x5E, END65,
107         A65_IMPL, 0xEA, END65,
108         A65_IMMED, 0x09, A65_ABS, 0x0D, A65_ZP, 0x05, A65_INDX, 0x01, A65_INDY, 0x11,
109         A65_ZPX, 0x15, A65_ABSX, 0x1D, A65_ABSY, 0x19, END65,
110         A65_IMPL, 0x48, END65,
111         A65_IMPL, 0x08, END65,
112         A65_IMPL, 0x68, END65,
113         A65_IMPL, 0x28, END65,
114         A65_ABS, 0x2E, A65_ZP, 0x26, A65_IMPL, 0x2A, A65_ZPX, 0x36,
115         A65_ABSX, 0x3E, END65,
116         A65_ABS, 0x6E, A65_ZP, 0x66, A65_IMPL, 0x6A, A65_ZPX, 0x76,
117         A65_ABSX, 0x7E, END65,
118         A65_IMPL, 0x40, END65,
119         A65_IMPL, 0x60, END65,
120         A65_IMMED, 0xE9, A65_ABS, 0xED, A65_ZP, 0xE5, A65_INDX, 0xE1, A65_INDY, 0xF1,
121         A65_ZPX, 0xF5, A65_ABSX, 0xFD, A65_ABSY, 0xF9, END65,
122         A65_IMPL, 0x38, END65,
123         A65_IMPL, 0xF8, END65,
124         A65_IMPL, 0x78, END65,
125         A65_ABS, 0x8D, A65_ZP, 0x85, A65_INDX, 0x81, A65_INDY, 0x91, A65_ZPX, 0x95,
126         A65_ABSX, 0x9D, A65_ABSY, 0x99, END65,
127         A65_ABS, 0x8E, A65_ZP, 0x86, A65_ZPY, 0x96, END65,
128         A65_ABS, 0x8C, A65_ZP, 0x84, A65_ZPX, 0x94, END65,
129         A65_IMPL, 0xAA, END65,
130         A65_IMPL, 0xA8, END65,
131         A65_IMPL, 0xBA, END65,
132         A65_IMPL, 0x8A, END65,
133         A65_IMPL, 0x9A, END65,
134         A65_IMPL, 0x98, END65
135 };
136
137 static char ops[NMACHOPS][NMODES];                      // Opcodes
138 static unsigned char inf[NMACHOPS][NMODES];     // Construction info
139
140 // Absolute-to-zeropage translation table
141 static int abs2zp[] =
142 {
143         A65_ZP,         // ABS
144         A65_ZPX,        // ABSX
145         A65_ZPY,        // ABSY
146         -1,                     // IMPL
147         -1,                     // IMMED
148         -1,                     // INDX
149         -1,                     // INDY
150         -1,                     // IND
151         -1,                     // REL
152         -1,                     // ZP
153         -1,                     // ZPX
154         -1                      // ZPY
155 };
156
157 static char a8internal[] =
158 {
159     ' ', 0,   '!', 1,   '"', 2,   '#', 3,   '$',  4,   '%', 5,   '&', 6,   '\'', 7,
160     '(', 8,   ')', 9,   '*', 10,  '+', 11,  ',',  12,  '-', 13,  '.', 14,  '/',  15,
161     '0', 16,  '1', 17,  '2', 18,  '3', 19,  '4',  20,  '5', 21,  '6', 22,  '7',  23,
162     '8', 24,  '9', 25,  ':', 26,  ';', 27,  '<',  28,  '=', 29,  '>', 30,  '?',  31,
163     '@', 32,  'A', 33,  'B', 34,  'C', 35,  'D',  36,  'E', 37,  'F', 38,  'G',  39,
164     'H', 40,  'I', 41,  'J', 42,  'K', 43,  'L',  44,  'M', 45,  'N', 46,  'O',  47,
165     'P', 48,  'Q', 49,  'R', 50,  'S', 51,  'T',  52,  'U', 53,  'V', 54,  'W',  55,
166     'X', 56,  'Y', 57,  'Z', 58,  '[', 59,  '\\', 60,  ']', 61,  '^', 62,  '_',  63,
167     'a', 97,  'b', 98,  'c', 99,  'd', 100, 'e',  101, 'f', 102, 'g', 103, 'h',  104,
168     'i', 105, 'j', 106, 'k', 107, 'l', 108, 'm',  109, 'n', 110, 'o', 111, 'p',  112,
169     'q', 113, 'r', 114, 's', 115, 't', 116, 'u',  117, 'v', 118, 'w', 119, 'x',  120,
170     'y', 121, 'z', 122
171 };
172
173
174 //
175 // Initialize 6502 assembler
176 //
177 void Init6502()
178 {
179         register int i;
180         register int j;
181
182         register char * s = imodes;
183
184         // Set all instruction slots to illegal
185         for(i=0; i<NMACHOPS; i++)
186                 for(j=0; j<NMODES; j++)
187                         inf[i][j] = ILLEGAL;
188
189         // Uncompress legal instructions into their slots
190         for(i=0; i<NMACHOPS; i++)
191         {
192                 do
193                 {
194                         j = *s & 0xFF;
195                         inf[i][j] = *s;
196                         ops[i][j] = s[1];
197
198                         /* hack A65_REL mode */
199                         if (*s == A65_REL)
200                         {
201                                 inf[i][A65_ABS] = A65_REL;
202                                 ops[i][A65_ABS] = s[1];
203                                 inf[i][A65_ZP] = A65_REL;
204                                 ops[i][A65_ZP] = s[1];
205                         }
206                 }
207                 while (*(s += 2) != (char)END65);
208
209                 s++;
210         }
211
212         // Set up first org section (set to zero)
213         orgmap[0][0] = 0;
214
215         SwitchSection(M6502);   // Switch to 6502 section
216
217         // Initialise string conversion table(s)
218         char * p = a8internal;
219         memset(strtoa8, 31, 128);   // 31=fallback value ("?")
220
221         for(; p<a8internal+sizeof(a8internal); p+=2)
222                 strtoa8[p[0]] = p[1];
223
224         if (challoc == 0)
225         {
226                 // Allocate and clear 64K of space for the 6502 section
227                 chcheck(UPSEG_SIZE);
228                 memset(sect[M6502].scode->chptr, 0, UPSEG_SIZE);
229         }
230
231         SwitchSection(TEXT);    // Go back to TEXT
232 }
233
234
235 //
236 // .6502 --- enter 6502 mode
237 //
238 int d_6502()
239 {
240         SaveSection();                  // Save curent section
241         SwitchSection(M6502);   // Switch to 6502 section
242         regbase = reg65base;    // Update register DFA tables
243         regtab = reg65tab;
244         regcheck = reg65check;
245         regaccept = reg65accept;
246
247         return 0;
248 }
249
250
251 //
252 // Do 6502 code generation
253 //
254 void m6502cg(int op)
255 {
256         register int amode;             // (Parsed) addressing mode
257         uint64_t eval = -1;             // Expression value
258         WORD eattr = 0;                 // Expression attributes
259         int zpreq = 0;                  // 1 = optimize instr to zero-page form
260         ch_size = 0;                    // Reset chunk size on every instruction
261
262         //
263         // Parse 6502 addressing mode
264         //
265         switch (tok[0])
266         {
267         case EOL:
268                 amode = A65_IMPL;
269                 break;
270
271         case REG65_A:
272                 if (tok[1] != EOL)
273                         goto badmode;
274
275                 tok++;
276                 amode = A65_IMPL;
277                 break;
278
279         case '#':
280                 tok++;
281
282                 if (*tok == '>')
283                 {
284                         tok++;
285                         amode = A65_IMMEDH;
286                 }
287                 else if (*tok == '<')
288                 {
289                         tok++;
290                         amode = A65_IMMEDL;
291                 }
292                 else
293                         amode = A65_IMMED;
294
295                 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
296                         return;
297
298                 break;
299
300         case '(':
301                 tok++;
302
303                 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
304                         return;
305
306                 if (*tok == ')')
307                 {
308                         // (foo) or (foo),y
309                         if (*++tok == ',')
310                         {
311                                 // (foo),y
312                                 tok++;
313                                 amode = A65_INDY;
314
315                                 if (tok[0] != REG65_Y)
316                                         goto badmode;
317
318                                 tok++;
319                         }
320                         else
321                                 amode = A65_IND;
322                 }
323                 else if ((tok[0] == ',') && (tok[1] == REG65_X) && (tok[2] == ')'))
324                 {
325                         // (foo,x)
326                         tok += 3;
327                         amode = A65_INDX;
328                 }
329                 else
330                         goto badmode;
331
332                 break;
333
334         // I'm guessing that the form of this is @<expr>(X) or @<expr>(Y), which
335         // I've *never* seen before.  :-/
336         case '@':
337                 tok++;
338
339                 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
340                         return;
341
342                 if (*tok == '(')
343                 {
344                         tok++;
345
346                         if ((tok[1] != ')') || (tok[2] != EOL))
347                                 goto badmode;
348
349                         if (tok[0] == REG65_X)
350                                 amode = A65_INDX;
351                         else if (tok[0] == REG65_Y)
352                                 amode = A65_INDY;
353                         else
354                                 goto badmode;
355
356                         tok += 2;
357                         zpreq = 1;              // Request zeropage optimization
358                 }
359                 else if (*tok == EOL)
360                         amode = A65_IND;
361                 else
362                         goto badmode;
363
364                 break;
365
366         default:
367                 //   <expr>
368                 //   <expr>,x
369                 //   <expr>,y
370                 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
371                         return;
372
373                 zpreq = 1;              // Request zeropage optimization
374
375                 if (tok[0] == EOL)
376                         amode = A65_ABS;
377                 else if (tok[0] == ',')
378                 {
379                         tok++;
380
381                         if (tok[0] == REG65_X)
382                         {
383                                 tok++;
384                                 amode = A65_ABSX;
385                         }
386                         else if (tok[0] == REG65_Y)
387                         {
388                                 tok++;
389                                 amode = A65_ABSY;
390                         }
391                 }
392                 else
393                         goto badmode;
394
395                 break;
396
397 badmode:
398                 error("bad 6502 addressing mode");
399                 return;
400         }
401
402         //
403         // Optimize ABS modes to zero-page when possible
404         //   o  ZPX or ZPY is illegal, or
405         //   o  expr is zeropage && zeropageRequest && expression is defined
406         //
407         if ((inf[op][amode] == ILLEGAL) // If current op is illegal OR
408                 || (zpreq                                       // amode requested a zero-page optimize
409                         && (eval < 0x100)               // and expr is zero-page
410                         && (eattr & DEFINED)))  // and the expression is defined
411         {
412                 int i = abs2zp[amode];          // Get zero-page translation of amode
413 #ifdef DO_DEBUG
414                 DEBUG printf(" OPT: op=%d amode=%d i=%d inf[op][i]=%d\n",
415                                          op, amode, i, inf[op][i]);
416 #endif
417                 if (i >= 0 && (inf[op][i] & 0xFF) != ILLEGAL) // & use it if it's legal
418                         amode = i;
419         }
420
421 #ifdef DO_DEBUG
422         DEBUG printf("6502: op=%d amode=%d ", op, amode);
423         DEBUG printf("inf[op][amode]=%d\n", (int)inf[op][amode]);
424 #endif
425
426         switch (inf[op][amode])
427         {
428                 case A65_IMPL:          // Just leave the instruction
429                         D_byte(ops[op][amode]);
430                         break;
431
432                 case A65_IMMEDH:
433                         D_byte(ops[op][amode]);
434
435                         if (!(eattr & DEFINED))
436                         {
437                                 AddFixup(FU_BYTEH, sloc, exprbuf);
438                                 eval = 0;
439                         }
440
441                         eval = (eval >> 8) & 0xFF; // Bring high byte to low
442                         D_byte(eval);                           // Deposit byte following instr
443                         break;
444
445                 case A65_IMMEDL:
446                         D_byte(ops[op][amode]);
447
448                         if (!(eattr & DEFINED))
449                         {
450                                 AddFixup(FU_BYTEL, sloc, exprbuf);
451                                 eval = 0;
452                         }
453
454                         eval = eval & 0xFF; // Mask high byte
455                         D_byte(eval);           // Deposit byte following instr
456                         break;
457
458                 case A65_IMMED:
459                 case A65_INDX:
460                 case A65_INDY:
461                 case A65_ZP:
462                 case A65_ZPX:
463                 case A65_ZPY:
464                         D_byte(ops[op][amode]);
465
466                         if (!(eattr & DEFINED))
467                         {
468                                 AddFixup(FU_BYTE, sloc, exprbuf);
469                                 eval = 0;
470                         }
471                         else if (eval + 0x100 >= 0x200)
472                         {
473                                 error(range_error);
474                                 eval = 0;
475                         }
476
477                         D_byte(eval);           // Deposit byte following instr
478                         break;
479
480                 case A65_REL:
481                         D_byte(ops[op][amode]);
482
483                         if (eattr & DEFINED)
484                         {
485                                 eval -= (sloc + 1);
486
487                                 if (eval + 0x80 >= 0x100)
488                                 {
489                                         error(range_error);
490                                         eval = 0;
491                                 }
492
493                                 D_byte(eval);
494                         }
495                         else
496                         {
497                                 AddFixup(FU_6BRA, sloc, exprbuf);
498                                 D_byte(0);
499                         }
500
501                         break;
502
503                 case A65_ABS:
504                 case A65_ABSX:
505                 case A65_ABSY:
506                 case A65_IND:
507                         D_byte(ops[op][amode]);
508
509                         if (!(eattr & DEFINED))
510                         {
511                                 AddFixup(FU_WORD, sloc, exprbuf);
512                                 eval = 0;
513                         }
514
515                         D_rword(eval);
516                         break;
517
518                 //
519                 // Deposit 3 NOPs for illegal things (why 3? why not 30? or zero?)
520                 //
521                 default:
522                 case ILLEGAL:
523                         D_byte(NOP);
524                         D_byte(NOP);
525                         D_byte(NOP);
526
527                         error("illegal 6502 addressing mode");
528         }
529
530         // Check for overflow of code region
531         if (sloc > 0x10000L)
532                 fatal("6502 code pointer > 64K");
533
534         ErrorIfNotAtEOL();
535 }
536
537
538 //
539 // Generate 6502 object output file.
540 // ggn: Converted to COM/EXE/XEX output format
541 //
542 void m6502obj(int ofd)
543 {
544         uint8_t header[4];
545
546         CHUNK * ch = sect[M6502].scode;
547
548         // If no 6502 code was generated, bail out
549         if ((ch == NULL) || (ch->challoc == 0))
550                 return;
551
552         register uint8_t * p = ch->chptr;
553
554         // Write out mandatory $FFFF header
555         header[0] = header[1] = 0xFF;
556         uint32_t unused = write(ofd, header, 2);
557
558         for(uint16_t * l=&orgmap[0][0]; l<currentorg; l+=2)
559         {
560                 SETLE16(header, 0, l[0]);
561                 SETLE16(header, 2, l[1] - 1);
562
563                 // Write header for segment
564                 unused = write(ofd, header, 4);
565                 // Write the segment data
566                 unused = write(ofd, p + l[0], l[1] - l[0]);
567         }
568 }
569