4 // Init6502 initialization
5 // d_6502 handle ".6502" directive
6 // m6502cg generate code for a 6502 mnemonic
7 // d_org handle 6502 section's ".org" directive
8 // m6502obj generate 6502 object file
20 #define UPSEG_SIZE 0x10010L // size of 6502 code buffer, 64K+16bytes
23 static uint16_t orgmap[1024][2]; // Mark all 6502 org changes
26 const char in_6502mode[] = "directive illegal in .6502 section";
27 uint16_t * currentorg = &orgmap[0][0]; // Current org range
30 // 6502 addressing modes;
31 // DO NOT CHANGE THESE VALUES.
48 #define NMACHOPS 56 // Number of machine ops
49 #define NMODES 14 // Number of addressing modes
50 #define NOP 0xEA // 6502 NOP instruction
51 #define ILLEGAL 0xFF // 'Illegal instr' marker
52 #define END65 0xFF // End-of-an-instr-list
54 static char imodes[] =
56 A65_IMMED, 0x69, A65_ABS, 0x6d, A65_ZP, 0x65, A65_INDX, 0x61, A65_INDY, 0x71,
57 A65_ZPX, 0x75, A65_ABSX, 0x7d, A65_ABSY, 0x79, END65,
58 A65_IMMED, 0x29, A65_ABS, 0x2d, A65_ZP, 0x25, A65_INDX, 0x21, A65_INDY, 0x31,
59 A65_ZPX, 0x35, A65_ABSX, 0x3d, A65_ABSY, 0x39, END65,
60 A65_ABS, 0x0e, A65_ZP, 0x06, A65_IMPL, 0x0a, A65_ZPX, 0x16, A65_ABSX,
70 A65_ABS, 0x2c, A65_ZP, 0x24, END65,
71 A65_IMPL, 0x00, END65,
72 A65_IMPL, 0x18, END65,
73 A65_IMPL, 0xd8, END65,
74 A65_IMPL, 0x58, END65,
75 A65_IMPL, 0xb8, END65,
76 A65_IMMED, 0xc9, A65_ABS, 0xcd, A65_ZP, 0xc5, A65_INDX, 0xc1, A65_INDY, 0xd1,
77 A65_ZPX, 0xd5, A65_ABSX, 0xdd, A65_ABSY, 0xd9, END65,
78 A65_IMMED, 0xe0, A65_ABS, 0xec, A65_ZP, 0xe4, END65,
79 A65_IMMED, 0xc0, A65_ABS, 0xcc, A65_ZP, 0xc4, END65,
80 A65_ABS, 0xce, A65_ZP, 0xc6, A65_ZPX, 0xd6, A65_ABSX, 0xde, END65,
81 A65_IMPL, 0xca, END65,
82 A65_IMPL, 0x88, END65,
83 A65_IMMED, 0x49, A65_ABS, 0x4d, A65_ZP, 0x45, A65_INDX, 0x41, A65_INDY, 0x51,
84 A65_ZPX, 0x55, A65_ABSX, 0x5d, A65_ABSY, 0x59, END65,
85 A65_ABS, 0xee, A65_ZP, 0xe6, A65_ZPX, 0xf6, A65_ABSX, 0xfe, END65,
86 A65_IMPL, 0xe8, END65,
87 A65_IMPL, 0xc8, END65,
88 A65_ABS, 0x4c, A65_IND, 0x6c, END65,
90 A65_IMMED, 0xa9, A65_ABS, 0xad, A65_ZP, 0xa5, A65_INDX, 0xa1, A65_INDY, 0xb1,
91 A65_ZPX, 0xb5, A65_ABSX, 0xbd, A65_ABSY, 0xb9, A65_IMMEDH, 0xa9, A65_IMMEDL, 0xa9, END65,
92 A65_IMMED, 0xa2, A65_ABS, 0xae, A65_ZP, 0xa6, A65_ABSY, 0xbe,
93 A65_ZPY, 0xb6, A65_IMMEDH, 0xa2, A65_IMMEDL, 0xa2, END65,
94 A65_IMMED, 0xa0, A65_ABS, 0xac, A65_ZP, 0xa4, A65_ZPX, 0xb4,
95 A65_ABSX, 0xbc, A65_IMMEDH, 0xa0, A65_IMMEDL, 0xa0, END65,
96 A65_ABS, 0x4e, A65_ZP, 0x46, A65_IMPL, 0x4a, A65_ZPX, 0x56,
97 A65_ABSX, 0x5e, END65,
98 A65_IMPL, 0xea, END65,
99 A65_IMMED, 0x09, A65_ABS, 0x0d, A65_ZP, 0x05, A65_INDX, 0x01, A65_INDY, 0x11,
100 A65_ZPX, 0x15, A65_ABSX, 0x1d, A65_ABSY, 0x19, END65,
101 A65_IMPL, 0x48, END65,
102 A65_IMPL, 0x08, END65,
103 A65_IMPL, 0x68, END65,
104 A65_IMPL, 0x28, END65,
105 A65_ABS, 0x2e, A65_ZP, 0x26, A65_IMPL, 0x2a, A65_ZPX, 0x36,
106 A65_ABSX, 0x3e, END65,
107 A65_ABS, 0x6e, A65_ZP, 0x66, A65_IMPL, 0x6a, A65_ZPX, 0x76,
108 A65_ABSX, 0x7e, END65,
109 A65_IMPL, 0x40, END65,
110 A65_IMPL, 0x60, END65,
111 A65_IMMED, 0xe9, A65_ABS, 0xed, A65_ZP, 0xe5, A65_INDX, 0xe1, A65_INDY, 0xf1,
112 A65_ZPX, 0xf5, A65_ABSX, 0xfd, A65_ABSY, 0xf9, END65,
113 A65_IMPL, 0x38, END65,
114 A65_IMPL, 0xf8, END65,
115 A65_IMPL, 0x78, END65,
116 A65_ABS, 0x8d, A65_ZP, 0x85, A65_INDX, 0x81, A65_INDY, 0x91, A65_ZPX, 0x95,
117 A65_ABSX, 0x9d, A65_ABSY, 0x99, END65,
118 A65_ABS, 0x8e, A65_ZP, 0x86, A65_ZPY, 0x96, END65,
119 A65_ABS, 0x8c, A65_ZP, 0x84, A65_ZPX, 0x94, END65,
120 A65_IMPL, 0xaa, END65,
121 A65_IMPL, 0xa8, END65,
122 A65_IMPL, 0xba, END65,
123 A65_IMPL, 0x8a, END65,
124 A65_IMPL, 0x9a, END65,
125 A65_IMPL, 0x98, END65
128 static char ops[NMACHOPS][NMODES]; // Opcodes
129 static unsigned char inf[NMACHOPS][NMODES]; // Construction info
131 // Absolute-to-zeropage translation table
132 static int abs2zp[] =
150 // Initialize 6502 assembler
157 register char * s = imodes;
159 // Set all instruction slots to illegal
160 for(i=0; i<NMACHOPS; i++)
161 for(j=0; j<NMODES; j++)
164 // Uncompress legal instructions into their slots
165 for(i=0; i<NMACHOPS; i++)
173 /* hack A65_REL mode */
176 inf[i][A65_ABS] = A65_REL;
177 ops[i][A65_ABS] = s[1];
178 inf[i][A65_ZP] = A65_REL;
179 ops[i][A65_ZP] = s[1];
182 while (*(s += 2) != (char)END65);
187 // Set up first org section (set to zero)
193 // .6502 --- enter 6502 mode
197 SaveSection(); // Save curent section
198 SwitchSection(M6502); // Switch to 6502 section
202 // Allocate and clear 64K of space for the 6502 section
204 memset(sect[M6502].scode->chptr, 0, UPSEG_SIZE);
212 // Do 6502 code generation
216 register int amode; // (Parsed) addressing mode
218 VALUE eval; // Expression value
219 WORD eattr; // Expression attributes
220 int zpreq; // 1, optimize instr to zero-page form
221 register char * p; // (Temp) string usage
222 ch_size = 0; // Reset chunk size on every instruction
225 // Parse 6502 addressing mode
241 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
247 else if (*tok == '<')
250 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
257 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
266 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
278 if (*tok != SYMBOL || p[1] != EOS || (*p | 0x20) != 'y') // Sleazo tolower()
287 else if (*tok == ',')
293 if (*tok != SYMBOL || p[1] != EOS || (*p | 0x20) != 'x') // Sleazo tolower()
311 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
319 if (*tok != SYMBOL || p[1] != EOS || tok[2] != ')' || tok[3] != EOL)
322 i = (*p | 0x20); // Sleazo tolower()
331 tok += 3; // Past SYMBOL <string> ')' EOL
332 zpreq = 1; // Request zeropage optimization
334 else if (*tok == EOL)
348 // ggn: the following code is effectively disabled as it would make
349 // single letter labels not work correctly (would not identify the
350 // label properly). And from what I understand it's something to
351 // keep compatibility with the coinop assembler which is probably
352 // something we don't care about much :D
354 if (*tok == SYMBOL && p[1] == EOS && tok[2] == ',')
356 tok += 3; // Past: SYMBOL <string> ','
366 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
378 if (expr(exprbuf, &eval, &eattr, NULL) < 0)
385 else if (*tok == ',')
390 if (*tok != SYMBOL || p[1] != EOS)
396 // Check for X or Y index register;
397 // the OR with 0x20 is a sleazo conversion
398 // to lower-case that actually works.
400 i = *p | 0x20; // Oooh, this is slimey (but fast!)
415 error("bad 6502 addressing mode");
420 // Optimize ABS modes to zero-page when possible
421 // o ZPX or ZPY is illegal, or
422 // o expr is zeropage && zeropageRequest && expression is defined
424 if (inf[op][amode] == ILLEGAL // If current op is illegal,
425 || (eval < 0x100 // or expr must be zero-page
426 && zpreq != 0 // amode must request zero-page opt.
427 && (eattr & DEFINED))) // and the expression must be defined
429 i = abs2zp[amode]; // i = zero-page translation of amode
431 DEBUG printf(" OPT: op=%d amode=%d i=%d inf[op][i]=%d\n",
432 op, amode, i, inf[op][i]);
434 if (i >= 0 && (inf[op][i] & 0xFF) != ILLEGAL) // Use it if it's legal
439 DEBUG printf("6502: op=%d amode=%d ", op, amode);
440 DEBUG printf("inf[op][amode]=%d\n", (int)inf[op][amode]);
443 switch (inf[op][amode])
445 case A65_IMPL: // Just leave the instruction
446 D_byte(ops[op][amode]);
450 D_byte(ops[op][amode]);
452 if (!(eattr & DEFINED))
454 AddFixup(FU_BYTEH, sloc, exprbuf);
458 eval = (eval >> 8) & 0xFF; // Bring high byte to low
459 D_byte(eval); // Deposit byte following instr
463 D_byte(ops[op][amode]);
465 if (!(eattr & DEFINED))
467 AddFixup(FU_BYTEL, sloc, exprbuf);
471 eval = eval & 0xFF; // Mask high byte
472 D_byte(eval); // Deposit byte following instr
481 D_byte(ops[op][amode]);
483 if (!(eattr & DEFINED))
485 AddFixup(FU_BYTE, sloc, exprbuf);
488 else if (eval + 0x100 >= 0x200)
494 D_byte(eval); // Deposit byte following instr
498 D_byte(ops[op][amode]);
504 if (eval + 0x80 >= 0x100)
514 AddFixup(FU_6BRA, sloc, exprbuf);
524 D_byte(ops[op][amode]);
526 if (!(eattr & DEFINED))
528 AddFixup(FU_WORD, sloc, exprbuf);
536 // Deposit 3 NOPs for illegal things
543 error("illegal 6502 addressing mode");
546 // Check for overflow of code region
548 fatal("6502 code pointer > 64K");
556 // Generate 6502 object output file.
558 // ggn: converted into a com/exe/xex output format
559 // Notes: 1. The $FFFF is only mandatory for the first segment, but let's
560 // dump it everywhere for now
561 // 2. It's still dumping pages instead of more fine grained stuff.
562 // Should look into this - a8 people don't like waste so much ;)
563 void m6502obj(int ofd)
565 uint16_t exeheader[3];
567 uint16_t * headpoint = exeheader;
569 CHUNK * ch = sect[M6502].scode;
571 // If no 6502 code was generated, forget it
572 if ((ch == NULL) || (ch->challoc == 0))
575 exeheader[0] = 0xFFFF; // Mandatory for first segment
576 register uint8_t * p = ch->chptr;
578 for(uint16_t * l=&orgmap[0][0]; l<currentorg; l+=2)
581 exeheader[2] = l[1] - 1;
584 size_t unused = write(ofd, headpoint, headsize);
585 unused = write(ofd, p + l[0], l[1] - l[0]);
587 // Skip the $FFFF after first segment, it's not mandatory
588 headpoint = &exeheader[1];