2 // RMAC - Reboot's Macro Assembler for all Atari computers
3 // 6502.C - 6502 Assembler
4 // Copyright (C) 199x Landon Dyer, 2011-2017 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 uint32_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
273 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
279 else if (*tok == '<')
282 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
289 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
298 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
310 if (*tok != SYMBOL || p[1] != EOS || (*p | 0x20) != 'y') // Sleazo tolower()
319 else if (*tok == ',')
325 if (*tok != SYMBOL || p[1] != EOS || (*p | 0x20) != 'x') // Sleazo tolower()
343 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
351 if (*tok != SYMBOL || p[1] != EOS || tok[2] != ')' || tok[3] != EOL)
354 i = (*p | 0x20); // Sleazo tolower()
363 tok += 3; // Past SYMBOL <string> ')' EOL
364 zpreq = 1; // Request zeropage optimization
366 else if (*tok == EOL)
380 // ggn: the following code is effectively disabled as it would make
381 // single letter labels not work correctly (would not identify the
382 // label properly). And from what I understand it's something to
383 // keep compatibility with the coinop assembler which is probably
384 // something we don't care about much :D
386 if (*tok == SYMBOL && p[1] == EOS && tok[2] == ',')
388 tok += 3; // Past: SYMBOL <string> ','
398 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
410 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
417 else if (*tok == ',')
422 if (*tok != SYMBOL || p[1] != EOS)
428 // Check for X or Y index register;
429 // the OR with 0x20 is a sleazo conversion
430 // to lower-case that actually works.
432 i = *p | 0x20; // Oooh, this is slimey (but fast!)
447 error("bad 6502 addressing mode");
452 // Optimize ABS modes to zero-page when possible
453 // o ZPX or ZPY is illegal, or
454 // o expr is zeropage && zeropageRequest && expression is defined
456 if (inf[op][amode] == ILLEGAL // If current op is illegal,
457 || (eval < 0x100 // or expr must be zero-page
458 && zpreq != 0 // amode must request zero-page opt.
459 && (eattr & DEFINED))) // and the expression must be defined
461 i = abs2zp[amode]; // i = zero-page translation of amode
463 DEBUG printf(" OPT: op=%d amode=%d i=%d inf[op][i]=%d\n",
464 op, amode, i, inf[op][i]);
466 if (i >= 0 && (inf[op][i] & 0xFF) != ILLEGAL) // Use it if it's legal
471 DEBUG printf("6502: op=%d amode=%d ", op, amode);
472 DEBUG printf("inf[op][amode]=%d\n", (int)inf[op][amode]);
475 switch (inf[op][amode])
477 case A65_IMPL: // Just leave the instruction
478 D_byte(ops[op][amode]);
482 D_byte(ops[op][amode]);
484 if (!(eattr & DEFINED))
486 AddFixup(FU_BYTEH, sloc, exprbuf);
490 eval = (eval >> 8) & 0xFF; // Bring high byte to low
491 D_byte(eval); // Deposit byte following instr
495 D_byte(ops[op][amode]);
497 if (!(eattr & DEFINED))
499 AddFixup(FU_BYTEL, sloc, exprbuf);
503 eval = eval & 0xFF; // Mask high byte
504 D_byte(eval); // Deposit byte following instr
513 D_byte(ops[op][amode]);
515 if (!(eattr & DEFINED))
517 AddFixup(FU_BYTE, sloc, exprbuf);
520 else if (eval + 0x100 >= 0x200)
526 D_byte(eval); // Deposit byte following instr
530 D_byte(ops[op][amode]);
536 if (eval + 0x80 >= 0x100)
546 AddFixup(FU_6BRA, sloc, exprbuf);
556 D_byte(ops[op][amode]);
558 if (!(eattr & DEFINED))
560 AddFixup(FU_WORD, sloc, exprbuf);
568 // Deposit 3 NOPs for illegal things
575 error("illegal 6502 addressing mode");
578 // Check for overflow of code region
580 fatal("6502 code pointer > 64K");
588 // Generate 6502 object output file.
590 // ggn: converted into a com/exe/xex output format
591 void m6502obj(int ofd)
593 uint16_t exeheader[3];
595 uint16_t * headpoint = exeheader;
597 CHUNK * ch = sect[M6502].scode;
599 // If no 6502 code was generated, forget it
600 if ((ch == NULL) || (ch->challoc == 0))
603 exeheader[0] = 0xFFFF; // Mandatory for first segment
604 register uint8_t * p = ch->chptr;
606 for(uint16_t * l=&orgmap[0][0]; l<currentorg; l+=2)
609 exeheader[2] = l[1] - 1;
612 size_t unused = write(ofd, headpoint, headsize);
613 unused = write(ofd, p + l[0], l[1] - l[0]);
615 // Skip the $FFFF after first segment, it's not mandatory
616 headpoint = &exeheader[1];