Logo Search packages:      
Sourcecode: xa version File versions  Download package

reloc65.c

/* 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 <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

#define     BUF   (9*2+8)           /* 16 bit header */

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);
unsigned char *reloc_globals(unsigned char *, file65 * fp);

file65 file;
unsigned char cmp[] = { 1, 0, 'o', '6', '5' };

void usage(void)
{
      printf("reloc65: relocates 'o65' files\n"
             "  reloc65 [options] [filenames...]\n"
             "options:\n"
             "  -v        = print version number\n"
             "  -h, -?    = print this help\n"
             "  -b? adr   = relocates segment '?' (i.e. 't' for text segment,\n"
             "              'd' for data, 'b' for bss and 'z' for zeropage) to the new\n"
             "              address 'adr'.\n"
             "  -o file   = uses 'file' as output file. otherwise write to 'a.o65'.\n"
             "  -x?       = extracts text '?'='t' or data '?'='d' segment from file\n"
             "              instead of writing back the whole file\n");
      exit(0);
}

int main(int argc, char *argv[])
{
      int i = 1, mode, hlen;
      size_t n;
      FILE *fp;
      int tflag = 0, dflag = 0, bflag = 0, zflag = 0;
      int tbase = 0, dbase = 0, bbase = 0, zbase = 0;
      char *outfile = "a.o65";
      int extract = 0;

      if (argc <= 1)
            usage();

      while (i < argc) {
            if (argv[i][0] == '-') {
                  /* process options */
                  switch (argv[i][1]) {
                  case 'v':
                        printf
                            ("reloc65 version 0.2 (c) 1997 a.fachat\n");
                        break;
                  case 'o':
                        if (argv[i][2])
                              outfile = argv[i] + 2;
                        else
                              outfile = argv[++i];
                        break;
                  case 'b':
                        switch (argv[i][2]) {
                        case 't':
                              tflag = 1;
                              if (argv[i][3])
                                    tbase = atoi(argv[i] + 3);
                              else
                                    tbase = atoi(argv[++i]);
                              break;
                        case 'd':
                              dflag = 1;
                              if (argv[i][3])
                                    dbase = atoi(argv[i] + 3);
                              else
                                    dbase = atoi(argv[++i]);
                              break;
                        case 'b':
                              bflag = 1;
                              if (argv[i][3])
                                    bbase = atoi(argv[i] + 3);
                              else
                                    bbase = atoi(argv[++i]);
                              break;
                        case 'z':
                              zflag = 1;
                              if (argv[i][3])
                                    zbase = atoi(argv[i] + 3);
                              else
                                    zbase = atoi(argv[++i]);
                              break;
                        default:
                              printf
                                  ("Unknown segment type '%c' - ignored!\n",
                                   argv[i][2]);
                              break;
                        }
                        break;
                  case 'x':   /* extract segment */
                        switch (argv[i][2]) {
                        case 't':
                              extract = 1;
                              break;
                        case 'd':
                              extract = 2;
                              break;
                        case 'z':
                        case 'b':
                              printf
                                  ("Cannot extract segment type '%c' - ignored!\n",
                                   argv[i][2]);
                              break;
                        default:
                              printf
                                  ("Unknown segment type '%c' - ignored!\n",
                                   argv[i][2]);
                              break;
                        }
                        break;
                  case 'h':
                  case '?':
                        usage();
                  default:
                        fprintf(stderr,
                              "reloc65: %s unknown option, use '-?' for help\n",
                              argv[i]);
                        break;
                  }
            } else {
                  struct stat fs;
                  file.fname = argv[i];
                  stat(argv[i], &fs);
                  file.fsize = fs.st_size;
                  file.buf = malloc(file.fsize);
                  if (!file.buf) {
                        fprintf(stderr, "Oops, no more memory!\n");
                        exit(1);
                  }
                  printf("reloc65: read file %s -> %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 & 0x2000) {
                                    fprintf(stderr,
                                          "reloc65: %s: 32 bit size not supported\n",
                                          argv[i]);
                              } else if (mode & 0x4000) {
                                    fprintf(stderr,
                                          "reloc65: %s: pagewise relocation not supported\n",
                                          argv[i]);
                              } else {
                                    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;
                                    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;
                                    file.zbase =
                                        file.buf[21] * 256 +
                                        file.buf[20];
                                    file.zlen =
                                        file.buf[23] * 256 +
                                        file.buf[21];
                                    file.zdiff =
                                        zflag ? zbase -
                                        file.zbase : 0;

                                    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);
                                    file.extab =
                                        reloc_seg(file.segd,
                                                file.dlen,
                                                file.rdtab,
                                                &file);

                                    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:
                                                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++]);
      }
      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 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:
                        old = buf[adr] + 256 * buf[adr + 1];
                        new = old + reldiff(seg);
                        buf[adr] = new & 255;
                        buf[adr + 1] = (new >> 8) & 255;
                        break;
                  case 0x40:
                        old = buf[adr] * 256 + *rtab;
                        new = old + reldiff(seg);
                        buf[adr] = (new >> 8) & 255;
                        *rtab = new & 255;
                        rtab++;
                        break;
                  case 0x20:
                        old = buf[adr];
                        new = old + reldiff(seg);
                        buf[adr] = new & 255;
                        break;
                  }
                  if (seg == 0)
                        rtab += 2;
            }
      }
      if (adr > len) {
            fprintf(stderr,
                  "reloc65: %s: Warning: relocation table entries past segment end!\n",
                  fp->fname);
      }
      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;
}

Generated by  Doxygen 1.6.0   Back to index