]> Shamusworld >> Repos - rmac/blob - 6502.c
Version bump for last commit. :-)
[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         used_architectures |= M6502;
247
248         return 0;
249 }
250
251
252 //
253 // Do 6502 code generation
254 //
255 void m6502cg(int op)
256 {
257         register int amode;             // (Parsed) addressing mode
258         uint64_t eval = -1;             // Expression value
259         WORD eattr = 0;                 // Expression attributes
260         int zpreq = 0;                  // 1 = optimize instr to zero-page form
261         ch_size = 0;                    // Reset chunk size on every instruction
262
263         //
264         // Parse 6502 addressing mode
265         //
266         switch (tok[0])
267         {
268         case EOL:
269                 amode = A65_IMPL;
270                 break;
271
272         case REG65_A:
273                 if (tok[1] != EOL)
274                         goto badmode;
275
276                 tok++;
277                 amode = A65_IMPL;
278                 break;
279
280         case '#':
281                 tok++;
282
283                 if (*tok == '>')
284                 {
285                         tok++;
286                         amode = A65_IMMEDH;
287                 }
288                 else if (*tok == '<')
289                 {
290                         tok++;
291                         amode = A65_IMMEDL;
292                 }
293                 else
294                         amode = A65_IMMED;
295
296                 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
297                         return;
298
299                 break;
300
301         case '(':
302                 tok++;
303
304                 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
305                         return;
306
307                 if (*tok == ')')
308                 {
309                         // (foo) or (foo),y
310                         if (*++tok == ',')
311                         {
312                                 // (foo),y
313                                 tok++;
314                                 amode = A65_INDY;
315
316                                 if (tok[0] != REG65_Y)
317                                         goto badmode;
318
319                                 tok++;
320                         }
321                         else
322                                 amode = A65_IND;
323                 }
324                 else if ((tok[0] == ',') && (tok[1] == REG65_X) && (tok[2] == ')'))
325                 {
326                         // (foo,x)
327                         tok += 3;
328                         amode = A65_INDX;
329                 }
330                 else
331                         goto badmode;
332
333                 break;
334
335         // I'm guessing that the form of this is @<expr>(X) or @<expr>(Y), which
336         // I've *never* seen before.  :-/
337         case '@':
338                 tok++;
339
340                 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
341                         return;
342
343                 if (*tok == '(')
344                 {
345                         tok++;
346
347                         if ((tok[1] != ')') || (tok[2] != EOL))
348                                 goto badmode;
349
350                         if (tok[0] == REG65_X)
351                                 amode = A65_INDX;
352                         else if (tok[0] == REG65_Y)
353                                 amode = A65_INDY;
354                         else
355                                 goto badmode;
356
357                         tok += 2;
358                         zpreq = 1;              // Request zeropage optimization
359                 }
360                 else if (*tok == EOL)
361                         amode = A65_IND;
362                 else
363                         goto badmode;
364
365                 break;
366
367         default:
368                 //   <expr>
369                 //   <expr>,x
370                 //   <expr>,y
371                 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
372                         return;
373
374                 zpreq = 1;              // Request zeropage optimization
375
376                 if (tok[0] == EOL)
377                         amode = A65_ABS;
378                 else if (tok[0] == ',')
379                 {
380                         tok++;
381
382                         if (tok[0] == REG65_X)
383                         {
384                                 tok++;
385                                 amode = A65_ABSX;
386                         }
387                         else if (tok[0] == REG65_Y)
388                         {
389                                 tok++;
390                                 amode = A65_ABSY;
391                         }
392                 }
393                 else
394                         goto badmode;
395
396                 break;
397
398 badmode:
399                 error("bad 6502 addressing mode");
400                 return;
401         }
402
403         //
404         // Optimize ABS modes to zero-page when possible
405         //   o  ZPX or ZPY is illegal, or
406         //   o  expr is zeropage && zeropageRequest && expression is defined
407         //
408         if ((inf[op][amode] == ILLEGAL) // If current op is illegal OR
409                 || (zpreq                                       // amode requested a zero-page optimize
410                         && (eval < 0x100)               // and expr is zero-page
411                         && (eattr & DEFINED)))  // and the expression is defined
412         {
413                 int i = abs2zp[amode];          // Get zero-page translation of amode
414 #ifdef DO_DEBUG
415                 DEBUG printf(" OPT: op=%d amode=%d i=%d inf[op][i]=%d\n",
416                                          op, amode, i, inf[op][i]);
417 #endif
418                 if (i >= 0 && (inf[op][i] & 0xFF) != ILLEGAL) // & use it if it's legal
419                         amode = i;
420         }
421
422 #ifdef DO_DEBUG
423         DEBUG printf("6502: op=%d amode=%d ", op, amode);
424         DEBUG printf("inf[op][amode]=%d\n", (int)inf[op][amode]);
425 #endif
426
427         GENLINENOSYM();
428
429         switch (inf[op][amode])
430         {
431                 case A65_IMPL:          // Just leave the instruction
432                         D_byte(ops[op][amode]);
433                         break;
434
435                 case A65_IMMEDH:
436                         D_byte(ops[op][amode]);
437
438                         if (!(eattr & DEFINED))
439                         {
440                                 AddFixup(FU_BYTEH, sloc, exprbuf);
441                                 eval = 0;
442                         }
443
444                         eval = (eval >> 8) & 0xFF; // Bring high byte to low
445                         D_byte(eval);                           // Deposit byte following instr
446                         break;
447
448                 case A65_IMMEDL:
449                         D_byte(ops[op][amode]);
450
451                         if (!(eattr & DEFINED))
452                         {
453                                 AddFixup(FU_BYTEL, sloc, exprbuf);
454                                 eval = 0;
455                         }
456
457                         eval = eval & 0xFF; // Mask high byte
458                         D_byte(eval);           // Deposit byte following instr
459                         break;
460
461                 case A65_IMMED:
462                 case A65_INDX:
463                 case A65_INDY:
464                 case A65_ZP:
465                 case A65_ZPX:
466                 case A65_ZPY:
467                         D_byte(ops[op][amode]);
468
469                         if (!(eattr & DEFINED))
470                         {
471                                 AddFixup(FU_BYTE, sloc, exprbuf);
472                                 eval = 0;
473                         }
474                         else if (eval + 0x100 >= 0x200)
475                         {
476                                 error(range_error);
477                                 eval = 0;
478                         }
479
480                         D_byte(eval);           // Deposit byte following instr
481                         break;
482
483                 case A65_REL:
484                         D_byte(ops[op][amode]);
485
486                         if (eattr & DEFINED)
487                         {
488                                 eval -= (sloc + 1);
489
490                                 if (eval + 0x80 >= 0x100)
491                                 {
492                                         error(range_error);
493                                         eval = 0;
494                                 }
495
496                                 D_byte(eval);
497                         }
498                         else
499                         {
500                                 AddFixup(FU_6BRA, sloc, exprbuf);
501                                 D_byte(0);
502                         }
503
504                         break;
505
506                 case A65_ABS:
507                 case A65_ABSX:
508                 case A65_ABSY:
509                 case A65_IND:
510                         D_byte(ops[op][amode]);
511
512                         if (!(eattr & DEFINED))
513                         {
514                                 AddFixup(FU_WORD, sloc, exprbuf);
515                                 eval = 0;
516                         }
517
518                         D_rword(eval);
519                         break;
520
521                 //
522                 // Deposit 3 NOPs for illegal things (why 3? why not 30? or zero?)
523                 //
524                 default:
525                 case ILLEGAL:
526                         D_byte(NOP);
527                         D_byte(NOP);
528                         D_byte(NOP);
529
530                         error("illegal 6502 addressing mode");
531         }
532
533         // Check for overflow of code region
534         if (sloc > 0x10000L)
535                 fatal("6502 code pointer > 64K");
536
537         ErrorIfNotAtEOL();
538 }
539
540
541 //
542 // Generate 6502 object output file.
543 // ggn: Converted to COM/EXE/XEX output format
544 //
545 void m6502obj(int ofd)
546 {
547         uint8_t header[4];
548
549         CHUNK * ch = sect[M6502].scode;
550
551         // If no 6502 code was generated, bail out
552         if ((ch == NULL) || (ch->challoc == 0))
553                 return;
554
555         register uint8_t * p = ch->chptr;
556
557         // Write out mandatory $FFFF header
558         header[0] = header[1] = 0xFF;
559         uint32_t unused = write(ofd, header, 2);
560
561         for(uint16_t * l=&orgmap[0][0]; l<currentorg; l+=2)
562         {
563                 SETLE16(header, 0, l[0]);
564                 SETLE16(header, 2, l[1] - 1);
565
566                 // Write header for segment
567                 unused = write(ofd, header, 4);
568                 // Write the segment data
569                 unused = write(ofd, p + l[0], l[1] - l[0]);
570         }
571 }
572
573
574 // Write raw 6502 org'd code.
575 // Super copypasta'd from above function
576 void m6502raw(int ofd)
577 {
578         CHUNK * ch = sect[M6502].scode;
579
580         // If no 6502 code was generated, bail out
581         if ((ch == NULL) || (ch->challoc == 0))
582                 return;
583
584         register uint8_t *p = ch->chptr;
585
586         for(uint16_t * l=&orgmap[0][0]; l<currentorg; l+=2)
587         {
588                 // Write the segment data
589                 uint32_t unused = write(ofd, p + l[0], l[1] - l[0]);
590         }
591 }
592
593
594 //
595 // Generate a C64 .PRG output file
596 //
597 void m6502c64(int ofd)
598 {
599         uint8_t header[2];
600
601         CHUNK * ch = sect[M6502].scode;
602
603         // If no 6502 code was generated, bail out
604         if ((ch == NULL) || (ch->challoc == 0))
605                 return;
606
607         if (currentorg != &orgmap[1][0])
608         {
609                 // More than one 6502 section created, this is not allowed
610                 error("when generating C64 .PRG files only one org section is allowed - aborting");
611                 return;
612         }
613
614         SETLE16(header, 0, orgmap[0][0]);
615         register uint8_t * p = ch->chptr;
616
617         // Write header
618         uint32_t unused = write(ofd, header, 2);
619         // Write the data
620         unused = write(ofd, p + orgmap[0][0], orgmap[0][1] - orgmap[0][0]);
621 }