2 // RMAC - Reboot's Macro Assembler for all Atari computers
3 // 6502.C - 6502 Assembler
4 // Copyright (C) 199x Landon Dyer, 2011-2018 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 == ',')
349 // Sleazo tolower() -----------------vvvvvvvvvvv
350 if (*tok != SYMBOL || p[1] != EOS || (*p | 0x20) != 'x')
368 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
376 if (*tok != SYMBOL || p[1] != EOS || tok[2] != ')' || tok[3] != EOL)
379 i = (*p | 0x20); // Sleazo tolower()
388 tok += 3; // Past SYMBOL <string> ')' EOL
389 zpreq = 1; // Request zeropage optimization
391 else if (*tok == EOL)
405 // ggn: the following code is effectively disabled as it would make
406 // single letter labels not work correctly (would not identify the
407 // label properly). And from what I understand it's something to
408 // keep compatibility with the coinop assembler which is probably
409 // something we don't care about much :D
411 if (*tok == SYMBOL && p[1] == EOS && tok[2] == ',')
413 tok += 3; // Past: SYMBOL <string> ','
423 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
435 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
442 else if (*tok == ',')
448 if (*tok != SYMBOL || p[1] != EOS)
454 // Check for X or Y index register;
455 // the OR with 0x20 is a sleazo conversion
456 // to lower-case that actually works.
458 i = *p | 0x20; // Oooh, this is slimey (but fast!)
472 else if (tok[0] == KW_Y)
488 error("bad 6502 addressing mode");
493 // Optimize ABS modes to zero-page when possible
494 // o ZPX or ZPY is illegal, or
495 // o expr is zeropage && zeropageRequest && expression is defined
497 if (inf[op][amode] == ILLEGAL // If current op is illegal,
498 || (eval < 0x100 // or expr must be zero-page
499 && zpreq != 0 // amode must request zero-page opt.
500 && (eattr & DEFINED))) // and the expression must be defined
502 i = abs2zp[amode]; // i = zero-page translation of amode
504 DEBUG printf(" OPT: op=%d amode=%d i=%d inf[op][i]=%d\n",
505 op, amode, i, inf[op][i]);
507 if (i >= 0 && (inf[op][i] & 0xFF) != ILLEGAL) // Use it if it's legal
512 DEBUG printf("6502: op=%d amode=%d ", op, amode);
513 DEBUG printf("inf[op][amode]=%d\n", (int)inf[op][amode]);
516 switch (inf[op][amode])
518 case A65_IMPL: // Just leave the instruction
519 D_byte(ops[op][amode]);
523 D_byte(ops[op][amode]);
525 if (!(eattr & DEFINED))
527 AddFixup(FU_BYTEH, sloc, exprbuf);
531 eval = (eval >> 8) & 0xFF; // Bring high byte to low
532 D_byte(eval); // Deposit byte following instr
536 D_byte(ops[op][amode]);
538 if (!(eattr & DEFINED))
540 AddFixup(FU_BYTEL, sloc, exprbuf);
544 eval = eval & 0xFF; // Mask high byte
545 D_byte(eval); // Deposit byte following instr
554 D_byte(ops[op][amode]);
556 if (!(eattr & DEFINED))
558 AddFixup(FU_BYTE, sloc, exprbuf);
561 else if (eval + 0x100 >= 0x200)
567 D_byte(eval); // Deposit byte following instr
571 D_byte(ops[op][amode]);
577 if (eval + 0x80 >= 0x100)
587 AddFixup(FU_6BRA, sloc, exprbuf);
597 D_byte(ops[op][amode]);
599 if (!(eattr & DEFINED))
601 AddFixup(FU_WORD, sloc, exprbuf);
609 // Deposit 3 NOPs for illegal things (why 3? why not 30? or zero?)
616 error("illegal 6502 addressing mode");
619 // Check for overflow of code region
621 fatal("6502 code pointer > 64K");
623 //Now why use this instead of at_eol()?
630 // Generate 6502 object output file.
632 // ggn: converted into a com/exe/xex output format
633 void m6502obj(int ofd)
635 uint16_t exeheader[3];
637 uint16_t * headpoint = exeheader;
639 CHUNK * ch = sect[M6502].scode;
641 // If no 6502 code was generated, forget it
642 if ((ch == NULL) || (ch->challoc == 0))
645 exeheader[0] = 0xFFFF; // Mandatory for first segment
646 register uint8_t * p = ch->chptr;
648 for(uint16_t * l=&orgmap[0][0]; l<currentorg; l+=2)
651 Why are we assuming endianness here? This is retarded
654 exeheader[2] = l[1] - 1;
657 size_t unused = write(ofd, headpoint, headsize);
658 unused = write(ofd, p + l[0], l[1] - l[0]);
660 // Skip the $FFFF after first segment, it's not mandatory
661 headpoint = &exeheader[1];