/* ********
* L2.C * New linker for BDS C
********
Written 1980 by Scott W. Layson
This code is in the public domain.
This is an improved linker for BDS C CRL format. It eliminates the
jump table at the beginning of each function in the object code,
thus saving up to 10% or so in code space with a slight improvement
in speed.
Modified further 30th July, 1982 by
Steve de Plater
v2.23
Now allows the "-s" parameter to send the Link
Map to the screen, as in CLINK. Functions are sorted
in address order.Use the "-sa" parameter to select
alphabetical order.
Modified 1982 by John Woolner
v.2.22
Automatic searching of all DEFF files in the following order, existing
on the default drive.
DEFF9 DEFF8 DEFF7 DEFF6 DEFF5 DEFF4 DEFF3 DEFF1 DEFF0 DEFF DEFF2
Note that DEFF2 is scanned last, and later libraries should NOT require
functions from libraries earlier in the list (they won't be found).
(if they don't exist on the default disk they will be skipped, no error
message will be generated).
A carriage return to the '... file to be searched' question initiates
a scan of all DEFF as described above.
'-o filename' allows setting output filename. If not specified output
will be first progfile loaded.
Auto repeating disk designators now exist. If a disk is specified in
the command line it will be used for all subsequent loads/searches
unless a new designator is specified. (then that new one will repeat
for remainder of line etc.).
e.g.
l2 b:l2 -l chario scott
will load l2.crl from disk B
scan chario.crl & scott.crl on disk B
scan CP/M default disk for 'DEFF's
and write output l2.com to disk B.
l2 b:l2 -l chario a:scott
will do as above but will search scott.crl on disk A
CAUTION
l2 l2 -l b:chario scott
will load l2.crl from disk A
scan chario.crl & scott.crl from disk B
scan CP/M default drive for DEFF's
and WRITE L2.COM to disk B (last designator before write)!!!
use instead
l2 a:l2 -l b:chario scott (first progfile has designator
therefore output file has designator)
or
l2 l2 -l b:chario scott -o a:l2 (output file set with designator).
*/
/**************** Globals ****************/
/* #define SDOS /* comment this out for CP/M */*/
/* commented out --------------------------
#define OVERLAYS /* comment this out for shorter version */
-------- end comment out */
/* comment out -----------------------------
#define MARC /* for MARC cross-linker version
(enables the "-marc" option) */
-------- end comment out */
#include "bdscio.h" /* for i/o buffer defs */
#define NUL 0
#define FLAG char
#define repeat while (1)
#define STDOUT 1
/* Phase control */
#define INMEM 1 /* while everything still fits */
#define DISK1 2 /* overflow; finish building table */
#define DISK2 3 /* use table to do window link */
int phase;
/* function table */
struct funct {
char fname[9];
FLAG flinkedp; /* in memory already? */
char *faddr; /* address of first ref link if not linked */
} *ftab;
int nfuncts; /* no. of functions in table */
int maxfuncts; /* table size */
#define LINKED 1 /* (flinkedp) function really here */
#define EXTERNAL 2 /* function defined in separate symbol table */
char fdir [512]; /* CRL file function directory */
/* command line parameters etc. */
int nprogs, nlibs;
char defdisk; /* optional default drive */
FLAG indeff; /* set when scanning DEFF files */
FLAG outflag; /* flag for if output set on command */
char outfile [15]; /* output file name */
char progfiles [30] [15]; /* program file names */
char libfiles [20] [15]; /* library file names */
FLAG symsp, /* write symbols to .sym file? */
appstatsp, /* append stats to .sym file? */
sepstatsp; /* write stats to .lnk file? */
#ifdef MARC
FLAG maxmemp, /* punt MARC shell? */
marcp; /* uses CM.CCC */
#endif
char mainfunct[10];
FLAG ovlp; /* make overlay? */
char symsfile [15]; /* file to load symbols from (for overlays) */
FLAG Tflag; /* TRUE if "-t" option given */
unsigned Tval; /* arg to "-t", if present */
FLAG Sflag; /* TRUE if "-s" option given */
FLAG SAflag; /* TRUE if "-sa" option given */
/* useful things to have defined */
struct inst {
char opcode;
char *address;
};
union ptr {
unsigned u; /* an int */
unsigned *w; /* a word ptr */
char *b; /* a byte ptr */
struct inst *i; /* an instruction ptr */
};
/* Link control variables */
union ptr codend; /* last used byte of code buffer + 1 */
union ptr exts; /* start of externals */
union ptr acodend; /* actual code-end address */
unsigned extspc; /* size of externals */
unsigned origin; /* origin of code */
unsigned buforg; /* origin of code buffer */
unsigned jtsaved; /* bytes of jump table saved */
char *lspace; /* space to link in */
char *lspcend; /* end of link area */
char *lodstart; /* beginning of current file */
/* i/o buffer */
struct iobuf {
int fd;
int isect; /* currently buffered sector */
int nextc; /* index of next char in buffer */
char buff [128];
} ibuf, obuf;
codend.b = lspace;
if (!ovlp) {
#ifdef MARC
if (copen (&ibuf, marcp ? "CM.CCC" : "C.CCC") < 0)
#else
if (copen (&ibuf, "C.CCC") < 0)
#endif
Fatal ("Can't open C.CCC\n");
else
printf ("<< Loading %14s >>\n", "C.CCC");
if (cread (&ibuf, lspace, 128) < 128) /* read a sector */
Fatal ("C.CCC: read error!\n");
temp.b = lspace + 0x17;
len = *temp.w; /* how long is it? */
cread (&ibuf, lspace + 128, len - 128); /* read rest */
codend.b += len;
cclose (&ibuf);
}
else codend.i++->opcode = JMP;
}
linkprog() /* link in all program files */
{
int i;
union ptr dirtmp;
struct funct *fnct;
for (i=0; i<nprogs; ++i) {
makeext (&progfiles[i], "CRL");
if (copen (&ibuf, progfiles[i]) < 0) {
printf ("Can't open %s\n", progfiles[i]);
continue;
}
printf ("<< Loading %14s >>\n", &progfiles[i]);
readprog (i==0);
for (dirtmp.b=&fdir; *dirtmp.b != 0x80;) {
fnct = intern (dirtmp.b); /* for each module */
skip7 (&dirtmp); /* in directory */
if (!fnct->flinkedp)
linkmod (fnct, lodstart + *dirtmp.w - 0x205);
else if (phase != DISK2) {
puts ("Duplicate program function '");
puts (&fnct->fname);
puts ("', not linked.\n");
}
dirtmp.w++;
} /* intern & link it */
cclose (&ibuf);
}
}
linklibs() /* link in library files */
{
int ifile;
for (ifile=0; ifile<nlibs; ++ifile) scanlib (ifile);
while (missingp()) {
puts ("Enter the name of a file to be searched: ");
gets (&libfiles[nlibs]);
if(strlen(&libfiles[nlibs])==0) strcpy7(&libfiles[nlibs],"DEFF");
scanlib (nlibs++);
}
acodend.b = codend.b - lspace + buforg; /* save that number! */
if (!exts.b) exts.b = acodend.b;
}
missingp() /* are any functions missing? print them out */
{
int i, foundp;
foundp = FALSE;
for (i=0; i<nfuncts; ++i)
if (!ftab[i].flinkedp) {
if (!foundp) puts ("*** Missing functions:\n");
puts (&ftab[i].fname);
puts ("\n");
foundp = TRUE;
}
return (foundp);
}
linkmod (fnct, modstart) /* link in a module */
struct funct *fnct;
union ptr modstart; /* loc. of module in memory */
{
union ptr temp,
jump, /* jump table temp */
body, /* loc. of function in memory */
code, /* loc. of code proper in mem. */
finalloc; /* runtime loc. of function */
unsigned flen, nrelocs, jtsiz, offset;
stats (chan) /* print statistics on chan */
int chan;
{
unsigned temp, *tptr;
tptr = 6;
fprintf (chan, "\n\nLink statistics:\n");
fprintf (chan, " Number of functions: %d\n", nfuncts);
fprintf (chan, " Code ends at: 0x%x\n", acodend.u);
fprintf (chan, " Externals begin at: 0x%x\n", exts.u);
fprintf (chan, " Externals end at: 0x%x\n", exts.u + extspc);
fprintf (chan, " End of current TPA: 0x%x\n", *tptr);
fprintf (chan, " Jump table bytes saved: 0x%x\n", jtsaved);
temp = lspcend;
if (phase == INMEM)
fprintf (chan,
" Link space remaining: %dK\n", (temp - codend.u) / 1024);
}
makeext (fname, ext) /* force a file extension to ext */
char *fname, *ext;
{
char fbuf [15],*f;
f = fname; /* to use later */
if(*(fname+1) == ':') defdisk = *fname;
else if (!indeff && defdisk) /* only if default exists */
{
strcpy7(&fbuf,fname);
sprintf(fname,"%c:%s",defdisk,&fbuf);
}
while (*fname && (*fname != '.')) {
*fname = toupper (*fname); /* upcase as well */
++fname;
}
*fname++ = '.';
strcpy7 (fname, ext);
while (*f = (*f++ & 0x7f)); /* strip parity */
}
strcmp7 (s1, s2) /* compare two bit-7-terminated strings */
char *s1, *s2; /* also works for non-null NUL-term strings */
{
/* char c1, c2, end1, end2; (These are now global for speed) */