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;
252 // Do 6502 code generation
256 register int amode; // (Parsed) addressing mode
257 uint64_t eval = -1; // Expression value
258 WORD eattr = 0; // Expression attributes
259 int zpreq = 0; // 1 = optimize instr to zero-page form
260 ch_size = 0; // Reset chunk size on every instruction
263 // Parse 6502 addressing mode
287 else if (*tok == '<')
295 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
303 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
315 if (tok[0] != REG65_Y)
323 else if ((tok[0] == ',') && (tok[1] == REG65_X) && (tok[2] == ')'))
334 // I'm guessing that the form of this is @<expr>(X) or @<expr>(Y), which
335 // I've *never* seen before. :-/
339 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
346 if ((tok[1] != ')') || (tok[2] != EOL))
349 if (tok[0] == REG65_X)
351 else if (tok[0] == REG65_Y)
357 zpreq = 1; // Request zeropage optimization
359 else if (*tok == EOL)
370 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
373 zpreq = 1; // Request zeropage optimization
377 else if (tok[0] == ',')
381 if (tok[0] == REG65_X)
386 else if (tok[0] == REG65_Y)
398 error("bad 6502 addressing mode");
403 // Optimize ABS modes to zero-page when possible
404 // o ZPX or ZPY is illegal, or
405 // o expr is zeropage && zeropageRequest && expression is defined
407 if ((inf[op][amode] == ILLEGAL) // If current op is illegal OR
408 || (zpreq // amode requested a zero-page optimize
409 && (eval < 0x100) // and expr is zero-page
410 && (eattr & DEFINED))) // and the expression is defined
412 int i = abs2zp[amode]; // Get zero-page translation of amode
414 DEBUG printf(" OPT: op=%d amode=%d i=%d inf[op][i]=%d\n",
415 op, amode, i, inf[op][i]);
417 if (i >= 0 && (inf[op][i] & 0xFF) != ILLEGAL) // & use it if it's legal
422 DEBUG printf("6502: op=%d amode=%d ", op, amode);
423 DEBUG printf("inf[op][amode]=%d\n", (int)inf[op][amode]);
428 switch (inf[op][amode])
430 case A65_IMPL: // Just leave the instruction
431 D_byte(ops[op][amode]);
435 D_byte(ops[op][amode]);
437 if (!(eattr & DEFINED))
439 AddFixup(FU_BYTEH, sloc, exprbuf);
443 eval = (eval >> 8) & 0xFF; // Bring high byte to low
444 D_byte(eval); // Deposit byte following instr
448 D_byte(ops[op][amode]);
450 if (!(eattr & DEFINED))
452 AddFixup(FU_BYTEL, sloc, exprbuf);
456 eval = eval & 0xFF; // Mask high byte
457 D_byte(eval); // Deposit byte following instr
466 D_byte(ops[op][amode]);
468 if (!(eattr & DEFINED))
470 AddFixup(FU_BYTE, sloc, exprbuf);
473 else if (eval + 0x100 >= 0x200)
479 D_byte(eval); // Deposit byte following instr
483 D_byte(ops[op][amode]);
489 if (eval + 0x80 >= 0x100)
499 AddFixup(FU_6BRA, sloc, exprbuf);
509 D_byte(ops[op][amode]);
511 if (!(eattr & DEFINED))
513 AddFixup(FU_WORD, sloc, exprbuf);
521 // Deposit 3 NOPs for illegal things (why 3? why not 30? or zero?)
529 error("illegal 6502 addressing mode");
532 // Check for overflow of code region
534 fatal("6502 code pointer > 64K");
541 // Generate 6502 object output file.
542 // ggn: Converted to COM/EXE/XEX output format
544 void m6502obj(int ofd)
548 CHUNK * ch = sect[M6502].scode;
550 // If no 6502 code was generated, bail out
551 if ((ch == NULL) || (ch->challoc == 0))
554 register uint8_t * p = ch->chptr;
556 // Write out mandatory $FFFF header
557 header[0] = header[1] = 0xFF;
558 uint32_t unused = write(ofd, header, 2);
560 for(uint16_t * l=&orgmap[0][0]; l<currentorg; l+=2)
562 SETLE16(header, 0, l[0]);
563 SETLE16(header, 2, l[1] - 1);
565 // Write header for segment
566 unused = write(ofd, header, 4);
567 // Write the segment data
568 unused = write(ofd, p + l[0], l[1] - l[0]);
574 // Generate a C64 .PRG output file
576 void m6502c64(int ofd)
580 CHUNK * ch = sect[M6502].scode;
582 // If no 6502 code was generated, bail out
583 if ((ch == NULL) || (ch->challoc == 0))
586 if (currentorg != &orgmap[1][0])
588 // More than one 6502 section created, this is not allowed
589 error("when generating C64 .PRG files only one org section is allowed - aborting");
593 SETLE16(header, 0, orgmap[0][0]);
594 register uint8_t * p = ch->chptr;
597 uint32_t unused = write(ofd, header, 2);
599 unused = write(ofd, p + orgmap[0][0], orgmap[0][1] - orgmap[0][0]);