]> Shamusworld >> Repos - rmac/blob - 6502.c
(c) message in header files and doc mini adjustments.
[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-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 //    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         VALUE 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 ((int)*tok)
262         {
263         case EOL:
264                 amode = A65_IMPL;
265                 break;
266
267         case '#':
268                 tok++;
269
270                 if (*tok == '>')
271                 {
272                         tok++;
273                         if (expr(exprbuf, &eval, &eattr, NULL) < 0)
274                                 return;
275
276                         amode = A65_IMMEDH;
277                         break;
278                 }
279                 else if (*tok == '<')
280                 {
281                         tok++;
282                         if (expr(exprbuf, &eval, &eattr, NULL) < 0)
283                                 return;
284
285                         amode = A65_IMMEDL;
286                         break;
287                 }
288
289                 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
290                         return;
291
292                 amode = A65_IMMED;
293                 break;
294
295         case '(':
296                 tok++;
297
298                 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
299                         return;
300
301                 if (*tok == ')')
302                 {
303                         // (foo) or (foo),y
304                         if (*++tok == ',')
305                         {
306                                 // (foo),y
307                                 tok++;
308                                 p = string[tok[1]];
309
310                                 if (*tok != SYMBOL || p[1] != EOS || (*p | 0x20) != 'y') // Sleazo tolower()
311                                         goto badmode;
312
313                                 tok += 2;
314                                 amode = A65_INDY;
315                         }
316                         else
317                                 amode = A65_IND;
318                 }
319                 else if (*tok == ',')
320                 {
321                         // (foo,x)
322                         tok++;
323                         p = string[tok[1]];
324
325                         if (*tok != SYMBOL || p[1] != EOS || (*p | 0x20) != 'x') // Sleazo tolower()
326                                 goto badmode;
327
328                         tok += 2;
329
330                         if (*tok++ != ')')
331                                 goto badmode;
332
333                         amode = A65_INDX;
334                 }
335                 else
336                         goto badmode;
337
338                 break;
339
340         case '@':
341                 tok++;
342
343                 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
344                         return;
345
346                 if (*tok == '(')
347                 {
348                         tok++;
349                         p = string[tok[1]];
350
351                         if (*tok != SYMBOL || p[1] != EOS || tok[2] != ')' || tok[3] != EOL)
352                                 goto badmode;
353
354                         i = (*p | 0x20);        // Sleazo tolower()
355
356                         if (i == 'x')
357                                 amode = A65_INDX;
358                         else if (i == 'y')
359                                 amode = A65_INDY;
360                         else
361                                 goto badmode;
362
363                         tok += 3;               // Past SYMBOL <string> ')' EOL
364                         zpreq = 1;              // Request zeropage optimization
365                 }
366                 else if (*tok == EOL)
367                         amode = A65_IND;
368                 else
369                         goto badmode;
370
371                 break;
372
373         default:
374                 //
375                 // Short-circuit
376                 //   x,foo
377                 //   y,foo
378                 //
379                 p = string[tok[1]];
380                 // ggn: the following code is effectively disabled as it would make
381                 //      single letter labels not work correctly (would not identify the
382                 //      label properly). And from what I understand it's something to
383                 //      keep compatibility with the coinop assembler which is probably
384                 //      something we don't care about much :D
385 #if 0
386                 if (*tok == SYMBOL && p[1] == EOS && tok[2] == ',')
387                 {
388                         tok += 3;               // Past: SYMBOL <string> ','
389                         i = (*p | 0x20);
390
391                         if (i == 'x')
392                                 amode = A65_ABSX;
393                         else if (i == 'y')
394                                 amode = A65_ABSY;
395                         else
396                                 goto not_coinop;
397
398                         if (expr(exprbuf, &eval, &eattr, NULL) < 0)
399                                 return;
400
401                         if (*tok != EOL)
402                                 goto badmode;
403
404                         zpreq = 1;
405                         break;
406                 }
407
408 not_coinop:
409 #endif
410                 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
411                         return;
412
413                 zpreq = 1;
414
415                 if (*tok == EOL)
416                         amode = A65_ABS;
417                 else if (*tok == ',')
418                 {
419                         tok++;
420                         p = string[tok[1]];
421
422                         if (*tok != SYMBOL || p[1] != EOS)
423                                 goto badmode;
424
425                         tok += 2;
426
427                         //
428                         // Check for X or Y index register;
429                         // the OR with 0x20 is a sleazo conversion
430                         // to lower-case that actually works.
431                         //
432                         i = *p | 0x20;  // Oooh, this is slimey (but fast!)
433
434                         if (i == 'x')
435                                 amode = A65_ABSX;
436                         else if (i == 'y')
437                                 amode = A65_ABSY;
438                         else
439                                 goto badmode;
440                 }
441                 else
442                         goto badmode;
443
444                 break;
445
446 badmode:
447                 error("bad 6502 addressing mode");
448                 return;
449         }
450
451         //
452         // Optimize ABS modes to zero-page when possible
453         //   o  ZPX or ZPY is illegal, or
454         //   o  expr is zeropage && zeropageRequest && expression is defined
455         //
456         if (inf[op][amode] == ILLEGAL   // If current op is illegal,
457                 || (eval < 0x100                        // or expr must be zero-page
458                 && zpreq != 0                           // amode must request zero-page opt.
459                 && (eattr & DEFINED)))          // and the expression must be defined
460         {
461                 i = abs2zp[amode];                      // i = zero-page translation of amode
462 #ifdef DO_DEBUG
463                 DEBUG printf(" OPT: op=%d amode=%d i=%d inf[op][i]=%d\n",
464                                          op, amode, i, inf[op][i]);
465 #endif
466                 if (i >= 0 && (inf[op][i] & 0xFF) != ILLEGAL) // Use it if it's legal
467                         amode = i;
468         }
469
470 #ifdef DO_DEBUG
471         DEBUG printf("6502: op=%d amode=%d ", op, amode);
472         DEBUG printf("inf[op][amode]=%d\n", (int)inf[op][amode]);
473 #endif
474
475         switch (inf[op][amode])
476         {
477                 case A65_IMPL:          // Just leave the instruction
478                         D_byte(ops[op][amode]);
479                         break;
480
481                 case A65_IMMEDH:
482                         D_byte(ops[op][amode]);
483
484                         if (!(eattr & DEFINED))
485                         {
486                                 AddFixup(FU_BYTEH, sloc, exprbuf);
487                                 eval = 0;
488                         }
489
490                         eval = (eval >> 8) & 0xFF; // Bring high byte to low
491                         D_byte(eval);                           // Deposit byte following instr
492                         break;
493
494                 case A65_IMMEDL:
495                         D_byte(ops[op][amode]);
496
497                         if (!(eattr & DEFINED))
498                         {
499                                 AddFixup(FU_BYTEL, sloc, exprbuf);
500                                 eval = 0;
501                         }
502
503                         eval = eval & 0xFF; // Mask high byte
504                         D_byte(eval);           // Deposit byte following instr
505                         break;
506
507                 case A65_IMMED:
508                 case A65_INDX:
509                 case A65_INDY:
510                 case A65_ZP:
511                 case A65_ZPX:
512                 case A65_ZPY:
513                         D_byte(ops[op][amode]);
514
515                         if (!(eattr & DEFINED))
516                         {
517                                 AddFixup(FU_BYTE, sloc, exprbuf);
518                                 eval = 0;
519                         }
520                         else if (eval + 0x100 >= 0x200)
521                         {
522                                 error(range_error);
523                                 eval = 0;
524                         }
525
526                         D_byte(eval);           // Deposit byte following instr
527                         break;
528
529                 case A65_REL:
530                         D_byte(ops[op][amode]);
531
532                         if (eattr & DEFINED)
533                         {
534                                 eval -= (sloc + 1);
535
536                                 if (eval + 0x80 >= 0x100)
537                                 {
538                                         error(range_error);
539                                         eval = 0;
540                                 }
541
542                                 D_byte(eval);
543                         }
544                         else
545                         {
546                                 AddFixup(FU_6BRA, sloc, exprbuf);
547                                 D_byte(0);
548                         }
549
550                         break;
551
552                 case A65_ABS:
553                 case A65_ABSX:
554                 case A65_ABSY:
555                 case A65_IND:
556                         D_byte(ops[op][amode]);
557
558                         if (!(eattr & DEFINED))
559                         {
560                                 AddFixup(FU_WORD, sloc, exprbuf);
561                                 eval = 0;
562                         }
563
564                         D_rword(eval);
565                         break;
566
567                         //
568                         // Deposit 3 NOPs for illegal things
569                         //
570                 default:
571                 case ILLEGAL:
572                         for(i=0; i<3; i++)
573                                 D_byte(NOP);
574
575                         error("illegal 6502 addressing mode");
576         }
577
578         // Check for overflow of code region
579         if (sloc > 0x10000L)
580                 fatal("6502 code pointer > 64K");
581
582         if (*tok != EOL)
583                 error(extra_stuff);
584 }
585
586
587 //
588 // Generate 6502 object output file.
589 //
590 // ggn: converted into a com/exe/xex output format
591 void m6502obj(int ofd)
592 {
593         uint16_t exeheader[3];
594         int headsize = 6;
595         uint16_t * headpoint = exeheader;
596
597         CHUNK * ch = sect[M6502].scode;
598
599         // If no 6502 code was generated, forget it
600         if ((ch == NULL) || (ch->challoc == 0))
601                 return;
602
603         exeheader[0] = 0xFFFF;          // Mandatory for first segment
604         register uint8_t * p = ch->chptr;
605
606         for(uint16_t * l=&orgmap[0][0]; l<currentorg; l+=2)
607         {
608                 exeheader[1] = l[0];
609                 exeheader[2] = l[1] - 1;
610
611                 // Write header
612                 size_t unused = write(ofd, headpoint, headsize);
613                 unused = write(ofd, p + l[0], l[1] - l[0]);
614
615                 // Skip the $FFFF after first segment, it's not mandatory
616                 headpoint = &exeheader[1];
617                 headsize = 4;
618         }
619 }
620