diff --git a/xa/Makefile b/xa/Makefile index fb0204f..f18a069 100644 --- a/xa/Makefile +++ b/xa/Makefile @@ -5,8 +5,8 @@ CC = gcc LD = gcc # for testing. not to be used; build failures in misc/. #CFLAGS = -O2 -W -Wall -pedantic -ansi -#CFLAGS = -O2 -g -CFLAGS = -O2 +CFLAGS = -g +#CFLAGS = -O2 LDFLAGS = -lc # for DOS? diff --git a/xa/misc/ldo65.c b/xa/misc/ldo65.c index 32ae45b..9c049c9 100644 --- a/xa/misc/ldo65.c +++ b/xa/misc/ldo65.c @@ -67,12 +67,21 @@ typedef struct { size_t fsize; /* length of file */ unsigned char *buf; /* file content */ + int mode; /* mode value */ + int align; /* align value */ + int tbase; /* header: text base */ int tlen; /* text length */ + int talign; /* insert to get correct alignment */ + int dbase; /* data base */ int dlen; /* data length */ + int dalign; /* insert to get correct alignment */ + int bbase; /* bss base */ int blen; /* bss length */ + int balign; /* insert to get correct alignment */ + int zbase; /* zero base */ int zlen; /* zero length */ @@ -150,6 +159,7 @@ int main(int argc, char *argv[]) { int noglob=0; int undefok=0; int i = 1; + // default output segment addresses, overwritten by cmdline params int tbase = 0x0400, dbase = 0x1000, bbase = 0x4000, zbase = 0x0002; int ttlen, tdlen, tblen, tzlen, routtlen, routdlen, tro, dro; int lasttaddr, lastdaddr; @@ -158,8 +168,11 @@ int main(int argc, char *argv[]) { int j, jm; file65 *file, **fp = NULL; FILE *fd; - int nundef = 0; // counter/index in list of remaining undef'd labels - + int nundef = 0; // counter/index in list of remaining undef'd labels + int maxalign; // maximum alignment over all files + char *alignfname; // (first) file that requires that alignment + int trgmode = 0; // mode for the output file + char alignbuf[256]; // to efficiently write align fillers char *arg; char **defined = NULL; @@ -282,6 +295,146 @@ int main(int argc, char *argv[]) { i++; } + // ------------------------------------------------------------------------- + // compute target file mode value + + // compute target mode and max align value + trgmode = 0; + maxalign = 0; + alignfname = ""; + { + int er = 0; + int trgcpu = 0; + const char *modenames[16] = { + "documented 6502", + "65C02 (CMOS with BBR/BBS/RMB/SMB)", + "65SC02 (CMOS without BBR/BBS/RMB/SMB)", + "65CE02", + "6502 with undocumented opcodes", + "65816 in 6502 emulation mode", + "n/a", "n/a" + "6809?", "n/a", // 1000 - + "Z80?", "n/a", "n/a", // 1010 - + "8086?", // 1101 - + "80286?", // 1110 - + "n/a" + }; + for(i=0;ialign > maxalign) { + maxalign = file->align; + alignfname = file->fname; + } + if (file->mode & 0x8000) { + // 65816 + trgmode |= 0x8000; + if (trgcpu == 4) { + fprintf(stderr, "Error: file '%s' in 65816 CPU mode is incompatible with previous NMOS undocumented opcodes CPU mode\n", + file->fname); + er = 1; + } + } + if (file->mode & 0x0200) { + // zero out bss + trgmode |= 0x0200; + } + // CPU bits + fcpu = (file->mode & 0x00f0) >> 4; + switch (fcpu) { + case 0x0: // bare minimum documented 6502 is just fine + break; + + case 0x1: // 65C02 - CMOS with BBR/BBS/RMB/SMB, incompatible with 65816 + case 0x3: // 65CE02 - CMOS with BBR/... and add'l opcodes, incompatible w/ 65816 + if (trgmode & 0x8000 || trgcpu == 5) { + fprintf(stderr, "Error: file '%s' in CPU mode %d (%s) " + "is incompatible with previous 65816 CPU mode\n", + file->fname, fcpu, modenames[fcpu]); + er = 1; + } + // fall-through + case 0x2: // 65SC02 - CMOS without BBR/BBS/RMB/SMB, compatible with 65816 + case 0x5: // 65816 in 6502 emulation mode + if (trgcpu == 4) { + // is incompatible with nmos6502 with undocumented opcodes + fprintf(stderr, "Error: file '%s' in CPU mode %d (%s) " + "is incompatible with previous files with mode %d (%s)\n", + file->fname, fcpu, modenames[fcpu], trgcpu, modenames[trgcpu]); + er = 1; + } + break; + + case 0x4: // NMOS 6502 with undocumented opcodes + if (trgcpu == 1 || trgcpu == 2 || trgcpu == 3 || trgcpu == 5) { + // is incompatible with nmos6502 with undocumented opcodes + fprintf(stderr, "Error: file '%s' in CPU mode %d (%s) is " + "incompatible with previous files with mode %d (%s)\n", + file->fname, fcpu, modenames[fcpu], trgcpu, modenames[trgcpu]); + er = 1; + } + if (trgmode & 0x8000) { + fprintf(stderr, "Error: file '%s' in mode %d (%s) is incompatible with previous 65816 CPU mode\n", + file->fname, 4, modenames[4]); + er = 1; + } + break; + default: + if (fcpu > 5) { + printf("Warning: unknown CPU mode %d (%s) detected in file %s\n", + fcpu, modenames[fcpu], file->fname); + } + break; + } + // setting the new mode + switch (fcpu) { + case 0x0: // compatible with everything, no change + break; + case 0x3: // 65CE02 -> supersedes 6502, 65SC02, and 65C02 + if (trgcpu == 1) { + // 65C02 + trgcpu = fcpu; + } + // fall-through + case 0x5: // 65816 in 6502 emu mode, supersedes documented NMOS and 65SC02 + case 0x1: // CMOS w/ BBR.. -> supersedes documented NMOS and 65SC02 + if (trgcpu == 2) { + // 65SC02 + trgcpu = fcpu; + } + // fall-through + case 0x2: // 65SC02 -> supersedes only NMOS 6502 + case 0x4: // NMOS 6502 w/ undocumented opcodes + default: + if (trgcpu == 0) { + // NMOS 6502 + trgcpu = fcpu; + } + break; + } + } + if (er) { + exit(1); + } + } + if (maxalign) { + printf("Info: file %s requires alignment at %d-boundaries\n", alignfname, maxalign + 1); + } + switch (maxalign) { + case 0: + break; + case 1: + trgmode |= 1; + break; + case 3: + trgmode |= 2; + break; + case 255: + trgmode |= 3; + break; + } + + // ------------------------------------------------------------------------- // step 2 - calculate new segment base addresses per file, by // concatenating the segments per type @@ -290,10 +443,45 @@ int main(int argc, char *argv[]) { /* set total length to zero */ ttlen = tdlen = tblen = tzlen = 0; + + // then check start addresses + file = fp[0]; + if (file->align != 0) { + int er = 1; + if (tbase & file->align) { + fprintf(stderr, "Error: text segment start address ($%04x) not aligned as required (at %d bytes)\n", + tbase, file->align + 1); + er = 1; + } + if (dbase & file->align) { + fprintf(stderr, "Error: data segment start address ($%04x) not aligned as required (at %d bytes)\n", + dbase, file->align + 1); + er = 1; + } + if (bbase & file->align) { + fprintf(stderr, "Error: bss segment start address ($%04x) not aligned as required (at %d bytes)\n", + bbase, file->align + 1); + er = 1; + } + if (er) { + exit(1); + } + } + /* find new addresses for the files and read globals */ for(i=0;italign = file->align - ((tbase + ttlen) & file->align); + file->dalign = file->align - ((dbase + tdlen) & file->align); + file->balign = file->align - ((bbase + tblen) & file->align); + + /* insert align fillers */ + ttlen += file->talign; + tdlen += file->talign; + tblen += file->talign; + /* compute relocation differences */ file->tdiff = ((tbase + ttlen) - file->tbase); file->ddiff = ((dbase + tdlen) - file->dbase); @@ -309,13 +497,13 @@ printf("zbase=%04x+len=%04x->%04x, file->zbase=%04x, f.zlen=%04x -> zdiff=%04x\n if (verbose > 0) { printf("Relocating file: %s\n", file->fname); - printf(" text: from %04x to %04x (diff is %04x, length is %04x)\n", - file->tbase, file->tbase + file->tdiff, file->tdiff, file->tlen); - printf(" data: from %04x to %04x (diff is %04x, length is %04x)\n", - file->dbase, file->dbase + file->ddiff, file->ddiff, file->dlen); - printf(" bss: from %04x to %04x (diff is %04x, length is %04x)\n", - file->bbase, file->bbase + file->bdiff, file->bdiff, file->blen); - printf(" zero: from %02x to %02x (diff is %02x, length is %02x)\n", + printf(" text: align fill %04x, relocate from %04x to %04x (diff is %04x, length is %04x)\n", + file->talign, file->tbase, file->tbase + file->tdiff, file->tdiff, file->tlen); + printf(" data: align fill %04x, relocate from %04x to %04x (diff is %04x, length is %04x)\n", + file->dalign, file->dbase, file->dbase + file->ddiff, file->ddiff, file->dlen); + printf(" bss: align fill %04x, relocate from %04x to %04x (diff is %04x, length is %04x)\n", + file->balign, file->bbase, file->bbase + file->bdiff, file->bdiff, file->blen); + printf(" zero: relocate from %02x to %02x (diff is %02x, length is %02x)\n", file->zbase, file->zbase + file->zdiff, file->zdiff, file->zlen); } @@ -499,13 +687,22 @@ printf("zbase=%04x+len=%04x->%04x, file->zbase=%04x, f.zlen=%04x -> zdiff=%04x\n } fputc(0,fd); + // align filler is NOP, just in case + memset(alignbuf, 0xea, sizeof(alignbuf)); + // write text segment for(i=0;italign) { + fwrite(alignbuf, 1, fp[i]->talign, fd); + } fwrite(fp[i]->buf + fp[i]->tpos, 1, fp[i]->tlen, fd); } // write data segment for(i=0;idalign) { + fwrite(alignbuf, 1, fp[i]->dalign, fd); + } fwrite(fp[i]->buf + fp[i]->dpos, 1, fp[i]->dlen, fd); } @@ -732,6 +929,7 @@ file65 *load_file(char *fname) { struct stat fs; FILE *fp; int mode, hlen; + int align; size_t n; file=malloc(sizeof(file65)); @@ -757,6 +955,7 @@ file65 *load_file(char *fname) { fclose(fp); if((n>=file->fsize) && (!memcmp(file->buf, cmp, 5))) { mode=file->buf[7]*256+file->buf[6]; + file->mode = mode; if(mode & 0x2000) { fprintf(stderr,"file65: %s: 32 bit size not supported\n", fname); free(file->buf); free(file); file=NULL; @@ -766,14 +965,38 @@ file65 *load_file(char *fname) { fname); free(file->buf); free(file); file=NULL; } else { + + align = mode & 3; + switch(align) { + case 0: + align = 0; + break; + case 1: + // word align + align = 1; + break; + case 2: + // long align + align = 3; + break; + case 3: + // page align + align = 255; + break; + } + file->align = align; + hlen = BUF+read_options(file->buf+BUF); file->tbase = file->buf[ 9]*256+file->buf[ 8]; file->tlen = file->buf[11]*256+file->buf[10]; + file->talign= 0; file->dbase = file->buf[13]*256+file->buf[12]; file->dlen = file->buf[15]*256+file->buf[14]; + file->dalign= 0; file->bbase = file->buf[17]*256+file->buf[16]; file->blen = file->buf[19]*256+file->buf[18]; + file->balign= 0; file->zbase = file->buf[21]*256+file->buf[20]; file->zlen = file->buf[23]*256+file->buf[22]; diff --git a/xa/src/xa.c b/xa/src/xa.c index 4bc4d4f..7cb012f 100644 --- a/xa/src/xa.c +++ b/xa/src/xa.c @@ -140,12 +140,20 @@ int main(int argc,char *argv[]) char old_o[MAXLINE]; tim1=time(NULL); - - ncmos=0; - n65816=0; + + // note: unfortunately we do no full distinction between 65C02 and 65816. + // The conflict is in the column 7 and column f opcodes, where the 65C02 + // has the BBR/BBS/SMB/RMB opcodes, but the 65816 has its own. + // Also, we potentially could support the 65SC02, which is the 65C02, but + // without the conflicting BBR/BBS/SMB/RMB opcodes. + // This, however, is a TODO for a later version. cmosfl=1; + //fmode = FM_CPU2_65C02; w65816=0; /* default: 6502 only */ + ncmos=0; // counter for CMOS opcodes used + n65816=0; // counter for 65816-specific opcodes used + altppchar = '#' ; /* i.e., NO alternate char */ if((tmpp = strrchr(argv[0],'/'))) { @@ -218,7 +226,7 @@ int main(int argc,char *argv[]) } if (argv[i][2] == '#') fprintf(stderr, - "using -p# is evidence of stupidity\n"); + "using -p# is not necessary, '#' is the default\n"); altppchar = argv[i][2]; if (argv[i][3] != '\0') fprintf(stderr, @@ -299,12 +307,29 @@ int main(int argc,char *argv[]) break; case 'C': cmosfl = 0; + fmode &= ~FM_CPU2; // fall back to standard 6502 + // breaks existing tests (compare with pre-assembled files) + //if (w65816) { + // fmode |= FM_CPU2_65816E; + //} break; case 'W': w65816 = 0; + fmode &= ~FM_CPU; + fmode &= ~FM_CPU2; + // breaks existing tests (compare with pre-assembled files) + //if (cmosfl) { + // fmode |= FM_CPU2_65C02; + //} break; case 'w': + // note: we do not disable cmos here, as opcode tables note CMOS for + // opcodes common to both, CMOS and 65816 as well. w65816 = 1; + fmode &= ~FM_CPU2; + // breaks existing tests (compare with pre-assembled files) + //fmode |= FM_CPU; // 65816 bit + //fmode |= FM_CPU2_65816E;// 6502 in 65816 emu, to manage opcode compatibility in ldo65 break; case 'B': showblk = 1; @@ -612,32 +637,6 @@ int h_length(void) { return 26+o_length(); } -#if 0 -/* write header for relocatable output format */ -int h_write(FILE *fp, int tbase, int tlen, int dbase, int dlen, - int bbase, int blen, int zbase, int zlen) { - - fputc(1, fp); /* version byte */ - fputc(0, fp); /* hi address 0 -> no C64 */ - fputc("o", fp); - fputc("6", fp); - fputc("5", fp); - fputc(0, fp); /* format version */ - fputw(mode, fp); /* file mode */ - fputw(tbase,fp); /* text base */ - fputw(tlen,fp); /* text length */ - fputw(dbase,fp); /* data base */ - fputw(dlen,fp); /* data length */ - fputw(bbase,fp); /* bss base */ - fputw(blen,fp); /* bss length */ - fputw(zbase,fp); /* zerop base */ - fputw(zlen,fp); /* zerop length */ - - o_write(fp); - - return 0; -} -#endif static int setfext(char *s, char *ext) { @@ -665,11 +664,6 @@ static int setfext(char *s, char *ext) return(0); } -/* -static char *tmp; -static unsigned long tmpz; -static unsigned long tmpe; -*/ static long ga_p1(void) { diff --git a/xa/src/xah.h b/xa/src/xah.h index a126ed2..1d3321e 100644 --- a/xa/src/xah.h +++ b/xa/src/xah.h @@ -192,11 +192,22 @@ typedef struct { #define A_LONG 0xc000 +/* o65 file format mode bits */ #define FM_OBJ 0x1000 #define FM_SIZE 0x2000 #define FM_RELOC 0x4000 #define FM_CPU 0x8000 +#define FM_CPU2 0x00f0 + +#define FM_CPU2_6502 0x0000 +#define FM_CPU2_65C02 0x0010 +#define FM_CPU2_65SC02 0x0020 +#define FM_CPU2_65CE02 0x0030 +#define FM_CPU2_NMOS 0x0040 +#define FM_CPU2_65816E 0x0050 + +/* segment definitions */ #define SEG_ABS 0 #define SEG_UNDEF 1 #define SEG_TEXT 2 diff --git a/xa/tests/cll/Makefile b/xa/tests/cll/Makefile index a3aee25..97e02d6 100644 --- a/xa/tests/cll/Makefile +++ b/xa/tests/cll/Makefile @@ -6,3 +6,7 @@ all: test1 %: %.a65 ${XA} -XCA65 -o $@ $< +clean: + rm -f *.o65 + rm -f test1 +