2 // RMAC - Reboot's Macro Assembler for all Atari computers
3 // 6502.C - 6502 Assembler
4 // Copyright (C) 199x Landon Dyer, 2011-2019 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
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
27 #define UPSEG_SIZE 0x10010L // size of 6502 code buffer, 64K+16bytes
30 static uint16_t orgmap[1024][2]; // Mark all 6502 org changes
33 const char in_6502mode[] = "directive illegal in .6502 section";
34 uint16_t * currentorg = &orgmap[0][0]; // Current org range
35 char strtoa8[128]; // ASCII to Atari 800 internal conversion table
38 // 6502 addressing modes;
39 // DO NOT CHANGE THESE VALUES.
56 #define NMACHOPS 56 // Number of machine ops
57 #define NMODES 14 // Number of addressing modes
58 #define NOP 0xEA // 6502 NOP instruction
59 #define ILLEGAL 0xFF // 'Illegal instr' marker
60 #define END65 0xFF // End-of-an-instr-list
62 static char imodes[] =
64 A65_IMMED, 0x69, A65_ABS, 0x6D, A65_ZP, 0x65, A65_INDX, 0x61, A65_INDY, 0x71,
65 A65_ZPX, 0x75, A65_ABSX, 0x7D, A65_ABSY, 0x79, END65,
66 A65_IMMED, 0x29, A65_ABS, 0x2D, A65_ZP, 0x25, A65_INDX, 0x21, A65_INDY, 0x31,
67 A65_ZPX, 0x35, A65_ABSX, 0x3D, A65_ABSY, 0x39, END65,
68 A65_ABS, 0x0E, A65_ZP, 0x06, A65_IMPL, 0x0A, A65_ZPX, 0x16, A65_ABSX,
78 A65_ABS, 0x2C, A65_ZP, 0x24, END65,
79 A65_IMPL, 0x00, END65,
80 A65_IMPL, 0x18, END65,
81 A65_IMPL, 0xD8, END65,
82 A65_IMPL, 0x58, END65,
83 A65_IMPL, 0xB8, END65,
84 A65_IMMED, 0xC9, A65_ABS, 0xCD, A65_ZP, 0xC5, A65_INDX, 0xC1, A65_INDY, 0xD1,
85 A65_ZPX, 0xD5, A65_ABSX, 0xDD, A65_ABSY, 0xD9, END65,
86 A65_IMMED, 0xE0, A65_ABS, 0xEC, A65_ZP, 0xE4, END65,
87 A65_IMMED, 0xC0, A65_ABS, 0xCC, A65_ZP, 0xC4, END65,
88 A65_ABS, 0xCE, A65_ZP, 0xC6, A65_ZPX, 0xD6, A65_ABSX, 0xDE, END65,
89 A65_IMPL, 0xCA, END65,
90 A65_IMPL, 0x88, END65,
91 A65_IMMED, 0x49, A65_ABS, 0x4D, A65_ZP, 0x45, A65_INDX, 0x41, A65_INDY, 0x51,
92 A65_ZPX, 0x55, A65_ABSX, 0x5D, A65_ABSY, 0x59, END65,
93 A65_ABS, 0xEE, A65_ZP, 0xE6, A65_ZPX, 0xF6, A65_ABSX, 0xFE, END65,
94 A65_IMPL, 0xE8, END65,
95 A65_IMPL, 0xC8, END65,
96 A65_ABS, 0x4C, A65_IND, 0x6C, END65,
98 A65_IMMED, 0xA9, A65_ABS, 0xAD, A65_ZP, 0xA5, A65_INDX, 0xA1, A65_INDY, 0xB1,
99 A65_ZPX, 0xB5, A65_ABSX, 0xBD, A65_ABSY, 0xB9, A65_IMMEDH, 0xA9, A65_IMMEDL, 0xA9, END65,
100 A65_IMMED, 0xA2, A65_ABS, 0xAE, A65_ZP, 0xA6, A65_ABSY, 0xBE,
101 A65_ZPY, 0xB6, A65_IMMEDH, 0xA2, A65_IMMEDL, 0xA2, END65,
102 A65_IMMED, 0xA0, A65_ABS, 0xAC, A65_ZP, 0xA4, A65_ZPX, 0xB4,
103 A65_ABSX, 0xBC, A65_IMMEDH, 0xA0, A65_IMMEDL, 0xA0, END65,
104 A65_ABS, 0x4E, A65_ZP, 0x46, A65_IMPL, 0x4A, A65_ZPX, 0x56,
105 A65_ABSX, 0x5E, END65,
106 A65_IMPL, 0xEA, END65,
107 A65_IMMED, 0x09, A65_ABS, 0x0D, A65_ZP, 0x05, A65_INDX, 0x01, A65_INDY, 0x11,
108 A65_ZPX, 0x15, A65_ABSX, 0x1D, A65_ABSY, 0x19, END65,
109 A65_IMPL, 0x48, END65,
110 A65_IMPL, 0x08, END65,
111 A65_IMPL, 0x68, END65,
112 A65_IMPL, 0x28, END65,
113 A65_ABS, 0x2E, A65_ZP, 0x26, A65_IMPL, 0x2A, A65_ZPX, 0x36,
114 A65_ABSX, 0x3E, END65,
115 A65_ABS, 0x6E, A65_ZP, 0x66, A65_IMPL, 0x6A, A65_ZPX, 0x76,
116 A65_ABSX, 0x7E, END65,
117 A65_IMPL, 0x40, END65,
118 A65_IMPL, 0x60, END65,
119 A65_IMMED, 0xE9, A65_ABS, 0xED, A65_ZP, 0xE5, A65_INDX, 0xE1, A65_INDY, 0xF1,
120 A65_ZPX, 0xF5, A65_ABSX, 0xFD, A65_ABSY, 0xF9, END65,
121 A65_IMPL, 0x38, END65,
122 A65_IMPL, 0xF8, END65,
123 A65_IMPL, 0x78, END65,
124 A65_ABS, 0x8D, A65_ZP, 0x85, A65_INDX, 0x81, A65_INDY, 0x91, A65_ZPX, 0x95,
125 A65_ABSX, 0x9D, A65_ABSY, 0x99, END65,
126 A65_ABS, 0x8E, A65_ZP, 0x86, A65_ZPY, 0x96, END65,
127 A65_ABS, 0x8C, A65_ZP, 0x84, A65_ZPX, 0x94, END65,
128 A65_IMPL, 0xAA, END65,
129 A65_IMPL, 0xA8, END65,
130 A65_IMPL, 0xBA, END65,
131 A65_IMPL, 0x8A, END65,
132 A65_IMPL, 0x9A, END65,
133 A65_IMPL, 0x98, END65
136 static char ops[NMACHOPS][NMODES]; // Opcodes
137 static unsigned char inf[NMACHOPS][NMODES]; // Construction info
139 // Absolute-to-zeropage translation table
140 static int abs2zp[] =
156 static char a8internal[] =
158 ' ', 0, '!', 1, '"', 2, '#', 3, '$', 4, '%', 5, '&', 6, '\'', 7,
159 '(', 8, ')', 9, '*', 10, '+', 11, ',', 12, '-', 13, '.', 14, '/', 15,
160 '0', 16, '1', 17, '2', 18, '3', 19, '4', 20, '5', 21, '6', 22, '7', 23,
161 '8', 24, '9', 25, ':', 26, ';', 27, '<', 28, '=', 29, '>', 30, '?', 31,
162 '@', 32, 'A', 33, 'B', 34, 'C', 35, 'D', 36, 'E', 37, 'F', 38, 'G', 39,
163 'H', 40, 'I', 41, 'J', 42, 'K', 43, 'L', 44, 'M', 45, 'N', 46, 'O', 47,
164 'P', 48, 'Q', 49, 'R', 50, 'S', 51, 'T', 52, 'U', 53, 'V', 54, 'W', 55,
165 'X', 56, 'Y', 57, 'Z', 58, '[', 59, '\\', 60, ']', 61, '^', 62, '_', 63,
166 'a', 97, 'b', 98, 'c', 99, 'd', 100, 'e', 101, 'f', 102, 'g', 103, 'h', 104,
167 'i', 105, 'j', 106, 'k', 107, 'l', 108, 'm', 109, 'n', 110, 'o', 111, 'p', 112,
168 'q', 113, 'r', 114, 's', 115, 't', 116, 'u', 117, 'v', 118, 'w', 119, 'x', 120,
174 // Initialize 6502 assembler
181 register char * s = imodes;
183 // Set all instruction slots to illegal
184 for(i=0; i<NMACHOPS; i++)
185 for(j=0; j<NMODES; j++)
188 // Uncompress legal instructions into their slots
189 for(i=0; i<NMACHOPS; i++)
197 /* hack A65_REL mode */
200 inf[i][A65_ABS] = A65_REL;
201 ops[i][A65_ABS] = s[1];
202 inf[i][A65_ZP] = A65_REL;
203 ops[i][A65_ZP] = s[1];
206 while (*(s += 2) != (char)END65);
211 // Set up first org section (set to zero)
214 SwitchSection(M6502); // Switch to 6502 section
216 // Initialise string conversion table(s)
217 char * p = a8internal;
218 memset(strtoa8, 31, 128); // 31=fallback value ("?")
220 for(; p<a8internal+sizeof(a8internal); p+=2)
221 strtoa8[p[0]] = p[1];
225 // Allocate and clear 64K of space for the 6502 section
227 memset(sect[M6502].scode->chptr, 0, UPSEG_SIZE);
230 SwitchSection(TEXT); // Go back to TEXT
235 // .6502 --- enter 6502 mode
239 SaveSection(); // Save curent section
240 SwitchSection(M6502); // Switch to 6502 section
247 // Do 6502 code generation
251 register int amode; // (Parsed) addressing mode
253 uint64_t eval; // Expression value
254 WORD eattr; // Expression attributes
255 int zpreq; // 1, optimize instr to zero-page form
256 register char * p; // (Temp) string usage
257 ch_size = 0; // Reset chunk size on every instruction
260 // Parse 6502 addressing mode
285 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
291 else if (*tok == '<')
295 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
302 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
311 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
324 // Sleazo tolower() -----------------vvvvvvvvvvv
325 if (*tok != SYMBOL || p[1] != EOS || (*p | 0x20) != 'y')
343 else if (*tok == ',')
350 // Sleazo tolower() -----------------vvvvvvvvvvv
351 if (*tok != SYMBOL || p[1] != EOS || (*p | 0x20) != 'x')
363 if ((tok[1] != ')') || (tok[2] != EOL))
374 // I'm guessing that the form of this is @<expr>(X) or @<expr>(Y), which
375 // I've *never* seen before. :-/
379 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
388 if (*tok != SYMBOL || p[1] != EOS || tok[2] != ')' || tok[3] != EOL)
391 i = (*p | 0x20); // Sleazo tolower()
400 tok += 3; // Past SYMBOL <string> ')' EOL
402 if ((tok[1] != ')') || (tok[2] != EOL))
407 else if (tok[0] == KW_Y)
414 zpreq = 1; // Request zeropage optimization
416 else if (*tok == EOL)
430 // ggn: the following code is effectively disabled as it would make
431 // single letter labels not work correctly (would not identify the
432 // label properly). And from what I understand it's something to
433 // keep compatibility with the coinop assembler which is probably
434 // something we don't care about much :D
436 if (*tok == SYMBOL && p[1] == EOS && tok[2] == ',')
438 tok += 3; // Past: SYMBOL <string> ','
448 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
460 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
467 else if (*tok == ',')
473 if (*tok != SYMBOL || p[1] != EOS)
479 // Check for X or Y index register;
480 // the OR with 0x20 is a sleazo conversion
481 // to lower-case that actually works.
483 i = *p | 0x20; // Oooh, this is slimey (but fast!)
497 else if (tok[0] == KW_Y)
513 error("bad 6502 addressing mode");
518 // Optimize ABS modes to zero-page when possible
519 // o ZPX or ZPY is illegal, or
520 // o expr is zeropage && zeropageRequest && expression is defined
522 if (inf[op][amode] == ILLEGAL // If current op is illegal,
523 || (eval < 0x100 // or expr must be zero-page
524 && zpreq != 0 // amode must request zero-page opt.
525 && (eattr & DEFINED))) // and the expression must be defined
527 i = abs2zp[amode]; // i = zero-page translation of amode
529 DEBUG printf(" OPT: op=%d amode=%d i=%d inf[op][i]=%d\n",
530 op, amode, i, inf[op][i]);
532 if (i >= 0 && (inf[op][i] & 0xFF) != ILLEGAL) // Use it if it's legal
537 DEBUG printf("6502: op=%d amode=%d ", op, amode);
538 DEBUG printf("inf[op][amode]=%d\n", (int)inf[op][amode]);
541 switch (inf[op][amode])
543 case A65_IMPL: // Just leave the instruction
544 D_byte(ops[op][amode]);
548 D_byte(ops[op][amode]);
550 if (!(eattr & DEFINED))
552 AddFixup(FU_BYTEH, sloc, exprbuf);
556 eval = (eval >> 8) & 0xFF; // Bring high byte to low
557 D_byte(eval); // Deposit byte following instr
561 D_byte(ops[op][amode]);
563 if (!(eattr & DEFINED))
565 AddFixup(FU_BYTEL, sloc, exprbuf);
569 eval = eval & 0xFF; // Mask high byte
570 D_byte(eval); // Deposit byte following instr
579 D_byte(ops[op][amode]);
581 if (!(eattr & DEFINED))
583 AddFixup(FU_BYTE, sloc, exprbuf);
586 else if (eval + 0x100 >= 0x200)
592 D_byte(eval); // Deposit byte following instr
596 D_byte(ops[op][amode]);
602 if (eval + 0x80 >= 0x100)
612 AddFixup(FU_6BRA, sloc, exprbuf);
622 D_byte(ops[op][amode]);
624 if (!(eattr & DEFINED))
626 AddFixup(FU_WORD, sloc, exprbuf);
634 // Deposit 3 NOPs for illegal things (why 3? why not 30? or zero?)
641 error("illegal 6502 addressing mode");
644 // Check for overflow of code region
646 fatal("6502 code pointer > 64K");
653 // Generate 6502 object output file.
654 // ggn: Converted to COM/EXE/XEX output format
656 void m6502obj(int ofd)
660 CHUNK * ch = sect[M6502].scode;
662 // If no 6502 code was generated, bail out
663 if ((ch == NULL) || (ch->challoc == 0))
666 register uint8_t * p = ch->chptr;
668 // Write out mandatory $FFFF header
669 header[0] = header[1] = 0xFF;
670 uint32_t unused = write(ofd, header, 2);
672 for(uint16_t * l=&orgmap[0][0]; l<currentorg; l+=2)
674 SETLE16(header, 0, l[0]);
675 SETLE16(header, 2, l[1] - 1);
677 // Write header for segment
678 unused = write(ofd, header, 4);
679 // Write the segment data
680 unused = write(ofd, p + l[0], l[1] - l[0]);