/* reloc65 -- A part of xa65 - 65xx/65816 cross-assembler and utility suite * o65 file relocator * * Copyright (C) 1989-1997 André Fachat (a.fachat@physik.tu-chemnitz.de) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include "version.h" #define BUF (9*2+8) /* 16 bit header */ #define programname "reloc65" #define progversion "v0.2.1" #define author "Written by Andre Fachat" #define copyright "Copyright (C) 1997-2002 Andre Fachat." /* 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 typedef struct { char *fname; size_t fsize; unsigned char *buf; int tbase, tlen, dbase, dlen, bbase, blen, zbase, zlen; int tdiff, ddiff, bdiff, zdiff; unsigned char *segt; unsigned char *segd; unsigned char *utab; unsigned char *rttab; unsigned char *rdtab; unsigned char *extab; } file65; int read_options(unsigned char *f); int read_undef(unsigned char *f); unsigned char *reloc_seg(unsigned char *f, int len, unsigned char *rtab, file65 *fp, int undefwarn); unsigned char *reloc_globals(unsigned char *, file65 *fp); file65 file; unsigned char cmp[] = { 1, 0, 'o', '6', '5' }; void usage(FILE *fp) { fprintf(fp, "Usage: %s [OPTION]... [FILE]...\n" "Relocator for o65 object files\n" "\n" " -b? addr relocates segment '?' (i.e. 't' for text segment,\n" " 'd' for data, 'b' for bss and 'z' for zeropage) to the new\n" " address `addr'\n" " -o file uses `file' as output file. Default is `a.o65'\n" " -x? extracts text `?' = `t' or data `?' = `d' segment from file\n", programname); fprintf(fp, " instead of writing back the whole file\n" " -X extracts the file such that text and data\n" " segments are chained, i.e. possibly relocating\n" " the data segment to the end of the text segment\n" " -C Set the o65 CPU flags in the output for the following CPUs:\n" " 6502, 65SC02, 65C02, 65CE02, 65816, NMOS6502\n" " (for details see the man page)\n" " -v verbose output\n" " --version output version information and exit\n" " --help display this help and exit\n"); } const char *cpunames[16] = { "6502", "65C02", "65SC02", "65CE02", "NMOS6502", "65816", NULL, NULL, "6809", NULL, // 1000 - "Z80", NULL, NULL, // 1010 - "8086", // 1101 - "80286", // 1110 - NULL }; int main(int argc, char *argv[]) { int i = 1, mode, hlen; int j; size_t n; FILE *fp; int tflag = 0, dflag = 0, bflag = 0, zflag = 0; int tbase = 0, dbase = 0, bbase = 0, zbase = 0; int *base; char *outfile = "a.o65"; int extract = 0; int verbose = 0; int trgcpu = -1; // output file target CPU flag (-1 = do not change) char *arg; // temporary argument pointer if (argc <= 1) { usage(stderr); exit(1); } if (strstr(argv[1], "--help") || strstr(argv[1], "-?") || strstr(argv[1], "-h")) { usage(stdout); exit(0); } if (strstr(argv[1], "--version")) { version(programname, progversion, author, copyright); exit(0); } while(i %s\n",argv[i],outfile); fp = fopen(argv[i],"rb"); if(fp) { n = fread(file.buf, 1, file.fsize, fp); fclose(fp); if((n>=file.fsize) && (!memcmp(file.buf, cmp, 5))) { mode=file.buf[7]*256+file.buf[6]; if(mode & FM_SIZE) { fprintf(stderr,"reloc65: %s: 32 bit size not supported\n", argv[i]); } else if(mode & FM_RELOC) { fprintf(stderr,"reloc65: %s: pagewise relocation not supported\n", argv[i]); } else { if (trgcpu >= 0) { // change CPU flags mode &= ~FM_CPU; mode &= ~FM_CPU2; mode |= (trgcpu << 4); if (trgcpu == 5) { // this trgcpu is actually 65816 in emulation mode // unsure if we should do an own cmdline option mode |= FM_CPU; // 65816 native } } file.buf[6] = mode & 0xff; file.buf[7] = (mode >> 8) & 0xff; 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.tdiff = tflag ? tbase - file.tbase : 0; file.dbase = file.buf[13]*256+file.buf[12]; file.dlen = file.buf[15]*256+file.buf[14]; file.ddiff = dflag ? dbase - file.dbase : 0; if (extract == 3) { if (dflag) { fprintf(stderr,"reloc65: %s: Warning: data segment address overrides -X option\n", argv[i]); } else { dbase = file.tbase + file.tdiff + file.tlen; file.ddiff = dbase - file.dbase; } } file.bbase = file.buf[17]*256+file.buf[16]; file.blen = file.buf[19]*256+file.buf[18]; file.bdiff = bflag ? bbase - file.bbase : 0; if (extract == 3) { if (bflag) { fprintf(stderr,"reloc65: %s: Warning: bss segment address overrides -X option\n", argv[i]); } else { bbase = file.dbase + file.ddiff + file.dlen; file.bdiff = bbase - file.bbase; } } file.zbase = file.buf[21]*256+file.buf[20]; file.zlen = file.buf[23]*256+file.buf[22]; file.zdiff = zflag ? zbase - file.zbase : 0; if (verbose) { printf("Relocating segments to:\n"); printf("text segment @ $%04x - $%04x, %5d ($%04x) bytes, diff is %5d ($%04x)\n", file.tbase + file.tdiff, file.tbase + file.tdiff + file.tlen, file.tlen, file.tlen, file.tdiff, file.tdiff & 0xffff); printf("data segment @ $%04x - $%04x, %5d ($%04x) bytes, diff is %5d ($%04x)\n", file.dbase + file.ddiff, file.dbase + file.ddiff + file.dlen, file.dlen, file.dlen, file.ddiff, file.ddiff & 0xffff); printf("bss segment @ $%04x - $%04x, %5d ($%04x) bytes, diff is %5d ($%04x)\n", file.bbase + file.bdiff, file.bbase + file.bdiff + file.blen, file.blen, file.blen, file.bdiff, file.bdiff & 0xffff); printf("zero segment @ $%04x - $%04x, %5d ($%04x) bytes, diff is %5d ($%04x)\n", file.zbase + file.zdiff, file.zbase + file.zdiff + file.zlen, file.zlen, file.zlen, file.zdiff, file.zdiff & 0xffff); } /* pointer of position in file */ file.segt = file.buf + hlen; file.segd = file.segt + file.tlen; file.utab = file.segd + file.dlen; file.rttab = file.utab + read_undef(file.utab); file.rdtab = reloc_seg(file.segt, file.tlen, file.rttab, &file, extract); file.extab = reloc_seg(file.segd, file.dlen, file.rdtab, &file, extract); reloc_globals(file.extab, &file); if(tflag) { file.buf[ 9]= (tbase>>8)&255; file.buf[ 8]= tbase & 255; } if(dflag) { file.buf[13]= (dbase>>8)&255; file.buf[12]= dbase & 255; } if(bflag) { file.buf[17]= (bbase>>8)&255; file.buf[16]= bbase & 255; } if(zflag) { file.buf[21]= (zbase>>8)&255; file.buf[20]= zbase & 255; } fp = fopen(outfile, "wb"); if(fp) { switch(extract) { case 0: /* whole file */ fwrite(file.buf, 1, file.fsize, fp); break; case 1: /* text segment */ fwrite(file.segt, 1, file.tlen, fp); break; case 2: /* data segment */ fwrite(file.segd, 1, file.dlen, fp); break; case 3: /* text+data */ fwrite(file.segt, 1, file.tlen, fp); fwrite(file.segd, 1, file.dlen, fp); break; } fclose(fp); } else { fprintf(stderr,"reloc65: write '%s': %s\n", outfile, strerror(errno)); } } } else { fprintf(stderr,"reloc65: %s: not an o65 file!\n", argv[i]); if(file.buf[0]==1 && file.buf[1]==8 && file.buf[3]==8) { printf("%s: C64 BASIC executable (start address $0801)?\n", argv[i]); } else if(file.buf[0]==1 && file.buf[1]==4 && file.buf[3]==4) { printf("%s: CBM PET BASIC executable (start address $0401)?\n", argv[i]); } } } else { fprintf(stderr,"reloc65: read '%s': %s\n", argv[i], strerror(errno)); } } i++; } exit(0); } int read_options(unsigned char *buf) { int c, l=0; c=buf[0]; while(c && c!=EOF) { c&=255; l+=c; c=buf[l]; } return ++l; } int read_undef(unsigned char *buf) { int n, l = 2; n = buf[0] + 256*buf[1]; while(n){ n--; while(buf[l] != 0) { l++; } l++; } return l; } #define reldiff(s) (((s)==2)?fp->tdiff:(((s)==3)?fp->ddiff:(((s)==4)?fp->bdiff:(((s)==5)?fp->zdiff:0)))) unsigned char *reloc_seg(unsigned char *buf, int len, unsigned char *rtab, file65 *fp, int undefwarn) { int adr = -1; int type, seg, old, new; /*printf("tdiff=%04x, ddiff=%04x, bdiff=%04x, zdiff=%04x\n", fp->tdiff, fp->ddiff, fp->bdiff, fp->zdiff);*/ while(*rtab) { if((*rtab & 255) == 255) { adr += 254; rtab++; } else { adr += *rtab & 255; rtab++; type = *rtab & 0xe0; seg = *rtab & 0x07; /*printf("reloc entry @ rtab=%p (offset=%d), adr=%04x, type=%02x, seg=%d\n",rtab-1, *(rtab-1), adr, type, seg);*/ rtab++; switch(type) { case 0x80: /* WORD - two byte address */ old = buf[adr] + 256*buf[adr+1]; new = old + reldiff(seg); buf[adr] = new & 255; buf[adr+1] = (new>>8)&255; break; case 0x40: /* HIGH - high byte of an address */ old = buf[adr]*256 + *rtab; new = old + reldiff(seg); buf[adr] = (new>>8)&255; *rtab = new & 255; rtab++; break; case 0x20: /* LOW - low byt of an address */ old = buf[adr]; new = old + reldiff(seg); buf[adr] = new & 255; break; } if(seg==0) { /* undefined segment entry */ if (undefwarn) { fprintf(stderr,"reloc65: %s: Warning: undefined relocation table entry not handled!\n", fp->fname); } rtab+=2; } } } if(adr > len) { fprintf(stderr,"reloc65: %s: Warning: relocation table entries past segment end!\n", fp->fname); fprintf(stderr, "reloc65: adr=%x len=%x\n", adr, len); } return ++rtab; } unsigned char *reloc_globals(unsigned char *buf, file65 *fp) { int n, old, new, seg; n = buf[0] + 256*buf[1]; buf +=2; while(n) { /*printf("relocating %s, ", buf);*/ while(*(buf++)); seg = *buf; old = buf[1] + 256*buf[2]; new = old + reldiff(seg); /*printf("old=%04x, seg=%d, rel=%04x, new=%04x\n", old, seg, reldiff(seg), new);*/ buf[1] = new & 255; buf[2] = (new>>8) & 255; buf +=3; n--; } return buf; }