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