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
24 #define UPSEG_SIZE 0x10010L // size of 6502 code buffer, 64K+16bytes
27 static uint16_t orgmap[1024][2]; // Mark all 6502 org changes
30 const char in_6502mode[] = "directive illegal in .6502 section";
31 uint16_t * currentorg = &orgmap[0][0]; // Current org range
32 char strtoa8[128]; // ASCII to Atari 800 internal conversion table
35 // 6502 addressing modes;
36 // DO NOT CHANGE THESE VALUES.
53 #define NMACHOPS 56 // Number of machine ops
54 #define NMODES 14 // Number of addressing modes
55 #define NOP 0xEA // 6502 NOP instruction
56 #define ILLEGAL 0xFF // 'Illegal instr' marker
57 #define END65 0xFF // End-of-an-instr-list
59 static char imodes[] =
61 A65_IMMED, 0x69, A65_ABS, 0x6d, A65_ZP, 0x65, A65_INDX, 0x61, A65_INDY, 0x71,
62 A65_ZPX, 0x75, A65_ABSX, 0x7d, A65_ABSY, 0x79, END65,
63 A65_IMMED, 0x29, A65_ABS, 0x2d, A65_ZP, 0x25, A65_INDX, 0x21, A65_INDY, 0x31,
64 A65_ZPX, 0x35, A65_ABSX, 0x3d, A65_ABSY, 0x39, END65,
65 A65_ABS, 0x0e, A65_ZP, 0x06, A65_IMPL, 0x0a, A65_ZPX, 0x16, A65_ABSX,
75 A65_ABS, 0x2c, A65_ZP, 0x24, END65,
76 A65_IMPL, 0x00, END65,
77 A65_IMPL, 0x18, END65,
78 A65_IMPL, 0xd8, END65,
79 A65_IMPL, 0x58, END65,
80 A65_IMPL, 0xb8, END65,
81 A65_IMMED, 0xc9, A65_ABS, 0xcd, A65_ZP, 0xc5, A65_INDX, 0xc1, A65_INDY, 0xd1,
82 A65_ZPX, 0xd5, A65_ABSX, 0xdd, A65_ABSY, 0xd9, END65,
83 A65_IMMED, 0xe0, A65_ABS, 0xec, A65_ZP, 0xe4, END65,
84 A65_IMMED, 0xc0, A65_ABS, 0xcc, A65_ZP, 0xc4, END65,
85 A65_ABS, 0xce, A65_ZP, 0xc6, A65_ZPX, 0xd6, A65_ABSX, 0xde, END65,
86 A65_IMPL, 0xca, END65,
87 A65_IMPL, 0x88, END65,
88 A65_IMMED, 0x49, A65_ABS, 0x4d, A65_ZP, 0x45, A65_INDX, 0x41, A65_INDY, 0x51,
89 A65_ZPX, 0x55, A65_ABSX, 0x5d, A65_ABSY, 0x59, END65,
90 A65_ABS, 0xee, A65_ZP, 0xe6, A65_ZPX, 0xf6, A65_ABSX, 0xfe, END65,
91 A65_IMPL, 0xe8, END65,
92 A65_IMPL, 0xc8, END65,
93 A65_ABS, 0x4c, A65_IND, 0x6c, END65,
95 A65_IMMED, 0xa9, A65_ABS, 0xad, A65_ZP, 0xa5, A65_INDX, 0xa1, A65_INDY, 0xb1,
96 A65_ZPX, 0xb5, A65_ABSX, 0xbd, A65_ABSY, 0xb9, A65_IMMEDH, 0xa9, A65_IMMEDL, 0xa9, END65,
97 A65_IMMED, 0xa2, A65_ABS, 0xae, A65_ZP, 0xa6, A65_ABSY, 0xbe,
98 A65_ZPY, 0xb6, A65_IMMEDH, 0xa2, A65_IMMEDL, 0xa2, END65,
99 A65_IMMED, 0xa0, A65_ABS, 0xac, A65_ZP, 0xa4, A65_ZPX, 0xb4,
100 A65_ABSX, 0xbc, A65_IMMEDH, 0xa0, A65_IMMEDL, 0xa0, END65,
101 A65_ABS, 0x4e, A65_ZP, 0x46, A65_IMPL, 0x4a, A65_ZPX, 0x56,
102 A65_ABSX, 0x5e, END65,
103 A65_IMPL, 0xea, END65,
104 A65_IMMED, 0x09, A65_ABS, 0x0d, A65_ZP, 0x05, A65_INDX, 0x01, A65_INDY, 0x11,
105 A65_ZPX, 0x15, A65_ABSX, 0x1d, A65_ABSY, 0x19, END65,
106 A65_IMPL, 0x48, END65,
107 A65_IMPL, 0x08, END65,
108 A65_IMPL, 0x68, END65,
109 A65_IMPL, 0x28, END65,
110 A65_ABS, 0x2e, A65_ZP, 0x26, A65_IMPL, 0x2a, A65_ZPX, 0x36,
111 A65_ABSX, 0x3e, END65,
112 A65_ABS, 0x6e, A65_ZP, 0x66, A65_IMPL, 0x6a, A65_ZPX, 0x76,
113 A65_ABSX, 0x7e, END65,
114 A65_IMPL, 0x40, END65,
115 A65_IMPL, 0x60, END65,
116 A65_IMMED, 0xe9, A65_ABS, 0xed, A65_ZP, 0xe5, A65_INDX, 0xe1, A65_INDY, 0xf1,
117 A65_ZPX, 0xf5, A65_ABSX, 0xfd, A65_ABSY, 0xf9, END65,
118 A65_IMPL, 0x38, END65,
119 A65_IMPL, 0xf8, END65,
120 A65_IMPL, 0x78, END65,
121 A65_ABS, 0x8d, A65_ZP, 0x85, A65_INDX, 0x81, A65_INDY, 0x91, A65_ZPX, 0x95,
122 A65_ABSX, 0x9d, A65_ABSY, 0x99, END65,
123 A65_ABS, 0x8e, A65_ZP, 0x86, A65_ZPY, 0x96, END65,
124 A65_ABS, 0x8c, A65_ZP, 0x84, A65_ZPX, 0x94, END65,
125 A65_IMPL, 0xaa, END65,
126 A65_IMPL, 0xa8, END65,
127 A65_IMPL, 0xba, END65,
128 A65_IMPL, 0x8a, END65,
129 A65_IMPL, 0x9a, END65,
130 A65_IMPL, 0x98, END65
133 static char ops[NMACHOPS][NMODES]; // Opcodes
134 static unsigned char inf[NMACHOPS][NMODES]; // Construction info
136 // Absolute-to-zeropage translation table
137 static int abs2zp[] =
153 static char a8internal[] =
155 ' ', 0, '!', 1, '"', 2, '#', 3, '$', 4, '%', 5, '&', 6, '\'', 7,
156 '(', 8, ')', 9, '*', 10, '+', 11, ',', 12, '-', 13, '.', 14, '/', 15,
157 '0', 16, '1', 17, '2', 18, '3', 19, '4', 20, '5', 21, '6', 22, '7', 23,
158 '8', 24, '9', 25, ':', 26, ';', 27, '<', 28, '=', 29, '>', 30, '?', 31,
159 '@', 32, 'A', 33, 'B', 34, 'C', 35, 'D', 36, 'E', 37, 'F', 38, 'G', 39,
160 'H', 40, 'I', 41, 'J', 42, 'K', 43, 'L', 44, 'M', 45, 'N', 46, 'O', 47,
161 'P', 48, 'Q', 49, 'R', 50, 'S', 51, 'T', 52, 'U', 53, 'V', 54, 'W', 55,
162 'X', 56, 'Y', 57, 'Z', 58, '[', 59, '\\', 60, ']', 61, '^', 62, '_', 63,
163 'a', 97, 'b', 98, 'c', 99, 'd', 100, 'e', 101, 'f', 102, 'g', 103, 'h', 104,
164 'i', 105, 'j', 106, 'k', 107, 'l', 108, 'm', 109, 'n', 110, 'o', 111, 'p', 112,
165 'q', 113, 'r', 114, 's', 115, 't', 116, 'u', 117, 'v', 118, 'w', 119, 'x', 120,
171 // Initialize 6502 assembler
178 register char * s = imodes;
180 // Set all instruction slots to illegal
181 for(i=0; i<NMACHOPS; i++)
182 for(j=0; j<NMODES; j++)
185 // Uncompress legal instructions into their slots
186 for(i=0; i<NMACHOPS; i++)
194 /* hack A65_REL mode */
197 inf[i][A65_ABS] = A65_REL;
198 ops[i][A65_ABS] = s[1];
199 inf[i][A65_ZP] = A65_REL;
200 ops[i][A65_ZP] = s[1];
203 while (*(s += 2) != (char)END65);
208 // Set up first org section (set to zero)
211 SwitchSection(M6502); // Switch to 6502 section
213 // Initialise string conversion table(s)
214 char * p = a8internal;
215 memset(strtoa8, 31, 128); // 31=fallback value ("?")
217 for(; p<a8internal+sizeof(a8internal); p+=2)
218 strtoa8[p[0]] = p[1];
222 // Allocate and clear 64K of space for the 6502 section
224 memset(sect[M6502].scode->chptr, 0, UPSEG_SIZE);
227 SwitchSection(TEXT); // Go back to TEXT
232 // .6502 --- enter 6502 mode
236 SaveSection(); // Save curent section
237 SwitchSection(M6502); // Switch to 6502 section
244 // Do 6502 code generation
248 register int amode; // (Parsed) addressing mode
250 uint64_t eval; // Expression value
251 WORD eattr; // Expression attributes
252 int zpreq; // 1, optimize instr to zero-page form
253 register char * p; // (Temp) string usage
254 ch_size = 0; // Reset chunk size on every instruction
257 // Parse 6502 addressing mode
274 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
280 else if (*tok == '<')
284 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
291 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
300 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
312 // Sleazo tolower() -----------------vvvvvvvvvvv
313 if (*tok != SYMBOL || p[1] != EOS || (*p | 0x20) != 'y')
322 else if (*tok == ',')
328 // Sleazo tolower() -----------------vvvvvvvvvvv
329 if (*tok != SYMBOL || p[1] != EOS || (*p | 0x20) != 'x')
347 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
355 if (*tok != SYMBOL || p[1] != EOS || tok[2] != ')' || tok[3] != EOL)
358 i = (*p | 0x20); // Sleazo tolower()
367 tok += 3; // Past SYMBOL <string> ')' EOL
368 zpreq = 1; // Request zeropage optimization
370 else if (*tok == EOL)
384 // ggn: the following code is effectively disabled as it would make
385 // single letter labels not work correctly (would not identify the
386 // label properly). And from what I understand it's something to
387 // keep compatibility with the coinop assembler which is probably
388 // something we don't care about much :D
390 if (*tok == SYMBOL && p[1] == EOS && tok[2] == ',')
392 tok += 3; // Past: SYMBOL <string> ','
402 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
414 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
421 else if (*tok == ',')
426 if (*tok != SYMBOL || p[1] != EOS)
432 // Check for X or Y index register;
433 // the OR with 0x20 is a sleazo conversion
434 // to lower-case that actually works.
436 i = *p | 0x20; // Oooh, this is slimey (but fast!)
451 error("bad 6502 addressing mode");
456 // Optimize ABS modes to zero-page when possible
457 // o ZPX or ZPY is illegal, or
458 // o expr is zeropage && zeropageRequest && expression is defined
460 if (inf[op][amode] == ILLEGAL // If current op is illegal,
461 || (eval < 0x100 // or expr must be zero-page
462 && zpreq != 0 // amode must request zero-page opt.
463 && (eattr & DEFINED))) // and the expression must be defined
465 i = abs2zp[amode]; // i = zero-page translation of amode
467 DEBUG printf(" OPT: op=%d amode=%d i=%d inf[op][i]=%d\n",
468 op, amode, i, inf[op][i]);
470 if (i >= 0 && (inf[op][i] & 0xFF) != ILLEGAL) // Use it if it's legal
475 DEBUG printf("6502: op=%d amode=%d ", op, amode);
476 DEBUG printf("inf[op][amode]=%d\n", (int)inf[op][amode]);
479 switch (inf[op][amode])
481 case A65_IMPL: // Just leave the instruction
482 D_byte(ops[op][amode]);
486 D_byte(ops[op][amode]);
488 if (!(eattr & DEFINED))
490 AddFixup(FU_BYTEH, sloc, exprbuf);
494 eval = (eval >> 8) & 0xFF; // Bring high byte to low
495 D_byte(eval); // Deposit byte following instr
499 D_byte(ops[op][amode]);
501 if (!(eattr & DEFINED))
503 AddFixup(FU_BYTEL, sloc, exprbuf);
507 eval = eval & 0xFF; // Mask high byte
508 D_byte(eval); // Deposit byte following instr
517 D_byte(ops[op][amode]);
519 if (!(eattr & DEFINED))
521 AddFixup(FU_BYTE, sloc, exprbuf);
524 else if (eval + 0x100 >= 0x200)
530 D_byte(eval); // Deposit byte following instr
534 D_byte(ops[op][amode]);
540 if (eval + 0x80 >= 0x100)
550 AddFixup(FU_6BRA, sloc, exprbuf);
560 D_byte(ops[op][amode]);
562 if (!(eattr & DEFINED))
564 AddFixup(FU_WORD, sloc, exprbuf);
572 // Deposit 3 NOPs for illegal things
579 error("illegal 6502 addressing mode");
582 // Check for overflow of code region
584 fatal("6502 code pointer > 64K");
592 // Generate 6502 object output file.
594 // ggn: converted into a com/exe/xex output format
595 void m6502obj(int ofd)
597 uint16_t exeheader[3];
599 uint16_t * headpoint = exeheader;
601 CHUNK * ch = sect[M6502].scode;
603 // If no 6502 code was generated, forget it
604 if ((ch == NULL) || (ch->challoc == 0))
607 exeheader[0] = 0xFFFF; // Mandatory for first segment
608 register uint8_t * p = ch->chptr;
610 for(uint16_t * l=&orgmap[0][0]; l<currentorg; l+=2)
613 exeheader[2] = l[1] - 1;
616 size_t unused = write(ofd, headpoint, headsize);
617 unused = write(ofd, p + l[0], l[1] - l[0]);
619 // Skip the $FFFF after first segment, it's not mandatory
620 headpoint = &exeheader[1];