Compare commits

...

2 Commits

Author SHA1 Message Date
Andre Fachat cc03ac80be finished work on aligned linking, added tests 2023-11-28 20:09:11 +01:00
Andre Fachat 5ff76efec2 add handling of align in ldo; start working on CPU modes 2023-11-28 17:51:34 +01:00
21 changed files with 427 additions and 51 deletions

View File

@ -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?

View File

@ -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 = "<none>";
{
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;i<j;i++) {
int fcpu;
file = fp[i];
if (file->align > 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: Alignment at %d-boundaries required\n", 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,64 @@ 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 = 0;
if (tbase & file->align) {
fprintf(stderr, "Error: text segment start address ($%04x) "
"not aligned as required by first file (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 by first file (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 (by first file 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;i<j;i++) {
file = fp[i];
/* compute align fillers */
file->talign = 0;
file->dalign = 0;
file->balign = 0;
// filler only needed if align not zero ...
if (file->align) {
// ... and respective segment not empty
if (file->tlen) {
//file->talign = file->align + 1 - ((tbase + ttlen) & file->align);
file->talign = ( -((tbase + ttlen) & file->align) ) & file->align;
}
if (file->dlen) {
//file->dalign = file->align + 1 - ((dbase + tdlen) & file->align);
file->dalign = ( -((dbase + tdlen) & file->align) ) & file->align;
}
if (file->blen) {
//file->balign = file->align + 1 - ((bbase + tblen) & file->align);
file->balign = ( -((bbase + tblen) & file->align) ) & file->align;
}
}
/* insert align fillers */
ttlen += file->talign;
tdlen += file->dalign;
tblen += file->balign;
/* compute relocation differences */
file->tdiff = ((tbase + ttlen) - file->tbase);
file->ddiff = ((dbase + tdlen) - file->dbase);
@ -309,13 +516,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);
}
@ -326,6 +533,31 @@ printf("zbase=%04x+len=%04x->%04x, file->zbase=%04x, f.zlen=%04x -> zdiff=%04x\n
tzlen += file->zlen;
}
// validate various situations.
if (maxalign != 0) {
int er = 0;
if (tbase & maxalign) {
fprintf(stderr, "Error: text segment start address ($%04x) "
"not aligned as first required by file %s (at %d bytes)\n",
tbase, alignfname, maxalign + 1);
er = 1;
}
if (dbase & maxalign) {
fprintf(stderr, "Error: data segment start address ($%04x) "
"not aligned as first required by file %s (at %d bytes)\n",
dbase, alignfname, maxalign + 1);
er = 1;
}
if (bbase & maxalign) {
fprintf(stderr, "Error: bss segment start address ($%04x) "
"not aligned as first required (by file %s (at %d bytes)\n",
bbase, alignfname, maxalign + 1);
er = 1;
}
if (er) {
exit(1);
}
}
// validate various situations.
{
int er = 0;
@ -471,7 +703,7 @@ printf("zbase=%04x+len=%04x->%04x, file->zbase=%04x, f.zlen=%04x -> zdiff=%04x\n
//
// prepare header
hdr[ 6] = 0; hdr[ 7] = 0;
hdr[ 6] = trgmode & 255; hdr[ 7] = (trgmode>>8)& 255;
hdr[ 8] = tbase & 255; hdr[ 9] = (tbase>>8) & 255;
hdr[10] = ttlen & 255; hdr[11] = (ttlen >>8)& 255;
hdr[12] = dbase & 255; hdr[13] = (dbase>>8) & 255;
@ -499,13 +731,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;i<j;i++) {
if (fp[i]->talign) {
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;i<j;i++) {
if (fp[i]->dalign) {
fwrite(alignbuf, 1, fp[i]->dalign, fd);
}
fwrite(fp[i]->buf + fp[i]->dpos, 1, fp[i]->dlen, fd);
}
@ -732,6 +973,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 +999,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 +1009,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];

View File

@ -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;
@ -510,10 +535,6 @@ int main(int argc,char *argv[])
sprintf(out,"Warning: bss segment ($%04x) start address doesn't align to %d!\n", bbase, align);
logout(out);
}
if(zbase & (align-1)) {
sprintf(out,"Warning: zero segment ($%04x) start address doesn't align to %d!\n", zbase, align);
logout(out);
}
if (n65816>0)
fmode |= 0x8000;
switch(align) {
@ -612,32 +633,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 +660,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)
{

View File

@ -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

View File

@ -6,3 +6,7 @@ all: test1
%: %.a65
${XA} -XCA65 -o $@ $<
clean:
rm -f *.o65
rm -f test1

40
xa/tests/mode/Makefile Normal file
View File

@ -0,0 +1,40 @@
FILES=at1.o65 at2.o65 at4.o65 at256.o65 ad1.o65 ad2.o65 ad4.o65 ad256.o65 ab1.o65 ab2.o65 ab4.o65 ab256.o65
VERBOSE=
#VERBOSE=-v
XA=../../xa
LDO=../../ldo65
all: test1 test2 test3 test4
%.o65: %.a65
${XA} -R -o $@ $<
# test with files in order of increasing align
test1: ${FILES}
${LDO} ${VERBOSE} -o $@.o65 $^
cmp $@.o65 $@.ok
# test with files in order of decreasing align
test2: ${FILES}
${LDO} ${VERBOSE} -o $@.o65 ab256.o65 ab4.o65 ab2.o65 ab1.o65 ad256.o65 ad4.o65 ad2.o65 ad1.o65 at256.o65 at4.o65 at2.o65 at1.o65
cmp $@.o65 $@.ok
# test with files in order of increasing align, not starting at align=1
test3: ${FILES}
${LDO} ${VERBOSE} -o $@.o65 ab2.o65 at2.o65 ad2.o65 ab4.o65 at4.o65 ad4.o65 ad256.o65 at256.o65 ab256.o65
cmp $@.o65 $@.ok
# test with files in order of increasing align, with non-aligned segment addresses
test4: ${FILES}
${LDO} ${VERBOSE} -bt 1025 -bd 1025 -o $@.o65 ab2.o65 at2.o65 ad2.o65 ab4.o65 at4.o65 ad4.o65 ad256.o65 at256.o65 ab256.o65 || exit 0 && exit 1
${LDO} ${VERBOSE} -bt 1026 -bd 1026 -o $@.o65 ab2.o65 at2.o65 ad2.o65 ab4.o65 at4.o65 ad4.o65 ad256.o65 at256.o65 ab256.o65 || exit 0 && exit 1
${LDO} ${VERBOSE} -bt 1027 -bd 1027 -o $@.o65 ab2.o65 at2.o65 ad2.o65 ab4.o65 at4.o65 ad4.o65 ad256.o65 at256.o65 ab256.o65 || exit 0 && exit 1
${LDO} ${VERBOSE} -bt 1028 -bd 1028 -o $@.o65 ab2.o65 at2.o65 ad2.o65 ab4.o65 at4.o65 ad4.o65 ad256.o65 at256.o65 ab256.o65 || exit 0 && exit 1
clean:
rm -f *.o65

6
xa/tests/mode/ab1.a65 Normal file
View File

@ -0,0 +1,6 @@
.align 1
.bss
.byt 0

6
xa/tests/mode/ab2.a65 Normal file
View File

@ -0,0 +1,6 @@
.align 2
.bss
.byt 0

6
xa/tests/mode/ab256.a65 Normal file
View File

@ -0,0 +1,6 @@
.align 256
.bss
.byt 0

6
xa/tests/mode/ab4.a65 Normal file
View File

@ -0,0 +1,6 @@
.align 4
.bss
.byt 0

6
xa/tests/mode/ad1.a65 Normal file
View File

@ -0,0 +1,6 @@
.align 1
.data
.byt 0

6
xa/tests/mode/ad2.a65 Normal file
View File

@ -0,0 +1,6 @@
.align 2
.data
.byt 0

6
xa/tests/mode/ad256.a65 Normal file
View File

@ -0,0 +1,6 @@
.align 256
.data
.byt 0

6
xa/tests/mode/ad4.a65 Normal file
View File

@ -0,0 +1,6 @@
.align 4
.data
.byt 0

4
xa/tests/mode/at1.a65 Normal file
View File

@ -0,0 +1,4 @@
.align 1
iny

4
xa/tests/mode/at2.a65 Normal file
View File

@ -0,0 +1,4 @@
.align 2
iny

4
xa/tests/mode/at256.a65 Normal file
View File

@ -0,0 +1,4 @@
.align 256
iny

4
xa/tests/mode/at4.a65 Normal file
View File

@ -0,0 +1,4 @@
.align 4
iny

BIN
xa/tests/mode/test1.ok Normal file

Binary file not shown.

BIN
xa/tests/mode/test2.ok Normal file

Binary file not shown.

BIN
xa/tests/mode/test3.ok Normal file

Binary file not shown.