]> Shamusworld >> Repos - rmac/blob - 6502.c
Don't print bogus message about optimising long address if it's suffixed with .l...
[rmac] / 6502.c
1 //
2 // RMAC - Reboot's Macro Assembler for all Atari computers
3 // 6502.C - 6502 Assembler
4 // Copyright (C) 199x Landon Dyer, 2011-2018 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 UPSEG_SIZE      0x10010L // size of 6502 code buffer, 64K+16bytes
25
26 // Internal vars
27 static uint16_t orgmap[1024][2];                // Mark all 6502 org changes
28
29 // Exported vars
30 const char in_6502mode[] = "directive illegal in .6502 section";
31 uint16_t * currentorg = &orgmap[0][0];  // Current org range
32 char strtoa8[128];      // ASCII to Atari 800 internal conversion table
33
34 //
35 // 6502 addressing modes;
36 // DO NOT CHANGE THESE VALUES.
37 //
38 #define A65_ABS         0
39 #define A65_ABSX        1
40 #define A65_ABSY        2
41 #define A65_IMPL        3
42 #define A65_IMMED       4
43 #define A65_INDX        5
44 #define A65_INDY        6
45 #define A65_IND         7
46 #define A65_REL         8
47 #define A65_ZP          9
48 #define A65_ZPX         10
49 #define A65_ZPY         11
50 #define A65_IMMEDH  12
51 #define A65_IMMEDL  13
52
53 #define NMACHOPS 56             // Number of machine ops
54 #define NMODES  14              // Number of addressing modes
55 #define NOP     0xEA            // 6502 NOP instruction
56 #define ILLEGAL 0xFF    // 'Illegal instr' marker
57 #define END65   0xFF    // End-of-an-instr-list
58
59 static char imodes[] =
60 {
61         A65_IMMED, 0x69, A65_ABS, 0x6d, A65_ZP, 0x65, A65_INDX, 0x61, A65_INDY, 0x71,
62         A65_ZPX, 0x75, A65_ABSX, 0x7d, A65_ABSY, 0x79, END65,
63         A65_IMMED, 0x29, A65_ABS, 0x2d, A65_ZP, 0x25, A65_INDX, 0x21, A65_INDY, 0x31,
64         A65_ZPX, 0x35, A65_ABSX, 0x3d, A65_ABSY, 0x39, END65,
65         A65_ABS, 0x0e, A65_ZP, 0x06, A65_IMPL, 0x0a, A65_ZPX, 0x16, A65_ABSX,
66         0x1e, END65,
67         A65_REL, 0x90, END65,
68         A65_REL, 0xb0, END65,
69         A65_REL, 0xf0, END65,
70         A65_REL, 0xd0, END65,
71         A65_REL, 0x30, END65,
72         A65_REL, 0x10, END65,
73         A65_REL, 0x50, END65,
74         A65_REL, 0x70, END65,
75         A65_ABS, 0x2c, A65_ZP, 0x24, END65,
76         A65_IMPL, 0x00, END65,
77         A65_IMPL, 0x18, END65,
78         A65_IMPL, 0xd8, END65,
79         A65_IMPL, 0x58, END65,
80         A65_IMPL, 0xb8, END65,
81         A65_IMMED, 0xc9, A65_ABS, 0xcd, A65_ZP, 0xc5, A65_INDX, 0xc1, A65_INDY, 0xd1,
82         A65_ZPX, 0xd5, A65_ABSX, 0xdd, A65_ABSY, 0xd9, END65,
83         A65_IMMED, 0xe0, A65_ABS, 0xec, A65_ZP, 0xe4, END65,
84         A65_IMMED, 0xc0, A65_ABS, 0xcc, A65_ZP, 0xc4, END65,
85         A65_ABS, 0xce, A65_ZP, 0xc6, A65_ZPX, 0xd6, A65_ABSX, 0xde, END65,
86         A65_IMPL, 0xca, END65,
87         A65_IMPL, 0x88, END65,
88         A65_IMMED, 0x49, A65_ABS, 0x4d, A65_ZP, 0x45, A65_INDX, 0x41, A65_INDY, 0x51,
89         A65_ZPX, 0x55, A65_ABSX, 0x5d, A65_ABSY, 0x59, END65,
90         A65_ABS, 0xee, A65_ZP, 0xe6, A65_ZPX, 0xf6, A65_ABSX, 0xfe, END65,
91         A65_IMPL, 0xe8, END65,
92         A65_IMPL, 0xc8, END65,
93         A65_ABS, 0x4c, A65_IND, 0x6c, END65,
94         A65_ABS, 0x20, END65,
95         A65_IMMED, 0xa9, A65_ABS, 0xad, A65_ZP, 0xa5, A65_INDX, 0xa1, A65_INDY, 0xb1,
96         A65_ZPX, 0xb5, A65_ABSX, 0xbd, A65_ABSY, 0xb9, A65_IMMEDH, 0xa9, A65_IMMEDL, 0xa9, END65,
97         A65_IMMED, 0xa2, A65_ABS, 0xae, A65_ZP, 0xa6, A65_ABSY, 0xbe,
98         A65_ZPY, 0xb6, A65_IMMEDH, 0xa2, A65_IMMEDL, 0xa2, END65,
99         A65_IMMED, 0xa0, A65_ABS, 0xac, A65_ZP, 0xa4, A65_ZPX, 0xb4,
100         A65_ABSX, 0xbc, A65_IMMEDH, 0xa0, A65_IMMEDL, 0xa0, END65,
101         A65_ABS, 0x4e, A65_ZP, 0x46, A65_IMPL, 0x4a, A65_ZPX, 0x56,
102         A65_ABSX, 0x5e, END65,
103         A65_IMPL, 0xea, END65,
104         A65_IMMED, 0x09, A65_ABS, 0x0d, A65_ZP, 0x05, A65_INDX, 0x01, A65_INDY, 0x11,
105         A65_ZPX, 0x15, A65_ABSX, 0x1d, A65_ABSY, 0x19, END65,
106         A65_IMPL, 0x48, END65,
107         A65_IMPL, 0x08, END65,
108         A65_IMPL, 0x68, END65,
109         A65_IMPL, 0x28, END65,
110         A65_ABS, 0x2e, A65_ZP, 0x26, A65_IMPL, 0x2a, A65_ZPX, 0x36,
111         A65_ABSX, 0x3e, END65,
112         A65_ABS, 0x6e, A65_ZP, 0x66, A65_IMPL, 0x6a, A65_ZPX, 0x76,
113         A65_ABSX, 0x7e, END65,
114         A65_IMPL, 0x40, END65,
115         A65_IMPL, 0x60, END65,
116         A65_IMMED, 0xe9, A65_ABS, 0xed, A65_ZP, 0xe5, A65_INDX, 0xe1, A65_INDY, 0xf1,
117         A65_ZPX, 0xf5, A65_ABSX, 0xfd, A65_ABSY, 0xf9, END65,
118         A65_IMPL, 0x38, END65,
119         A65_IMPL, 0xf8, END65,
120         A65_IMPL, 0x78, END65,
121         A65_ABS, 0x8d, A65_ZP, 0x85, A65_INDX, 0x81, A65_INDY, 0x91, A65_ZPX, 0x95,
122         A65_ABSX, 0x9d, A65_ABSY, 0x99, END65,
123         A65_ABS, 0x8e, A65_ZP, 0x86, A65_ZPY, 0x96, END65,
124         A65_ABS, 0x8c, A65_ZP, 0x84, A65_ZPX, 0x94, END65,
125         A65_IMPL, 0xaa, END65,
126         A65_IMPL, 0xa8, END65,
127         A65_IMPL, 0xba, END65,
128         A65_IMPL, 0x8a, END65,
129         A65_IMPL, 0x9a, END65,
130         A65_IMPL, 0x98, END65
131 };
132
133 static char ops[NMACHOPS][NMODES];                      // Opcodes
134 static unsigned char inf[NMACHOPS][NMODES];     // Construction info
135
136 // Absolute-to-zeropage translation table
137 static int abs2zp[] =
138 {
139         A65_ZP,         // ABS
140         A65_ZPX,        // ABSX
141         A65_ZPY,        // ABSY
142         -1,                     // IMPL
143         -1,                     // IMMED
144         -1,                     // INDX
145         -1,                     // INDY
146         -1,                     // IND
147         -1,                     // REL
148         -1,                     // ZP
149         -1,                     // ZPX
150         -1                      // ZPY
151 };
152
153 static char a8internal[] =
154 {
155     ' ', 0,   '!', 1,   '"', 2,   '#', 3,   '$',  4,   '%', 5,   '&', 6,   '\'', 7,
156     '(', 8,   ')', 9,   '*', 10,  '+', 11,  ',',  12,  '-', 13,  '.', 14,  '/',  15,
157     '0', 16,  '1', 17,  '2', 18,  '3', 19,  '4',  20,  '5', 21,  '6', 22,  '7',  23,
158     '8', 24,  '9', 25,  ':', 26,  ';', 27,  '<',  28,  '=', 29,  '>', 30,  '?',  31,
159     '@', 32,  'A', 33,  'B', 34,  'C', 35,  'D',  36,  'E', 37,  'F', 38,  'G',  39,
160     'H', 40,  'I', 41,  'J', 42,  'K', 43,  'L',  44,  'M', 45,  'N', 46,  'O',  47,
161     'P', 48,  'Q', 49,  'R', 50,  'S', 51,  'T',  52,  'U', 53,  'V', 54,  'W',  55,
162     'X', 56,  'Y', 57,  'Z', 58,  '[', 59,  '\\', 60,  ']', 61,  '^', 62,  '_',  63,
163     'a', 97,  'b', 98,  'c', 99,  'd', 100, 'e',  101, 'f', 102, 'g', 103, 'h',  104,
164     'i', 105, 'j', 106, 'k', 107, 'l', 108, 'm',  109, 'n', 110, 'o', 111, 'p',  112,
165     'q', 113, 'r', 114, 's', 115, 't', 116, 'u',  117, 'v', 118, 'w', 119, 'x',  120,
166     'y', 121, 'z', 122
167 };
168
169
170 //
171 // Initialize 6502 assembler
172 //
173 void Init6502()
174 {
175         register int i;
176         register int j;
177
178         register char * s = imodes;
179
180         // Set all instruction slots to illegal
181         for(i=0; i<NMACHOPS; i++)
182                 for(j=0; j<NMODES; j++)
183                         inf[i][j] = ILLEGAL;
184
185         // Uncompress legal instructions into their slots
186         for(i=0; i<NMACHOPS; i++)
187         {
188                 do
189                 {
190                         j = *s & 0xFF;
191                         inf[i][j] = *s;
192                         ops[i][j] = s[1];
193
194                         /* hack A65_REL mode */
195                         if (*s == A65_REL)
196                         {
197                                 inf[i][A65_ABS] = A65_REL;
198                                 ops[i][A65_ABS] = s[1];
199                                 inf[i][A65_ZP] = A65_REL;
200                                 ops[i][A65_ZP] = s[1];
201                         }
202                 }
203                 while (*(s += 2) != (char)END65);
204
205                 s++;
206         }
207
208         // Set up first org section (set to zero)
209         orgmap[0][0] = 0;
210
211         SwitchSection(M6502);   // Switch to 6502 section
212
213         // Initialise string conversion table(s)
214         char * p = a8internal;
215         memset(strtoa8, 31, 128);   // 31=fallback value ("?")
216
217         for(; p<a8internal+sizeof(a8internal); p+=2)
218                 strtoa8[p[0]] = p[1];
219
220         if (challoc == 0)
221         {
222                 // Allocate and clear 64K of space for the 6502 section
223                 chcheck(UPSEG_SIZE);
224                 memset(sect[M6502].scode->chptr, 0, UPSEG_SIZE);
225         }
226
227         SwitchSection(TEXT);    // Go back to TEXT
228 }
229
230
231 //
232 // .6502 --- enter 6502 mode
233 //
234 int d_6502()
235 {
236         SaveSection();                  // Save curent section
237         SwitchSection(M6502);   // Switch to 6502 section
238
239         return 0;
240 }
241
242
243 //
244 // Do 6502 code generation
245 //
246 void m6502cg(int op)
247 {
248         register int amode;             // (Parsed) addressing mode
249         register int i;
250         uint64_t eval;                  // Expression value
251         WORD eattr;                             // Expression attributes
252         int zpreq;                              // 1, optimize instr to zero-page form
253         register char * p;              // (Temp) string usage
254         ch_size = 0;                    // Reset chunk size on every instruction
255
256         //
257         // Parse 6502 addressing mode
258         //
259         zpreq = 0;
260
261         switch (tok[0])
262         {
263         case EOL:
264                 amode = A65_IMPL;
265                 break;
266
267         case '#':
268                 tok++;
269
270                 if (*tok == '>')
271                 {
272                         tok++;
273
274                         if (expr(exprbuf, &eval, &eattr, NULL) < 0)
275                                 return;
276
277                         amode = A65_IMMEDH;
278                         break;
279                 }
280                 else if (*tok == '<')
281                 {
282                         tok++;
283
284                         if (expr(exprbuf, &eval, &eattr, NULL) < 0)
285                                 return;
286
287                         amode = A65_IMMEDL;
288                         break;
289                 }
290
291                 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
292                         return;
293
294                 amode = A65_IMMED;
295                 break;
296
297         case '(':
298                 tok++;
299
300                 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
301                         return;
302
303                 if (*tok == ')')
304                 {
305                         // (foo) or (foo),y
306                         if (*++tok == ',')
307                         {
308                                 // (foo),y
309                                 tok++;
310                                 p = string[tok[1]];
311
312                                 // Sleazo tolower() -----------------vvvvvvvvvvv
313                                 if (*tok != SYMBOL || p[1] != EOS || (*p | 0x20) != 'y')
314                                         goto badmode;
315
316                                 tok += 2;
317                                 amode = A65_INDY;
318                         }
319                         else
320                                 amode = A65_IND;
321                 }
322                 else if (*tok == ',')
323                 {
324                         // (foo,x)
325                         tok++;
326                         p = string[tok[1]];
327
328                         // Sleazo tolower() -----------------vvvvvvvvvvv
329                         if (*tok != SYMBOL || p[1] != EOS || (*p | 0x20) != 'x')
330                                 goto badmode;
331
332                         tok += 2;
333
334                         if (*tok++ != ')')
335                                 goto badmode;
336
337                         amode = A65_INDX;
338                 }
339                 else
340                         goto badmode;
341
342                 break;
343
344         case '@':
345                 tok++;
346
347                 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
348                         return;
349
350                 if (*tok == '(')
351                 {
352                         tok++;
353                         p = string[tok[1]];
354
355                         if (*tok != SYMBOL || p[1] != EOS || tok[2] != ')' || tok[3] != EOL)
356                                 goto badmode;
357
358                         i = (*p | 0x20);        // Sleazo tolower()
359
360                         if (i == 'x')
361                                 amode = A65_INDX;
362                         else if (i == 'y')
363                                 amode = A65_INDY;
364                         else
365                                 goto badmode;
366
367                         tok += 3;               // Past SYMBOL <string> ')' EOL
368                         zpreq = 1;              // Request zeropage optimization
369                 }
370                 else if (*tok == EOL)
371                         amode = A65_IND;
372                 else
373                         goto badmode;
374
375                 break;
376
377         default:
378                 //
379                 // Short-circuit
380                 //   x,foo
381                 //   y,foo
382                 //
383                 p = string[tok[1]];
384                 // ggn: the following code is effectively disabled as it would make
385                 //      single letter labels not work correctly (would not identify the
386                 //      label properly). And from what I understand it's something to
387                 //      keep compatibility with the coinop assembler which is probably
388                 //      something we don't care about much :D
389 #if 0
390                 if (*tok == SYMBOL && p[1] == EOS && tok[2] == ',')
391                 {
392                         tok += 3;               // Past: SYMBOL <string> ','
393                         i = (*p | 0x20);
394
395                         if (i == 'x')
396                                 amode = A65_ABSX;
397                         else if (i == 'y')
398                                 amode = A65_ABSY;
399                         else
400                                 goto not_coinop;
401
402                         if (expr(exprbuf, &eval, &eattr, NULL) < 0)
403                                 return;
404
405                         if (*tok != EOL)
406                                 goto badmode;
407
408                         zpreq = 1;
409                         break;
410                 }
411
412 not_coinop:
413 #endif
414                 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
415                         return;
416
417                 zpreq = 1;
418
419                 if (*tok == EOL)
420                         amode = A65_ABS;
421                 else if (*tok == ',')
422                 {
423                         tok++;
424                         p = string[tok[1]];
425
426                         if (*tok != SYMBOL || p[1] != EOS)
427                                 goto badmode;
428
429                         tok += 2;
430
431                         //
432                         // Check for X or Y index register;
433                         // the OR with 0x20 is a sleazo conversion
434                         // to lower-case that actually works.
435                         //
436                         i = *p | 0x20;  // Oooh, this is slimey (but fast!)
437
438                         if (i == 'x')
439                                 amode = A65_ABSX;
440                         else if (i == 'y')
441                                 amode = A65_ABSY;
442                         else
443                                 goto badmode;
444                 }
445                 else
446                         goto badmode;
447
448                 break;
449
450 badmode:
451                 error("bad 6502 addressing mode");
452                 return;
453         }
454
455         //
456         // Optimize ABS modes to zero-page when possible
457         //   o  ZPX or ZPY is illegal, or
458         //   o  expr is zeropage && zeropageRequest && expression is defined
459         //
460         if (inf[op][amode] == ILLEGAL   // If current op is illegal,
461                 || (eval < 0x100                        // or expr must be zero-page
462                 && zpreq != 0                           // amode must request zero-page opt.
463                 && (eattr & DEFINED)))          // and the expression must be defined
464         {
465                 i = abs2zp[amode];                      // i = zero-page translation of amode
466 #ifdef DO_DEBUG
467                 DEBUG printf(" OPT: op=%d amode=%d i=%d inf[op][i]=%d\n",
468                                          op, amode, i, inf[op][i]);
469 #endif
470                 if (i >= 0 && (inf[op][i] & 0xFF) != ILLEGAL) // Use it if it's legal
471                         amode = i;
472         }
473
474 #ifdef DO_DEBUG
475         DEBUG printf("6502: op=%d amode=%d ", op, amode);
476         DEBUG printf("inf[op][amode]=%d\n", (int)inf[op][amode]);
477 #endif
478
479         switch (inf[op][amode])
480         {
481                 case A65_IMPL:          // Just leave the instruction
482                         D_byte(ops[op][amode]);
483                         break;
484
485                 case A65_IMMEDH:
486                         D_byte(ops[op][amode]);
487
488                         if (!(eattr & DEFINED))
489                         {
490                                 AddFixup(FU_BYTEH, sloc, exprbuf);
491                                 eval = 0;
492                         }
493
494                         eval = (eval >> 8) & 0xFF; // Bring high byte to low
495                         D_byte(eval);                           // Deposit byte following instr
496                         break;
497
498                 case A65_IMMEDL:
499                         D_byte(ops[op][amode]);
500
501                         if (!(eattr & DEFINED))
502                         {
503                                 AddFixup(FU_BYTEL, sloc, exprbuf);
504                                 eval = 0;
505                         }
506
507                         eval = eval & 0xFF; // Mask high byte
508                         D_byte(eval);           // Deposit byte following instr
509                         break;
510
511                 case A65_IMMED:
512                 case A65_INDX:
513                 case A65_INDY:
514                 case A65_ZP:
515                 case A65_ZPX:
516                 case A65_ZPY:
517                         D_byte(ops[op][amode]);
518
519                         if (!(eattr & DEFINED))
520                         {
521                                 AddFixup(FU_BYTE, sloc, exprbuf);
522                                 eval = 0;
523                         }
524                         else if (eval + 0x100 >= 0x200)
525                         {
526                                 error(range_error);
527                                 eval = 0;
528                         }
529
530                         D_byte(eval);           // Deposit byte following instr
531                         break;
532
533                 case A65_REL:
534                         D_byte(ops[op][amode]);
535
536                         if (eattr & DEFINED)
537                         {
538                                 eval -= (sloc + 1);
539
540                                 if (eval + 0x80 >= 0x100)
541                                 {
542                                         error(range_error);
543                                         eval = 0;
544                                 }
545
546                                 D_byte(eval);
547                         }
548                         else
549                         {
550                                 AddFixup(FU_6BRA, sloc, exprbuf);
551                                 D_byte(0);
552                         }
553
554                         break;
555
556                 case A65_ABS:
557                 case A65_ABSX:
558                 case A65_ABSY:
559                 case A65_IND:
560                         D_byte(ops[op][amode]);
561
562                         if (!(eattr & DEFINED))
563                         {
564                                 AddFixup(FU_WORD, sloc, exprbuf);
565                                 eval = 0;
566                         }
567
568                         D_rword(eval);
569                         break;
570
571                         //
572                         // Deposit 3 NOPs for illegal things
573                         //
574                 default:
575                 case ILLEGAL:
576                         for(i=0; i<3; i++)
577                                 D_byte(NOP);
578
579                         error("illegal 6502 addressing mode");
580         }
581
582         // Check for overflow of code region
583         if (sloc > 0x10000L)
584                 fatal("6502 code pointer > 64K");
585
586         if (*tok != EOL)
587                 error(extra_stuff);
588 }
589
590
591 //
592 // Generate 6502 object output file.
593 //
594 // ggn: converted into a com/exe/xex output format
595 void m6502obj(int ofd)
596 {
597         uint16_t exeheader[3];
598         int headsize = 6;
599         uint16_t * headpoint = exeheader;
600
601         CHUNK * ch = sect[M6502].scode;
602
603         // If no 6502 code was generated, forget it
604         if ((ch == NULL) || (ch->challoc == 0))
605                 return;
606
607         exeheader[0] = 0xFFFF;          // Mandatory for first segment
608         register uint8_t * p = ch->chptr;
609
610         for(uint16_t * l=&orgmap[0][0]; l<currentorg; l+=2)
611         {
612                 exeheader[1] = l[0];
613                 exeheader[2] = l[1] - 1;
614
615                 // Write header
616                 size_t unused = write(ofd, headpoint, headsize);
617                 unused = write(ofd, p + l[0], l[1] - l[0]);
618
619                 // Skip the $FFFF after first segment, it's not mandatory
620                 headpoint = &exeheader[1];
621                 headsize = 4;
622         }
623 }
624