mirror of
https://github.com/fachat/xa65.git
synced 2024-06-08 07:29:39 +00:00
1498 lines
38 KiB
C
1498 lines
38 KiB
C
/* ldo65 -- A part of xa65 - 65xx/65816 cross-assembler and utility suite
|
||
* o65 relocatable object file linker
|
||
*
|
||
* A part of the xa65 - 65xx/65816 cross-assembler and utility suite
|
||
*
|
||
* Copyright (C) 1997-2023 Andr<64> Fachat (fachat@web.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 <errno.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#ifndef _MSC_VER
|
||
#include <unistd.h>
|
||
#endif
|
||
#include <sys/stat.h>
|
||
|
||
#include "version.h"
|
||
|
||
#define BUF (9*2+8) /* 16 bit header */
|
||
|
||
#define programname "ldo65"
|
||
#define progversion "v0.2.0"
|
||
#define author "Written by Andre Fachat"
|
||
#define copyright "Copyright (C) 1997-2023 Andre Fachat. Formerly ld65."
|
||
|
||
#undef DEBUG
|
||
|
||
/*
|
||
|
||
The process of linking works as follows:
|
||
|
||
1. Every file is loaded in turn via load_file()
|
||
2. Calculate new base addresses per segment
|
||
3. Merge all globals from all files into a single table, checking for duplicates
|
||
4. Resolve undefined labels, and merge remaining into global list
|
||
5. relocate all segments, create global relocation tables
|
||
6. verify undefined labels
|
||
7. write out target file
|
||
|
||
*/
|
||
|
||
/* 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 *name;
|
||
int len;
|
||
int newidx; /* index in new global undef table (for reloc) */
|
||
int resolved; /* index in current global label table after resolve (-1 is not found) */
|
||
} undefs;
|
||
|
||
/* file information */
|
||
typedef struct {
|
||
char *fname; /* file name */
|
||
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 */
|
||
|
||
int tdiff; /* text segment relocation diff */
|
||
int ddiff; /* data segment relocation diff */
|
||
int bdiff; /* bss segment relocation diff */
|
||
int zdiff; /* zero segment relocation diff */
|
||
|
||
int tpos; /* position of text segment in file */
|
||
int dpos; /* position of data segment in file */
|
||
int upos; /* position of undef'd list in file */
|
||
int trpos; /* position of text reloc tab in file */
|
||
int drpos; /* position of data reloc tab in file */
|
||
int gpos; /* position of globals list in file */
|
||
|
||
int nundef; /* number of undefined labels */
|
||
undefs *ud; /* undefined labels list NULL if none */
|
||
} file65;
|
||
|
||
/* globally defined lables are stored in this struct */
|
||
typedef struct {
|
||
char *name;
|
||
int len; /* length of labelname */
|
||
int fl; /* 0=ok, 1=multiply defined */
|
||
int val; /* address value */
|
||
int seg; /* segment */
|
||
file65 *file; /* in which file is it? */
|
||
} glob;
|
||
|
||
file65* load_file(char *fname);
|
||
|
||
int read_options(unsigned char *f);
|
||
int read_undef(unsigned char *f, file65 *fp);
|
||
int write_undef(FILE *f, file65 *fp);
|
||
int check_undef(file65 *fp, char *defined[], int ndefined);
|
||
int len_reloc_seg(unsigned char *buf, int ri);
|
||
int reloc_seg(unsigned char *buf, int pos, int addr, int rdiff, int ri,
|
||
unsigned char *obuf, int *lastaddrp, int *rop, file65 *fp);
|
||
unsigned char* reloc_globals(unsigned char*, file65 *fp);
|
||
int read_globals(file65 *file);
|
||
int write_options(FILE *fp, file65 *file);
|
||
int write_reloc(file65 *fp[], int nfp, FILE *f);
|
||
int write_globals(FILE *fp);
|
||
int write_nglobals(FILE *fp, char **globdef, int nglobal);
|
||
int find_global(unsigned char *name);
|
||
int resolve_undef(file65 *file, int *remains);
|
||
|
||
file65 file;
|
||
unsigned char cmp[] = { 1, 0, 'o', '6', '5' };
|
||
unsigned char hdr[26] = { 1, 0, 'o', '6', '5', 0 };
|
||
|
||
const char *cpunames[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"
|
||
};
|
||
|
||
int verbose = 0;
|
||
|
||
void usage(FILE *fp)
|
||
{
|
||
fprintf(fp,
|
||
"Usage: %s [OPTION]... [FILE]...\n"
|
||
"Linker 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"
|
||
" -G suppress writing of globals\n"
|
||
" -U accept any undef'd labels after linking\n"
|
||
" -L<name> accept specific given undef'd labels after linking\n"
|
||
" -g<name> only export the globals defined with (multiple) -g options\n"
|
||
" -v verbose output\n"
|
||
" --version output version information and exit\n"
|
||
" --help display this help and exit\n",
|
||
programname);
|
||
}
|
||
|
||
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;
|
||
unsigned char *treloc, *dreloc;
|
||
char *outfile = "a.o65";
|
||
int j, jm;
|
||
file65 *file, **fp = NULL;
|
||
FILE *fd;
|
||
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;
|
||
int ndefined = 0;
|
||
int ndefalloc = 0;
|
||
|
||
// globals allowed by -g
|
||
char **globdef = NULL;
|
||
int nglobal = 0;
|
||
int ngloballoc = 0;
|
||
|
||
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);
|
||
}
|
||
|
||
/* read options */
|
||
while (i < argc && argv[i][0] == '-') {
|
||
arg = NULL;
|
||
/* process options */
|
||
switch (argv[i][1]) {
|
||
case 'v':
|
||
j = 1;
|
||
while (argv[i][j] == 'v') {
|
||
verbose++;
|
||
j++;
|
||
}
|
||
break;
|
||
case 'G':
|
||
noglob = 1;
|
||
break;
|
||
case 'U':
|
||
undefok = 1;
|
||
break;
|
||
case 'o':
|
||
if (argv[i][2])
|
||
outfile = argv[i] + 2;
|
||
else
|
||
outfile = argv[++i];
|
||
break;
|
||
case 'g':
|
||
noglob = 1;
|
||
if (argv[i][2])
|
||
arg = argv[i] + 2;
|
||
else
|
||
arg = argv[++i];
|
||
if (ngloballoc == 0) {
|
||
ngloballoc = 20;
|
||
globdef = malloc(ngloballoc * sizeof(char*));
|
||
} else if (nglobal >= ngloballoc) {
|
||
ngloballoc *= 2;
|
||
globdef = realloc(globdef, ngloballoc * sizeof(char*));
|
||
}
|
||
globdef[nglobal++] = arg;
|
||
break;
|
||
case 'L':
|
||
if (argv[i][2])
|
||
arg = argv[i] + 2;
|
||
else
|
||
arg = argv[++i];
|
||
if (ndefalloc == 0) {
|
||
ndefalloc = 20;
|
||
defined = malloc(ndefalloc * sizeof(char*));
|
||
} else if (ndefined >= ndefalloc) {
|
||
ndefalloc *= 2;
|
||
defined = realloc(defined, ndefalloc * sizeof(char*));
|
||
}
|
||
defined[ndefined++] = arg;
|
||
break;
|
||
case 'b':
|
||
switch (argv[i][2]) {
|
||
case 't':
|
||
if (argv[i][3])
|
||
tbase = atoi(argv[i] + 3);
|
||
else
|
||
tbase = atoi(argv[++i]);
|
||
break;
|
||
case 'd':
|
||
if (argv[i][3])
|
||
dbase = atoi(argv[i] + 3);
|
||
else
|
||
dbase = atoi(argv[++i]);
|
||
break;
|
||
case 'b':
|
||
if (argv[i][3])
|
||
bbase = atoi(argv[i] + 3);
|
||
else
|
||
bbase = atoi(argv[++i]);
|
||
break;
|
||
case 'z':
|
||
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;
|
||
default:
|
||
fprintf(stderr, "file65: %s unknown option, use '-h for help\n",
|
||
argv[i]);
|
||
break;
|
||
}
|
||
i++;
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// step 1 - load files
|
||
|
||
/* each file is loaded first */
|
||
j = 0;
|
||
jm = 0;
|
||
fp = NULL;
|
||
while (i < argc) {
|
||
file65 *f;
|
||
f = load_file(argv[i]);
|
||
if (f) {
|
||
if (j >= jm)
|
||
fp = realloc(fp, (jm = (jm ? jm * 2 : 10)) * sizeof(file65*));
|
||
if (!fp) {
|
||
fprintf(stderr, "Oops, no more memory\n");
|
||
exit(1);
|
||
}
|
||
fp[j++] = f;
|
||
} else {
|
||
exit(1);
|
||
}
|
||
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;
|
||
if (verbose) {
|
||
printf("Starting CPU type calculation with mode %s (%d) ...\n",
|
||
cpunames[trgcpu], trgcpu);
|
||
}
|
||
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 & FM_CPU2) >> 4;
|
||
if (verbose) {
|
||
printf(
|
||
"Matching file %s with CPU %s (%d) to target %s (%d) ...\n",
|
||
file->fname, cpunames[fcpu], fcpu, cpunames[trgcpu],
|
||
trgcpu);
|
||
}
|
||
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, cpunames[fcpu]);
|
||
er = 1;
|
||
}
|
||
// fall-through
|
||
case 0x2: // 65SC02 - CMOS without BBR/BBS/RMB/SMB, compatible with 65816
|
||
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, cpunames[fcpu], trgcpu,
|
||
cpunames[trgcpu]);
|
||
er = 1;
|
||
}
|
||
break;
|
||
|
||
case 0x5: // 65816 in 6502 emulation mode
|
||
if (trgcpu == 1 || trgcpu == 3) {
|
||
// 65C02 and 65CE02 are 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, cpunames[fcpu], trgcpu,
|
||
cpunames[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, cpunames[fcpu], trgcpu,
|
||
cpunames[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, cpunames[4]);
|
||
er = 1;
|
||
}
|
||
break;
|
||
default:
|
||
if (fcpu > 5) {
|
||
printf(
|
||
"Warning: unknown CPU mode %d (%s) detected in file %s\n",
|
||
fcpu, cpunames[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 (verbose && !er) {
|
||
printf("... to new target %s (%d)\n", cpunames[trgcpu], trgcpu);
|
||
}
|
||
}
|
||
if (er) {
|
||
exit(1);
|
||
}
|
||
trgmode |= trgcpu << 4;
|
||
}
|
||
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
|
||
|
||
/* now [tdbz]base holds new segment base address */
|
||
/* 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);
|
||
file->bdiff = ((bbase + tblen) - file->bbase);
|
||
file->zdiff = ((zbase + tzlen) - file->zbase);
|
||
|
||
/*
|
||
printf("tbase=%04x+len=%04x->%04x, file->tbase=%04x, f.tlen=%04x -> tdiff=%04x\n",
|
||
tbase, ttlen, (tbase + ttlen), file->tbase, file->tlen, file->tdiff);
|
||
printf("zbase=%04x+len=%04x->%04x, file->zbase=%04x, f.zlen=%04x -> zdiff=%04x\n",
|
||
zbase, tzlen, (zbase + tzlen), file->zbase, file->zlen, file->zdiff);
|
||
*/
|
||
|
||
if (verbose > 0) {
|
||
printf("Relocating file: %s [CPU %s]\n", file->fname,
|
||
cpunames[((file->mode & FM_CPU2) >> 4) & 0x0f]);
|
||
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);
|
||
}
|
||
|
||
/* update globals (for result file) */
|
||
ttlen += file->tlen;
|
||
tdlen += file->dlen;
|
||
tblen += file->blen;
|
||
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;
|
||
if (tbase + ttlen > 0x10000) {
|
||
fprintf(stderr,
|
||
"Overflow in text segment: end at %06x behind 64k limit\n",
|
||
tbase + ttlen);
|
||
er = 1;
|
||
}
|
||
if (dbase + tdlen > 0x10000) {
|
||
fprintf(stderr,
|
||
"Overflow in data segment: end at %06x behind 64k limit\n",
|
||
dbase + tdlen);
|
||
er = 1;
|
||
}
|
||
if (bbase + tblen > 0x10000) {
|
||
fprintf(stderr,
|
||
"Overflow in bss segment: end at %06x behind 64k limit\n",
|
||
bbase + tblen);
|
||
er = 1;
|
||
}
|
||
if (zbase + tzlen > 0x100) {
|
||
fprintf(stderr,
|
||
"Overflow in zero segment: end at %04x behind 256 byte limit\n",
|
||
zbase + tzlen);
|
||
er = 1;
|
||
}
|
||
if (er) {
|
||
exit(1);
|
||
}
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// step 3 - merge globals from all files into single table
|
||
//
|
||
|
||
for (i = 0; i < j; i++) {
|
||
file = fp[i];
|
||
// merge globals into single table
|
||
read_globals(file);
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// step 4 - for each file, resolve undefined lables, storing replacement info
|
||
// in the ud label table; merge remaining undefined labels into global
|
||
// undef list
|
||
|
||
for (i = 0; i < j; i++) {
|
||
file = fp[i];
|
||
// merge globals into single table
|
||
resolve_undef(file, &nundef);
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// step 5 - relocate each text and data segment, replacing the resolved
|
||
// undefined labels and re-numbering the remaining ones
|
||
|
||
// reloc globals first, so reloc_seg has current info for resolved undef'd labels
|
||
|
||
routtlen = 1; // end-of-table byte
|
||
routdlen = 1; // end-of-table byte
|
||
|
||
for (i = 0; i < j; i++) {
|
||
file = fp[i];
|
||
|
||
routtlen += (file->drpos - file->trpos);
|
||
routdlen += (file->gpos - file->drpos);
|
||
|
||
reloc_globals(file->buf + file->gpos, file);
|
||
}
|
||
|
||
// prep global reloc tables
|
||
treloc = malloc(routtlen);
|
||
dreloc = malloc(routdlen);
|
||
|
||
#ifdef DEBUG
|
||
printf("prep'd text reloc table at %p (%d bytes)\n", treloc, routtlen);
|
||
printf("prep'd data reloc table at %p (%d bytes)\n", dreloc, routdlen);
|
||
#endif
|
||
tro = 0;
|
||
dro = 0;
|
||
|
||
// segment position of last relocation entry to compute offsets across files
|
||
lasttaddr = tbase - 1;
|
||
lastdaddr = dbase - 1;
|
||
|
||
for (i = 0; i < j; i++) {
|
||
file = fp[i];
|
||
|
||
reloc_seg(file->buf, // input buffer
|
||
file->tpos, // position of segment in input buffer
|
||
file->tbase, // segment base address
|
||
file->tdiff, // reloc difference
|
||
file->trpos, // position of reloc table in input
|
||
treloc, // output reloc buffer
|
||
&lasttaddr, // last relocated target address
|
||
&tro, // pointer in output reloc bufer
|
||
file);
|
||
|
||
reloc_seg(file->buf, file->dpos, file->dbase, file->ddiff, file->drpos,
|
||
dreloc, &lastdaddr, &dro, file);
|
||
|
||
// change file information to relocated values
|
||
file->tbase += file->tdiff;
|
||
file->dbase += file->ddiff;
|
||
file->bbase += file->bdiff;
|
||
file->zbase += file->zdiff;
|
||
}
|
||
|
||
// finalize global reloc table
|
||
treloc[tro++] = 0;
|
||
dreloc[dro++] = 0;
|
||
|
||
// -------------------------------------------------------------------------
|
||
// step 6 - validate undefined labels
|
||
//
|
||
|
||
if (nundef > 0 && !undefok) {
|
||
int er = 0;
|
||
// we have undefined labels, but it's not ok (no -U)
|
||
// check -L defined labels
|
||
for (i = 0; i < j; i++) {
|
||
if (check_undef(fp[i], defined, ndefined)) {
|
||
er = -1;
|
||
}
|
||
}
|
||
if (er) {
|
||
fprintf(stderr, "%d Undefined labels remain - aborting\n", nundef);
|
||
exit(1);
|
||
}
|
||
}
|
||
|
||
// -------------------------------------------------------------------------
|
||
// step 7 - write out the resulting o65 file
|
||
//
|
||
|
||
// prepare header
|
||
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;
|
||
hdr[14] = tdlen & 255;
|
||
hdr[15] = (tdlen >> 8) & 255;
|
||
hdr[16] = bbase & 255;
|
||
hdr[17] = (bbase >> 8) & 255;
|
||
hdr[18] = tblen & 255;
|
||
hdr[19] = (tblen >> 8) & 255;
|
||
hdr[20] = zbase & 255;
|
||
hdr[21] = (zbase >> 8) & 255;
|
||
hdr[22] = tzlen & 255;
|
||
hdr[23] = (tzlen >> 8) & 255;
|
||
hdr[24] = 0;
|
||
hdr[25] = 0;
|
||
|
||
// open file
|
||
fd = fopen(outfile, "wb");
|
||
if (!fd) {
|
||
fprintf(stderr, "Couldn't open output file %s (%s)\n", outfile,
|
||
strerror(errno));
|
||
exit(2);
|
||
}
|
||
|
||
// write header
|
||
fwrite(hdr, 1, 26, fd);
|
||
|
||
// write options - this writes _all_ options from _all_files!
|
||
for (i = 0; i < j; i++) {
|
||
write_options(fd, fp[i]);
|
||
}
|
||
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);
|
||
}
|
||
|
||
// write list of undefined labels
|
||
fputc(nundef & 0xff, fd);
|
||
fputc((nundef >> 8) & 0xff, fd);
|
||
if (nundef > 0) {
|
||
for (i = 0; i < j; i++) {
|
||
write_undef(fd, fp[i]);
|
||
}
|
||
}
|
||
|
||
// write relocation tables
|
||
fwrite(treloc, tro, 1, fd);
|
||
fwrite(dreloc, dro, 1, fd);
|
||
|
||
// write globals
|
||
if (!noglob) {
|
||
write_globals(fd);
|
||
} else {
|
||
if (nglobal > 0) {
|
||
write_nglobals(fd, globdef, nglobal);
|
||
} else {
|
||
fputc(0, fd);
|
||
fputc(0, fd);
|
||
}
|
||
}
|
||
|
||
fclose(fd);
|
||
return 0;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
int write_options(FILE *fp, file65 *file) {
|
||
return fwrite(file->buf + BUF, 1, file->tpos - BUF - 1, fp);
|
||
}
|
||
|
||
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, file65 *file) {
|
||
int bufp; // pointer in input buffer
|
||
int startp; // pointer to start of label name
|
||
int nlabels; // number of labels in file
|
||
undefs *current = NULL;
|
||
int i;
|
||
|
||
bufp = 0;
|
||
nlabels = buf[bufp] + 256 * buf[bufp + 1];
|
||
bufp += 2;
|
||
|
||
file->nundef = nlabels;
|
||
|
||
if (nlabels == 0) {
|
||
file->ud = NULL;
|
||
} else {
|
||
file->ud = malloc(nlabels * sizeof(undefs));
|
||
if (!file->ud) {
|
||
fprintf(stderr, "Oops, no more memory\n");
|
||
exit(1);
|
||
}
|
||
i = 0;
|
||
while (i < nlabels) {
|
||
// find length of label name
|
||
startp = bufp;
|
||
while (buf[bufp++])
|
||
;
|
||
// store label info
|
||
current = &file->ud[i];
|
||
current->name = (char*) buf + startp;
|
||
current->len = bufp - startp - 1;
|
||
current->resolved = -1;
|
||
/*printf("read undef '%s'(%p), len=%d, ll=%d, l=%d, buf[l]=%d\n",
|
||
file->ud[i].name, file->ud[i].name, file->ud[i].len,ll,l,buf[l]);*/
|
||
i++;
|
||
}
|
||
}
|
||
return bufp;
|
||
}
|
||
|
||
int resolve_undef(file65 *file, int *remains) {
|
||
int i;
|
||
undefs *current;
|
||
int nlabels = file->nundef;
|
||
#ifdef DEBUG
|
||
printf("resolved undef file %s (%d undef'd)\n", file->fname, nlabels);
|
||
#endif
|
||
if (nlabels == 0) {
|
||
return 0;
|
||
}
|
||
current = file->ud;
|
||
|
||
for (i = 0; i < nlabels; i++) {
|
||
// store pointer to global in label info
|
||
// if NULL is returned, is not resolved
|
||
current->resolved = find_global(current->name);
|
||
#ifdef DEBUG
|
||
printf("resolved undef label %s to: resolved=%d, newidx=%d\n", current->name, current->resolved, *remains);
|
||
#endif
|
||
if (current->resolved == -1) {
|
||
// keep in global undef list
|
||
current->newidx = *remains;
|
||
*remains += 1;
|
||
}
|
||
current++;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int write_undef(FILE *f, file65 *fp) {
|
||
int i;
|
||
for (i = 0; i < fp->nundef; i++) {
|
||
undefs *current = &fp->ud[i];
|
||
|
||
if (current->resolved == -1) {
|
||
// only write unresolved entries
|
||
fprintf(f, "%s%c", current->name, 0);
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int check_undef(file65 *fp, char *defined[], int ndefined) {
|
||
|
||
int er = 0;
|
||
int i, j;
|
||
for (i = 0; i < fp->nundef; i++) {
|
||
undefs *current = &fp->ud[i];
|
||
|
||
if (current->resolved == -1) {
|
||
// only check unresolved entries
|
||
int found = 0;
|
||
for (j = 0; j < ndefined; j++) {
|
||
if (defined && !strcmp(defined[j], current->name)) {
|
||
// label is found, so it's ok
|
||
found = 1;
|
||
break;
|
||
}
|
||
}
|
||
if (!found) {
|
||
fprintf(stderr, "Unresolved label '%s' from file '%s'\n",
|
||
current->name, fp->fname);
|
||
er = -1;
|
||
}
|
||
}
|
||
}
|
||
return er;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
/* compute and return the length of the relocation table */
|
||
int len_reloc_seg(unsigned char *buf, int ri) {
|
||
int type, seg;
|
||
|
||
while (buf[ri]) {
|
||
if ((buf[ri] & 255) == 255) {
|
||
ri++;
|
||
} else {
|
||
ri++;
|
||
type = buf[ri] & 0xe0;
|
||
seg = buf[ri] & 0x07;
|
||
/*printf("reloc entry @ rtab=%p (offset=%d), adr=%04x, type=%02x, seg=%d\n",buf+ri-1, *(buf+ri-1), adr, type, seg);*/
|
||
ri++;
|
||
switch (type) {
|
||
case 0x80:
|
||
break;
|
||
case 0x40:
|
||
ri++;
|
||
break;
|
||
case 0x20:
|
||
break;
|
||
}
|
||
if (seg == 0)
|
||
ri += 2;
|
||
}
|
||
}
|
||
return ++ri;
|
||
}
|
||
|
||
#define reldiff(s) (((s)==2)?fp->tdiff:(((s)==3)?fp->ddiff:(((s)==4)?fp->bdiff:(((s)==5)?fp->zdiff:0))))
|
||
|
||
unsigned char* reloc_globals(unsigned char *buf, file65 *fp) {
|
||
int n, old, new, seg;
|
||
char *name;
|
||
|
||
n = buf[0] + 256 * buf[1];
|
||
buf += 2;
|
||
|
||
while (n) {
|
||
name = buf;
|
||
while (*(buf++))
|
||
;
|
||
seg = *buf & 0x07;
|
||
old = buf[1] + 256 * buf[2];
|
||
new = old + reldiff(seg);
|
||
if (verbose > 1) {
|
||
printf("%s:%s: old=%04x, seg=%d, rel=%04x, new=%04x\n", fp->fname,
|
||
name, old, seg, reldiff(seg), new);
|
||
}
|
||
buf[1] = new & 255;
|
||
buf[2] = (new >> 8) & 255;
|
||
buf += 3;
|
||
n--;
|
||
}
|
||
return buf;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
file65* load_file(char *fname) {
|
||
file65 *file;
|
||
struct stat fs;
|
||
FILE *fp;
|
||
int mode, hlen;
|
||
int align;
|
||
size_t n;
|
||
|
||
file = malloc(sizeof(file65));
|
||
if (!file) {
|
||
fprintf(stderr, "Oops, not enough memory!\n");
|
||
exit(1);
|
||
}
|
||
|
||
/*printf("load_file(%s)\n",fname);*/
|
||
|
||
file->fname = fname;
|
||
if (stat(fname, &fs) < 0) {
|
||
perror("while opening file: stat");
|
||
exit(1);
|
||
}
|
||
file->fsize = fs.st_size;
|
||
file->buf = malloc(file->fsize);
|
||
if (!file->buf) {
|
||
fprintf(stderr, "Oops, no more memory!\n");
|
||
exit(1);
|
||
}
|
||
|
||
fp = fopen(fname, "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];
|
||
file->mode = mode;
|
||
if (mode & 0x2000) {
|
||
fprintf(stderr, "file65: %s: 32 bit size not supported\n",
|
||
fname);
|
||
free(file->buf);
|
||
free(file);
|
||
file = NULL;
|
||
} else if (mode & 0x4000) {
|
||
fprintf(stderr,
|
||
"file65: %s: pagewise relocation not supported\n",
|
||
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];
|
||
|
||
file->tpos = hlen;
|
||
file->dpos = hlen + file->tlen;
|
||
file->upos = file->dpos + file->dlen;
|
||
file->trpos = file->upos
|
||
+ read_undef(file->buf + file->upos, file);
|
||
file->drpos = len_reloc_seg(file->buf, file->trpos);
|
||
file->gpos = len_reloc_seg(file->buf, file->drpos);
|
||
}
|
||
} else {
|
||
fprintf(stderr, "Error: %s: not an o65 file\n", fname);
|
||
return NULL;
|
||
}
|
||
} else {
|
||
fprintf(stderr, "file65: %s: %s\n", fname, strerror(errno));
|
||
return NULL;
|
||
}
|
||
return file;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
// global list of all global labels
|
||
glob *gp = NULL;
|
||
// number of global labels
|
||
int g = 0;
|
||
// number of globals for which memory is already allocated
|
||
int gm = 0;
|
||
|
||
int write_globals(FILE *fp) {
|
||
int i;
|
||
|
||
fputc(g & 255, fp);
|
||
fputc((g >> 8) & 255, fp);
|
||
|
||
for (i = 0; i < g; i++) {
|
||
fprintf(fp, "%s%c%c%c%c", gp[i].name, 0, gp[i].seg, gp[i].val & 255,
|
||
(gp[i].val >> 8) & 255);
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int write_nglobals(FILE *fp, char **globdef, int nglobal) {
|
||
int i, j;
|
||
int newnum = 0;
|
||
|
||
// first check which defined globals are allowed to be exported
|
||
// and clear out the other ones
|
||
for (i = 0; i < g; i++) {
|
||
for (j = 0; j < nglobal; j++) {
|
||
if (!strcmp(gp[i].name, globdef[j])) {
|
||
// found
|
||
break;
|
||
}
|
||
}
|
||
if (j >= nglobal) {
|
||
// not found
|
||
gp[i].name = NULL;
|
||
} else {
|
||
// found, so we inc the counter
|
||
newnum++;
|
||
}
|
||
}
|
||
|
||
// then check which globals from the -g list are actually used, and warn about unused ones
|
||
for (j = 0; j < nglobal; j++) {
|
||
for (i = 0; i < g; i++) {
|
||
if (gp[i].name != NULL && !strcmp(gp[i].name, globdef[j])) {
|
||
// found
|
||
break;
|
||
}
|
||
}
|
||
if (i >= g) {
|
||
// not found
|
||
fprintf(stderr,
|
||
"Warning: command line allowed global '%s' is not defined!\n",
|
||
globdef[j]);
|
||
}
|
||
}
|
||
|
||
// write out only defined globals
|
||
fputc(newnum & 255, fp);
|
||
fputc((newnum >> 8) & 255, fp);
|
||
|
||
for (i = 0; i < g; i++) {
|
||
if (gp[i].name != NULL) {
|
||
fprintf(fp, "%s%c%c%c%c", gp[i].name, 0, gp[i].seg, gp[i].val & 255,
|
||
(gp[i].val >> 8) & 255);
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int read_globals(file65 *fp) {
|
||
int i, l, n, old, new, seg, ll;
|
||
char *name;
|
||
unsigned char *buf = fp->buf + fp->gpos;
|
||
|
||
n = buf[0] + 256 * buf[1];
|
||
buf += 2;
|
||
|
||
while (n) {
|
||
/*printf("reading %s, ", buf);*/
|
||
name = (char*) buf;
|
||
l = 0;
|
||
while (buf[l++])
|
||
;
|
||
buf += l;
|
||
ll = l - 1;
|
||
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);*/
|
||
|
||
/* multiply defined? */
|
||
for (i = 0; i < g; i++) {
|
||
if (ll == gp[i].len && !strcmp(name, gp[i].name)) {
|
||
fprintf(stderr,
|
||
"Warning: label '%s' multiply defined (%s and %s)\n",
|
||
name, fp->fname, gp[i].file->fname);
|
||
gp[i].fl = 1;
|
||
break;
|
||
}
|
||
}
|
||
/* not already defined */
|
||
if (i >= g) {
|
||
if (g >= gm) {
|
||
gp = realloc(gp, (gm = (gm ? 2 * gm : 40)) * sizeof(glob));
|
||
if (!gp) {
|
||
fprintf(stderr, "Oops, no more memory\n");
|
||
exit(1);
|
||
}
|
||
}
|
||
if (g >= 0x10000) {
|
||
fprintf(stderr,
|
||
"Outch, maximum number of labels (65536) exceeded!\n");
|
||
exit(3);
|
||
}
|
||
gp[g].name = name;
|
||
gp[g].len = ll;
|
||
gp[g].seg = seg;
|
||
gp[g].val = new;
|
||
gp[g].fl = 0;
|
||
gp[g].file = fp;
|
||
#ifdef DEBUG
|
||
printf("set global label '%s' (l=%d, seg=%d, val=%04x)\n", gp[g].name,
|
||
gp[g].len, gp[g].seg, gp[g].val);
|
||
#endif
|
||
g++;
|
||
}
|
||
|
||
buf += 3;
|
||
n--;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int find_global(unsigned char *name) {
|
||
int i;
|
||
for (i = 0; i < g; i++) {
|
||
|
||
if (!strcmp(gp[i].name, name)) {
|
||
// found
|
||
return i;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
// searches for a global label in a file by name.
|
||
// returns the value of a found global value
|
||
int find_file_global(unsigned char *bp, file65 *fp, int *seg) {
|
||
int i, l;
|
||
char *n;
|
||
int nl = bp[0] + 256 * bp[1];
|
||
|
||
l = fp->ud[nl].len;
|
||
n = fp->ud[nl].name;
|
||
/*printf("find_global(%s (len=%d))\n",n,l);*/
|
||
|
||
for (i = 0; i < g; i++) {
|
||
if (gp[i].len == l && !strcmp(gp[i].name, n)) {
|
||
*seg = gp[i].seg;
|
||
bp[0] = i & 255;
|
||
bp[1] = (i >> 8) & 255;
|
||
/*printf("return gp[%d]=%s (len=%d), val=%04x\n",i,gp[i].name,gp[i].len,gp[i].val);*/
|
||
return gp[i].val;
|
||
}
|
||
}
|
||
fprintf(stderr, "Warning: undefined label '%s' in file %s\n", n, fp->fname);
|
||
return 0;
|
||
}
|
||
|
||
/***************************************************************************/
|
||
|
||
#define forwardpos() \
|
||
while(addr-lastaddr>254){obuf[ro++]=255;lastaddr+=254;}obuf[ro++]=addr-lastaddr;lastaddr=addr
|
||
|
||
int reloc_seg(unsigned char *buf, int pos, int addr, int rdiff, int ri,
|
||
unsigned char *obuf, int *lastaddrp, int *rop, file65 *fp) {
|
||
int type, seg, old, new, ro, lastaddr, diff;
|
||
int base;
|
||
|
||
/*
|
||
pos = address of current position
|
||
ri = position of relocation table in *buf for reading the reloc entries
|
||
ro(p) = position of relocation table entry for writing the modified entries
|
||
*/
|
||
base = addr;
|
||
addr--;
|
||
ro = *rop;
|
||
lastaddr = *lastaddrp - rdiff;
|
||
|
||
#ifdef DEBUG
|
||
printf("reloc_seg: %s: addr=%04x, pos=%04x, lastaddr=%04x (%04x - %04x)\n",
|
||
fp->fname, addr, pos, lastaddr, *lastaddrp, rdiff);
|
||
#endif
|
||
|
||
while (buf[ri]) {
|
||
// still reloc entry
|
||
if ((buf[ri] & 255) == 255) {
|
||
addr += 254;
|
||
ri++;
|
||
} else {
|
||
addr += buf[ri] & 255;
|
||
type = buf[ri + 1] & 0xe0;
|
||
seg = buf[ri + 1] & 0x07;
|
||
#ifdef DEBUG
|
||
printf("reloc entry @ ri=%04x, pos=%04x, type=%02x, seg=%d, offset=%d, reldiff=%04x\n",
|
||
ri, pos, type, seg, addr-lastaddr, reldiff(seg));
|
||
#endif
|
||
switch (type) {
|
||
case 0x80:
|
||
// address (word) relocation
|
||
old = buf[addr - base + pos] + 256 * buf[addr - base + pos + 1];
|
||
if (seg) {
|
||
diff = reldiff(seg);
|
||
ri++; // skip position byte
|
||
forwardpos(); // re-write position offset
|
||
obuf[ro++] = buf[ri++]; // relocation byte ($8x for segments text, data, bss, zp)
|
||
} else {
|
||
// undefined
|
||
undefs *u = &fp->ud[buf[ri + 2] + 256 * buf[ri + 3]];
|
||
#ifdef DEBUG
|
||
printf("found undef'd label %s, resolved=%d, newidx=%d, (ri=%d, ro=%d)\n", u->name, u->resolved, u->newidx, ri, ro);
|
||
#endif
|
||
if (u->resolved == -1) {
|
||
// not resolved
|
||
diff = 0;
|
||
ri++; // skip position byte
|
||
forwardpos(); // re-write position offset
|
||
obuf[ro++] = buf[ri++]; // relocation byte ($8x for segments text, data, bss, zp)
|
||
obuf[ro++] = u->newidx & 0xff;// output label number lo/hi
|
||
obuf[ro++] = (u->newidx >> 8) & 0xff;
|
||
ri += 2; // acount for label number in input
|
||
} else {
|
||
// resolved from global list
|
||
glob *gl = &gp[u->resolved];
|
||
diff = gl->val;
|
||
seg = gl->seg;
|
||
if (seg != 1) {
|
||
// not an absolute value
|
||
forwardpos(); // re-write position offset
|
||
obuf[ro++] = 0x80 | seg;// relocation byte for new segment
|
||
} else {
|
||
// absolute value - do not write a new relocation entry
|
||
}
|
||
ri += 4;// account for position, segment byte, label number in reloc table
|
||
}
|
||
}
|
||
new = old + diff;
|
||
/*printf("old=%04x, new=%04x\n",old,new);*/
|
||
buf[addr - base + pos] = new & 255;
|
||
buf[addr - base + pos + 1] = (new >> 8) & 255;
|
||
break;
|
||
case 0x40:
|
||
// high byte relocation
|
||
if (seg) {
|
||
old = buf[addr - base + pos] * 256 + buf[ri + 2];
|
||
diff = reldiff(seg);
|
||
forwardpos(); // re-write position offset
|
||
obuf[ro++] = buf[ri + 1];// relocation byte ($4x for segments text, data, bss, zp)
|
||
obuf[ro++] = (old + diff) & 255;
|
||
ri += 3; // skip position, segment, and low byte
|
||
} else {
|
||
undefs *u;
|
||
old = buf[addr - base + pos] * 256 + buf[ri + 4];
|
||
// undefined
|
||
u = &fp->ud[buf[ri + 2] + 256 * buf[ri + 3]];
|
||
if (u->resolved == -1) {
|
||
// not resolved
|
||
diff = 0;
|
||
forwardpos(); // re-write position offset
|
||
obuf[ro++] = buf[ri + 1];// relocation byte ($8x for segments text, data, bss, zp)
|
||
obuf[ro++] = u->newidx & 0xff;// output label number lo/hi
|
||
obuf[ro++] = (u->newidx >> 8) & 0xff;
|
||
obuf[ro++] = buf[ri + 4]; // low byte for relocation
|
||
} else {
|
||
// resolved from global list
|
||
glob *gl = &gp[u->resolved];
|
||
diff = gl->val;
|
||
seg = gl->seg;
|
||
if (seg != 1) {
|
||
// not an absolute value
|
||
forwardpos(); // re-write position offset
|
||
obuf[ro++] = 0x40 | seg;// relocation byte for new segment
|
||
obuf[ro++] = (old + diff) & 0xff;// low byte for relocation
|
||
} else {
|
||
// absolute value - do not write a new relocation entry
|
||
}
|
||
}
|
||
ri += 5; // account for position, segment byte, label number in reloc table, low byte
|
||
}
|
||
new = old + diff;
|
||
buf[addr - base + pos] = (new >> 8) & 255;
|
||
break;
|
||
case 0x20:
|
||
// low byte relocation
|
||
old = buf[addr - base + pos];
|
||
diff = 0;
|
||
if (seg) {
|
||
diff = reldiff(seg);
|
||
forwardpos();
|
||
obuf[ro++] = buf[ri + 1];// relocation byte ($4x for segments text, data, bss, zp)
|
||
ri += 2; // account for position & segment
|
||
} else {
|
||
// undefined
|
||
undefs *u = &fp->ud[buf[ri + 2] + 256 * buf[ri + 3]];
|
||
if (u->resolved == -1) {
|
||
// not resolved
|
||
diff = 0;
|
||
forwardpos(); // re-write position offset
|
||
obuf[ro++] = buf[ri + 1];// relocation byte ($8x for segments text, data, bss, zp)
|
||
obuf[ro++] = u->newidx & 0xff;// output label number lo/hi
|
||
obuf[ro++] = (u->newidx >> 8) & 0xff;
|
||
} else {
|
||
// resolved from global list
|
||
glob *gl = &gp[u->resolved];
|
||
diff = gl->val;
|
||
seg = gl->seg;
|
||
if (seg != 1) {
|
||
// not an absolute value
|
||
forwardpos(); // re-write position offset
|
||
obuf[ro++] = 0x20 | seg;// relocation byte for new segment
|
||
} else {
|
||
// absolute value - do not write a new relocation entry
|
||
}
|
||
}
|
||
ri += 4;// account for position, segment byte, label number in reloc table
|
||
}
|
||
new = old + diff;
|
||
buf[addr - base + pos] = new & 255;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
*lastaddrp = lastaddr + rdiff;
|
||
*rop = ro;
|
||
#ifdef DEBUG
|
||
printf(" --> lastaddr=%04x (%04x - %04x), rop=%d\n", lastaddr, *lastaddrp, rdiff, ro);
|
||
#endif
|
||
return ++ri;
|
||
}
|
||
|