2 // RMAC - Reboot's Macro Assembler for all Atari computers
3 // 6502.C - 6502 Assembler
4 // Copyright (C) 199x Landon Dyer, 2011-2020 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
252 uint64_t eval = -1; // Expression value
253 WORD eattr = 0; // Expression attributes
254 int zpreq = 0; // 1 = optimize instr to zero-page form
255 ch_size = 0; // Reset chunk size on every instruction
258 // Parse 6502 addressing mode
282 else if (*tok == '<')
290 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
298 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
318 else if ((tok[0] == ',') && (tok[1] == KW_X) && (tok[2] == ')'))
329 // I'm guessing that the form of this is @<expr>(X) or @<expr>(Y), which
330 // I've *never* seen before. :-/
334 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
341 if ((tok[1] != ')') || (tok[2] != EOL))
346 else if (tok[0] == KW_Y)
352 zpreq = 1; // Request zeropage optimization
354 else if (*tok == EOL)
365 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
368 zpreq = 1; // Request zeropage optimization
372 else if (tok[0] == ',')
381 else if (tok[0] == KW_Y)
393 error("bad 6502 addressing mode");
398 // Optimize ABS modes to zero-page when possible
399 // o ZPX or ZPY is illegal, or
400 // o expr is zeropage && zeropageRequest && expression is defined
402 if ((inf[op][amode] == ILLEGAL) // If current op is illegal OR
403 || (zpreq // amode requested a zero-page optimize
404 && (eval < 0x100) // and expr is zero-page
405 && (eattr & DEFINED))) // and the expression is defined
407 int i = abs2zp[amode]; // Get zero-page translation of amode
409 DEBUG printf(" OPT: op=%d amode=%d i=%d inf[op][i]=%d\n",
410 op, amode, i, inf[op][i]);
412 if (i >= 0 && (inf[op][i] & 0xFF) != ILLEGAL) // & use it if it's legal
417 DEBUG printf("6502: op=%d amode=%d ", op, amode);
418 DEBUG printf("inf[op][amode]=%d\n", (int)inf[op][amode]);
421 switch (inf[op][amode])
423 case A65_IMPL: // Just leave the instruction
424 D_byte(ops[op][amode]);
428 D_byte(ops[op][amode]);
430 if (!(eattr & DEFINED))
432 AddFixup(FU_BYTEH, sloc, exprbuf);
436 eval = (eval >> 8) & 0xFF; // Bring high byte to low
437 D_byte(eval); // Deposit byte following instr
441 D_byte(ops[op][amode]);
443 if (!(eattr & DEFINED))
445 AddFixup(FU_BYTEL, sloc, exprbuf);
449 eval = eval & 0xFF; // Mask high byte
450 D_byte(eval); // Deposit byte following instr
459 D_byte(ops[op][amode]);
461 if (!(eattr & DEFINED))
463 AddFixup(FU_BYTE, sloc, exprbuf);
466 else if (eval + 0x100 >= 0x200)
472 D_byte(eval); // Deposit byte following instr
476 D_byte(ops[op][amode]);
482 if (eval + 0x80 >= 0x100)
492 AddFixup(FU_6BRA, sloc, exprbuf);
502 D_byte(ops[op][amode]);
504 if (!(eattr & DEFINED))
506 AddFixup(FU_WORD, sloc, exprbuf);
514 // Deposit 3 NOPs for illegal things (why 3? why not 30? or zero?)
522 error("illegal 6502 addressing mode");
525 // Check for overflow of code region
527 fatal("6502 code pointer > 64K");
534 // Generate 6502 object output file.
535 // ggn: Converted to COM/EXE/XEX output format
537 void m6502obj(int ofd)
541 CHUNK * ch = sect[M6502].scode;
543 // If no 6502 code was generated, bail out
544 if ((ch == NULL) || (ch->challoc == 0))
547 register uint8_t * p = ch->chptr;
549 // Write out mandatory $FFFF header
550 header[0] = header[1] = 0xFF;
551 uint32_t unused = write(ofd, header, 2);
553 for(uint16_t * l=&orgmap[0][0]; l<currentorg; l+=2)
555 SETLE16(header, 0, l[0]);
556 SETLE16(header, 2, l[1] - 1);
558 // Write header for segment
559 unused = write(ofd, header, 4);
560 // Write the segment data
561 unused = write(ofd, p + l[0], l[1] - l[0]);