2 // RMAC - Renamed Macro Assembler for all Atari computers
3 // 6502.C - 6502 Assembler
4 // Copyright (C) 199x Landon Dyer, 2011-2021 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
28 #define UPSEG_SIZE 0x10010L // size of 6502 code buffer, 64K+16bytes
31 static uint16_t orgmap[1024][2]; // Mark all 6502 org changes
34 const char in_6502mode[] = "directive illegal in .6502 section";
35 uint16_t * currentorg = &orgmap[0][0]; // Current org range
36 char strtoa8[128]; // ASCII to Atari 800 internal conversion table
39 // 6502 addressing modes;
40 // DO NOT CHANGE THESE VALUES.
57 #define NMACHOPS 56 // Number of machine ops
58 #define NMODES 14 // Number of addressing modes
59 #define NOP 0xEA // 6502 NOP instruction
60 #define ILLEGAL 0xFF // 'Illegal instr' marker
61 #define END65 0xFF // End-of-an-instr-list
63 static char imodes[] =
65 A65_IMMED, 0x69, A65_ABS, 0x6D, A65_ZP, 0x65, A65_INDX, 0x61, A65_INDY, 0x71,
66 A65_ZPX, 0x75, A65_ABSX, 0x7D, A65_ABSY, 0x79, END65,
67 A65_IMMED, 0x29, A65_ABS, 0x2D, A65_ZP, 0x25, A65_INDX, 0x21, A65_INDY, 0x31,
68 A65_ZPX, 0x35, A65_ABSX, 0x3D, A65_ABSY, 0x39, END65,
69 A65_ABS, 0x0E, A65_ZP, 0x06, A65_IMPL, 0x0A, A65_ZPX, 0x16, A65_ABSX,
79 A65_ABS, 0x2C, A65_ZP, 0x24, END65,
80 A65_IMPL, 0x00, END65,
81 A65_IMPL, 0x18, END65,
82 A65_IMPL, 0xD8, END65,
83 A65_IMPL, 0x58, END65,
84 A65_IMPL, 0xB8, END65,
85 A65_IMMED, 0xC9, A65_ABS, 0xCD, A65_ZP, 0xC5, A65_INDX, 0xC1, A65_INDY, 0xD1,
86 A65_ZPX, 0xD5, A65_ABSX, 0xDD, A65_ABSY, 0xD9, END65,
87 A65_IMMED, 0xE0, A65_ABS, 0xEC, A65_ZP, 0xE4, END65,
88 A65_IMMED, 0xC0, A65_ABS, 0xCC, A65_ZP, 0xC4, END65,
89 A65_ABS, 0xCE, A65_ZP, 0xC6, A65_ZPX, 0xD6, A65_ABSX, 0xDE, END65,
90 A65_IMPL, 0xCA, END65,
91 A65_IMPL, 0x88, END65,
92 A65_IMMED, 0x49, A65_ABS, 0x4D, A65_ZP, 0x45, A65_INDX, 0x41, A65_INDY, 0x51,
93 A65_ZPX, 0x55, A65_ABSX, 0x5D, A65_ABSY, 0x59, END65,
94 A65_ABS, 0xEE, A65_ZP, 0xE6, A65_ZPX, 0xF6, A65_ABSX, 0xFE, END65,
95 A65_IMPL, 0xE8, END65,
96 A65_IMPL, 0xC8, END65,
97 A65_ABS, 0x4C, A65_IND, 0x6C, END65,
99 A65_IMMED, 0xA9, A65_ABS, 0xAD, A65_ZP, 0xA5, A65_INDX, 0xA1, A65_INDY, 0xB1,
100 A65_ZPX, 0xB5, A65_ABSX, 0xBD, A65_ABSY, 0xB9, A65_IMMEDH, 0xA9, A65_IMMEDL, 0xA9, END65,
101 A65_IMMED, 0xA2, A65_ABS, 0xAE, A65_ZP, 0xA6, A65_ABSY, 0xBE,
102 A65_ZPY, 0xB6, A65_IMMEDH, 0xA2, A65_IMMEDL, 0xA2, END65,
103 A65_IMMED, 0xA0, A65_ABS, 0xAC, A65_ZP, 0xA4, A65_ZPX, 0xB4,
104 A65_ABSX, 0xBC, A65_IMMEDH, 0xA0, A65_IMMEDL, 0xA0, END65,
105 A65_ABS, 0x4E, A65_ZP, 0x46, A65_IMPL, 0x4A, A65_ZPX, 0x56,
106 A65_ABSX, 0x5E, END65,
107 A65_IMPL, 0xEA, END65,
108 A65_IMMED, 0x09, A65_ABS, 0x0D, A65_ZP, 0x05, A65_INDX, 0x01, A65_INDY, 0x11,
109 A65_ZPX, 0x15, A65_ABSX, 0x1D, A65_ABSY, 0x19, END65,
110 A65_IMPL, 0x48, END65,
111 A65_IMPL, 0x08, END65,
112 A65_IMPL, 0x68, END65,
113 A65_IMPL, 0x28, END65,
114 A65_ABS, 0x2E, A65_ZP, 0x26, A65_IMPL, 0x2A, A65_ZPX, 0x36,
115 A65_ABSX, 0x3E, END65,
116 A65_ABS, 0x6E, A65_ZP, 0x66, A65_IMPL, 0x6A, A65_ZPX, 0x76,
117 A65_ABSX, 0x7E, END65,
118 A65_IMPL, 0x40, END65,
119 A65_IMPL, 0x60, END65,
120 A65_IMMED, 0xE9, A65_ABS, 0xED, A65_ZP, 0xE5, A65_INDX, 0xE1, A65_INDY, 0xF1,
121 A65_ZPX, 0xF5, A65_ABSX, 0xFD, A65_ABSY, 0xF9, END65,
122 A65_IMPL, 0x38, END65,
123 A65_IMPL, 0xF8, END65,
124 A65_IMPL, 0x78, END65,
125 A65_ABS, 0x8D, A65_ZP, 0x85, A65_INDX, 0x81, A65_INDY, 0x91, A65_ZPX, 0x95,
126 A65_ABSX, 0x9D, A65_ABSY, 0x99, END65,
127 A65_ABS, 0x8E, A65_ZP, 0x86, A65_ZPY, 0x96, END65,
128 A65_ABS, 0x8C, A65_ZP, 0x84, A65_ZPX, 0x94, END65,
129 A65_IMPL, 0xAA, END65,
130 A65_IMPL, 0xA8, END65,
131 A65_IMPL, 0xBA, END65,
132 A65_IMPL, 0x8A, END65,
133 A65_IMPL, 0x9A, END65,
134 A65_IMPL, 0x98, END65
137 static char ops[NMACHOPS][NMODES]; // Opcodes
138 static unsigned char inf[NMACHOPS][NMODES]; // Construction info
140 // Absolute-to-zeropage translation table
141 static int abs2zp[] =
157 static char a8internal[] =
159 ' ', 0, '!', 1, '"', 2, '#', 3, '$', 4, '%', 5, '&', 6, '\'', 7,
160 '(', 8, ')', 9, '*', 10, '+', 11, ',', 12, '-', 13, '.', 14, '/', 15,
161 '0', 16, '1', 17, '2', 18, '3', 19, '4', 20, '5', 21, '6', 22, '7', 23,
162 '8', 24, '9', 25, ':', 26, ';', 27, '<', 28, '=', 29, '>', 30, '?', 31,
163 '@', 32, 'A', 33, 'B', 34, 'C', 35, 'D', 36, 'E', 37, 'F', 38, 'G', 39,
164 'H', 40, 'I', 41, 'J', 42, 'K', 43, 'L', 44, 'M', 45, 'N', 46, 'O', 47,
165 'P', 48, 'Q', 49, 'R', 50, 'S', 51, 'T', 52, 'U', 53, 'V', 54, 'W', 55,
166 'X', 56, 'Y', 57, 'Z', 58, '[', 59, '\\', 60, ']', 61, '^', 62, '_', 63,
167 'a', 97, 'b', 98, 'c', 99, 'd', 100, 'e', 101, 'f', 102, 'g', 103, 'h', 104,
168 'i', 105, 'j', 106, 'k', 107, 'l', 108, 'm', 109, 'n', 110, 'o', 111, 'p', 112,
169 'q', 113, 'r', 114, 's', 115, 't', 116, 'u', 117, 'v', 118, 'w', 119, 'x', 120,
175 // Initialize 6502 assembler
182 register char * s = imodes;
184 // Set all instruction slots to illegal
185 for(i=0; i<NMACHOPS; i++)
186 for(j=0; j<NMODES; j++)
189 // Uncompress legal instructions into their slots
190 for(i=0; i<NMACHOPS; i++)
198 /* hack A65_REL mode */
201 inf[i][A65_ABS] = A65_REL;
202 ops[i][A65_ABS] = s[1];
203 inf[i][A65_ZP] = A65_REL;
204 ops[i][A65_ZP] = s[1];
207 while (*(s += 2) != (char)END65);
212 // Set up first org section (set to zero)
215 SwitchSection(M6502); // Switch to 6502 section
217 // Initialise string conversion table(s)
218 char * p = a8internal;
219 memset(strtoa8, 31, 128); // 31=fallback value ("?")
221 for(; p<a8internal+sizeof(a8internal); p+=2)
222 strtoa8[p[0]] = p[1];
226 // Allocate and clear 64K of space for the 6502 section
228 memset(sect[M6502].scode->chptr, 0, UPSEG_SIZE);
231 SwitchSection(TEXT); // Go back to TEXT
236 // .6502 --- enter 6502 mode
240 SaveSection(); // Save curent section
241 SwitchSection(M6502); // Switch to 6502 section
242 regbase = reg65base; // Update register DFA tables
244 regcheck = reg65check;
245 regaccept = reg65accept;
246 used_architectures |= M6502;
253 // Do 6502 code generation
257 register int amode; // (Parsed) addressing mode
258 uint64_t eval = -1; // Expression value
259 WORD eattr = 0; // Expression attributes
260 int zpreq = 0; // 1 = optimize instr to zero-page form
261 ch_size = 0; // Reset chunk size on every instruction
264 // Parse 6502 addressing mode
288 else if (*tok == '<')
296 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
304 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
316 if (tok[0] != REG65_Y)
324 else if ((tok[0] == ',') && (tok[1] == REG65_X) && (tok[2] == ')'))
335 // I'm guessing that the form of this is @<expr>(X) or @<expr>(Y), which
336 // I've *never* seen before. :-/
340 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
347 if ((tok[1] != ')') || (tok[2] != EOL))
350 if (tok[0] == REG65_X)
352 else if (tok[0] == REG65_Y)
358 zpreq = 1; // Request zeropage optimization
360 else if (*tok == EOL)
371 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
374 zpreq = 1; // Request zeropage optimization
378 else if (tok[0] == ',')
382 if (tok[0] == REG65_X)
387 else if (tok[0] == REG65_Y)
399 error("bad 6502 addressing mode");
404 // Optimize ABS modes to zero-page when possible
405 // o ZPX or ZPY is illegal, or
406 // o expr is zeropage && zeropageRequest && expression is defined
408 if ((inf[op][amode] == ILLEGAL) // If current op is illegal OR
409 || (zpreq // amode requested a zero-page optimize
410 && (eval < 0x100) // and expr is zero-page
411 && (eattr & DEFINED))) // and the expression is defined
413 int i = abs2zp[amode]; // Get zero-page translation of amode
415 DEBUG printf(" OPT: op=%d amode=%d i=%d inf[op][i]=%d\n",
416 op, amode, i, inf[op][i]);
418 if (i >= 0 && (inf[op][i] & 0xFF) != ILLEGAL) // & use it if it's legal
423 DEBUG printf("6502: op=%d amode=%d ", op, amode);
424 DEBUG printf("inf[op][amode]=%d\n", (int)inf[op][amode]);
429 switch (inf[op][amode])
431 case A65_IMPL: // Just leave the instruction
432 D_byte(ops[op][amode]);
436 D_byte(ops[op][amode]);
438 if (!(eattr & DEFINED))
440 AddFixup(FU_BYTEH, sloc, exprbuf);
444 eval = (eval >> 8) & 0xFF; // Bring high byte to low
445 D_byte(eval); // Deposit byte following instr
449 D_byte(ops[op][amode]);
451 if (!(eattr & DEFINED))
453 AddFixup(FU_BYTEL, sloc, exprbuf);
457 eval = eval & 0xFF; // Mask high byte
458 D_byte(eval); // Deposit byte following instr
467 D_byte(ops[op][amode]);
469 if (!(eattr & DEFINED))
471 AddFixup(FU_BYTE, sloc, exprbuf);
474 else if (eval + 0x100 >= 0x200)
480 D_byte(eval); // Deposit byte following instr
484 D_byte(ops[op][amode]);
490 if (eval + 0x80 >= 0x100)
500 AddFixup(FU_6BRA, sloc, exprbuf);
510 D_byte(ops[op][amode]);
512 if (!(eattr & DEFINED))
514 AddFixup(FU_WORD, sloc, exprbuf);
522 // Deposit 3 NOPs for illegal things (why 3? why not 30? or zero?)
530 error("illegal 6502 addressing mode");
533 // Check for overflow of code region
535 fatal("6502 code pointer > 64K");
542 // Generate 6502 object output file.
543 // ggn: Converted to COM/EXE/XEX output format
545 void m6502obj(int ofd)
549 CHUNK * ch = sect[M6502].scode;
551 // If no 6502 code was generated, bail out
552 if ((ch == NULL) || (ch->challoc == 0))
555 register uint8_t * p = ch->chptr;
557 // Write out mandatory $FFFF header
558 header[0] = header[1] = 0xFF;
559 uint32_t unused = write(ofd, header, 2);
561 for(uint16_t * l=&orgmap[0][0]; l<currentorg; l+=2)
563 SETLE16(header, 0, l[0]);
564 SETLE16(header, 2, l[1] - 1);
566 // Write header for segment
567 unused = write(ofd, header, 4);
568 // Write the segment data
569 unused = write(ofd, p + l[0], l[1] - l[0]);
574 // Write raw 6502 org'd code.
575 // Super copypasta'd from above function
576 void m6502raw(int ofd)
578 CHUNK * ch = sect[M6502].scode;
580 // If no 6502 code was generated, bail out
581 if ((ch == NULL) || (ch->challoc == 0))
584 register uint8_t *p = ch->chptr;
586 for(uint16_t * l=&orgmap[0][0]; l<currentorg; l+=2)
588 // Write the segment data
589 uint32_t unused = write(ofd, p + l[0], l[1] - l[0]);
595 // Generate a C64 .PRG output file
597 void m6502c64(int ofd)
601 CHUNK * ch = sect[M6502].scode;
603 // If no 6502 code was generated, bail out
604 if ((ch == NULL) || (ch->challoc == 0))
607 if (currentorg != &orgmap[1][0])
609 // More than one 6502 section created, this is not allowed
610 error("when generating C64 .PRG files only one org section is allowed - aborting");
614 SETLE16(header, 0, orgmap[0][0]);
615 register uint8_t * p = ch->chptr;
618 uint32_t unused = write(ofd, header, 2);
620 unused = write(ofd, p + orgmap[0][0], orgmap[0][1] - orgmap[0][0]);