/* lkmain.c */
/*
* Copyright (C) 1989-2009 Alan R. Baldwin
*
* 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 3 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, see .
*
*
* Alan R. Baldwin
* 721 Berkeley St.
* Kent, Ohio 44240
*/
#include "aslink.h"
/*)Module lkmain.c
*
* The module lkmain.c contains the functions which
* (1) input the linker options, parameters, and specifications
* (2) perform a two pass link
* (3) produce the appropriate linked data output and/or
* link map file and/or relocated listing files.
*
* lkmain.c contains the following functions:
* FILE * afile()
* VOID bassav()
* VOID gblsav()
* int intsiz()
* VOID link_main()
* VOID lkexit()
* int fndext()
* int fndidx()
* int main()
* VOID map()
* int parse()
* VOID doparse()
* VOID setgbl()
* VOID usage()
*
* lkmain.c contains the following local variables:
* char * usetext[] array of pointers to the
* command option tect lines
*
*/
/* sdld 8051 specific */
/*JCF: Creates some of the default areas so they are allocated in the right order.*/
void Areas51 (void)
{
char * rel[] = {
"XH",
"H 7 areas 0 global symbols",
"A _CODE size 0 flags 0", /*Each .rel has one, so...*/
"A REG_BANK_0 size 0 flags 4", /*Register banks are overlayable*/
"A REG_BANK_1 size 0 flags 4",
"A REG_BANK_2 size 0 flags 4",
"A REG_BANK_3 size 0 flags 4",
"A BSEG size 0 flags 80", /*BSEG must be just before BITS*/
"A BSEG_BYTES size 0 flags 0", /*Size will be obtained from BSEG in lnkarea()*/
""
};
char * rel2[] = {
"XH",
"H C areas 0 global symbols",
"A _CODE size 0 flags 0", /*Each .rel has one, so...*/
"A REG_BANK_0 size 0 flags 4", /*Register banks are overlayable*/
"A REG_BANK_1 size 0 flags 4",
"A REG_BANK_2 size 0 flags 4",
"A REG_BANK_3 size 0 flags 4",
"A BSEG size 0 flags 80", /*BSEG must be just before BITS*/
"A BSEG_BYTES size 0 flags 0", /*Size will be obtained from BSEG in lnkarea()*/
"A BIT_BANK size 0 flags 4", /*Bit register bank is overlayable*/
"A DSEG size 0 flags 0",
"A OSEG size 0 flags 4",
"A ISEG size 0 flags 0",
"A SSEG size 0 flags 4",
""
};
int j;
struct sym * sp;
if (packflag) {
for (j = 0; rel2[j][0] != 0; j++) {
ip = rel2[j];
link_main();
}
}
else {
for (j = 0; rel[j][0] != 0; j++) {
ip = rel[j];
link_main();
}
}
/*Set the start address of the default areas:*/
for (ap = areap; ap; ap = ap->a_ap) {
/**/ if (!strcmp(ap->a_id, "REG_BANK_0")) { ap->a_addr = 0x00; ap->a_bset = 1; }
else if (!strcmp(ap->a_id, "REG_BANK_1")) { ap->a_addr = 0x08; ap->a_bset = 1; }
else if (!strcmp(ap->a_id, "REG_BANK_2")) { ap->a_addr = 0x10; ap->a_bset = 1; }
else if (!strcmp(ap->a_id, "REG_BANK_3")) { ap->a_addr = 0x18; ap->a_bset = 1; }
else if (!strcmp(ap->a_id, "BSEG_BYTES")) { ap->a_addr = 0x20; ap->a_bset = 1; }
else if (TARGET_IS_8051 && !strcmp(ap->a_id, "SSEG")) {
if (stacksize) ap->a_axp->a_size = stacksize;
}
}
sp = lkpsym("l_IRAM", 1);
sp->s_addr = ((iram_size>0) && (iram_size<=0x100)) ? iram_size : 0x0100;
sp->s_axp = NULL;
sp->s_type |= S_DEF;
}
/* end sdld 8051 specific */
/*)Function int main(argc,argv)
*
* int argc number of command line arguments + 1
* char * argv[] array of pointers to the command line
* arguments
*
* The function main() evaluates the command line arguments to
* determine if the linker parameters are to input through 'stdin'
* or read from a command file. The functions nxtline() and parse()
* are to input and evaluate the linker parameters. The linking process
* proceeds by making the first pass through each .rel file in the order
* presented to the linker. At the end of the first pass the setarea(),
* lnkarea(), setgbl(), and symdef() functions are called to evaluate
* the base address terms, link all areas, define global variables,
* and look for undefined symbols. Following these routines a linker
* map file may be produced and the linker output files may be opened.
* The second pass through the .rel files will output the linked data
* in one of the supported formats.
*
* local variables:
* int c character from argument string
* int i loop counter
* int j loop counter
* int k loop counter
*
* global variables:
* text line in ib[]
* lfile *cfp The pointer *cfp points to the
* current lfile structure
* char ctype[] array of character types, one per
* ASCII character
* lfile *filep The pointer *filep points to the
* beginning of a linked list of
* lfile structures.
* head *hp Pointer to the current
* head structure
* char ib[NINPUT] .rel file text line
* char *ip pointer into the .rel file
* lfile *linkp pointer to first lfile structure
* containing an input .rel file
* specification
* int lkerr error flag
* int oflag Output file type flag
* int objflg Linked file/library output object flag
* int pass linker pass number
* int pflag print linker command file flag
* int radix current number conversion radix
* FILE *sfp The file handle sfp points to the
* currently open file
* lfile *startp aslink startup file structure
* FILE * stdout c_library
*
* functions called:
* VOID chkbank() lkbank.c
* int fclose() c_library
* int fprintf() c_library
* VOID library() lklibr.c
* VOID link_main() lkmain.c
* VOID lkexit() lkmain.c
* VOID lkfopen() lkbank.c
* VOID lnkarea() lkarea.c
* VOID map() lkmain.c
* VOID new() lksym.c
* int nxtline() lklex.c
* int parse() lkmain.c
* VOID reloc() lkreloc.c
* VOID search() lklibr.c
* VOID setarea() lkarea.c
* VOID setbank() lkbank.c
* VOID setgbl() lkmain.c
* char * sprintf() c_library
* VOID symdef() lksym.c
* VOID usage() lkmain.c
* int fndidx() lkmain.c
*
* side effects:
* Completion of main() completes the linking process
* and may produce a map file (.map) and/or a linked
* data files (.ihx or .s19) and/or one or more
* relocated listing files (.rst).
*/
int
main(int argc, char *argv[])
{
int c, i, j, k;
if (intsiz() < 4) {
fprintf(stderr, "?ASlink-Error-Size of INT32 is not 32 bits or larger.\n\n");
exit(ER_FATAL);
}
/* sdas specific */
/* sdas initialization */
sdld_init(argv[0]);
/* use these defaults for parsing the .lnk script */
a_bytes = 4;
a_mask = 0xFFFFFFFF;
s_mask = 0x80000000;
v_mask = 0x7FFFFFFF;
/* end sdas specific */
if (!is_sdld())
fprintf(stdout, "\n");
startp = (struct lfile *) new (sizeof (struct lfile));
startp->f_idp = "";
pflag = 1;
for(i=1; i> %s\n", ip);
parse();
k++;
}
} else {
strcpy(ip, argv[i]);
if(pflag)
fprintf(stdout, "ASlink >> %s\n", ip);
parse();
}
}
if (linkp == NULL)
usage(ER_FATAL);
/*
* If no input file is specified
* then assume a single file with
* the same name as the output file.
*/
if (lfp == linkp) {
lfp->f_flp = (struct lfile *) new (sizeof (struct lfile));
lfp = lfp->f_flp;
lfp->f_idp = strsto(linkp->f_idp);
lfp->f_idx = fndidx(linkp->f_idp);
lfp->f_obj = objflg;
lfp->f_type = F_REL;
}
syminit();
#if SDCDB
/*
* Open SDCC Debug output file
*/
SDCDBfopen();
#endif
for (pass=0; pass<2; ++pass) {
cfp = NULL;
sfp = NULL;
filep = linkp->f_flp;
hp = NULL;
radix = 10;
/* sdld specific */
if (TARGET_IS_8051)
Areas51(); /*JCF: Create the default 8051 areas in the right order*/
/* end sdld specific */
while (nxtline()) {
ip = ib;
link_main();
}
if (pass == 0) {
/*
* Search libraries for global symbols
*/
search();
/* sdas specific */
/* use these defaults for parsing the .lk script */
a_bytes = 4;
a_mask = 0xFFFFFFFF;
s_mask = 0x80000000;
v_mask = 0x7FFFFFFF;
/* end sdas specific */
/*
* Set area base addresses.
*/
setarea();
/*
* Set bank base addresses.
*/
setbank();
/*
* Link all area addresses.
*/
if (!packflag)
lnkarea();
else {
/* sdld 8051 specific */
lnkarea2();
/* end sdld 8051 specific */
}
/*
* Check bank size limits.
*/
chkbank(stderr);
/*
* Process global definitions.
*/
setgbl();
/*
* Check for undefined globals.
*/
symdef(stderr);
#if NOICE
/*
* Open NoICE output file
*/
NoICEfopen();
#endif
/*
* Output Link Map.
*/
map();
/* sdld specific */
if (sflag) { /*JCF: memory usage summary output*/
if (!packflag) {
if (summary(areap)) lkexit(1);
}
else {
/* sdld 8051 specific */
if (summary2(areap)) lkexit(1);
/* end sdld 8051 specific */
}
}
if ((iram_size) && (!packflag))
iramcheck();
/* end sdld specific */
/*
* Open output file(s)
*/
lkfopen();
} else {
/*
* Link in library files
*/
library();
/*
* Complete Processing
*/
reloc('E');
}
}
if (TARGET_IS_PDK && get_sdld_target() != TARGET_ID_PDK) {
unsigned ram = 0;
unsigned rom = 0;
for (struct area *it = areap; it; it = it->a_ap) {
if (!strcmp(it->a_id, "DATA") ||
!strcmp(it->a_id, "OSEG")) {
if (it->a_addr + it->a_size > ram) {
ram = it->a_addr + it->a_size;
}
} else if (!strcmp(it->a_id, "CODE") ||
!strcmp(it->a_id, "CONST")) {
if (it->a_addr + it->a_size > rom) {
rom = it->a_addr + it->a_size;
}
}
}
enum sdld_target_e target = get_sdld_target();
const unsigned max_ram =
target == TARGET_ID_PDK13 ? 64 :
target == TARGET_ID_PDK14 ? 128 : 256;
const unsigned max_rom =
target == TARGET_ID_PDK13 ? 2048 :
target == TARGET_ID_PDK14 ? 4096 : 8192;
if (ram > max_ram) {
fprintf(stderr,
"?ASlink-Warning-"
"RAM value %u too large "
"(%uB max)\n", ram, max_ram);
}
if (rom > max_rom) {
fprintf(stderr,
"?ASlink-Warning-"
"ROM value %u too large "
"(%uW max)\n", rom / 2, max_rom / 2);
}
}
if (TARGET_IS_8051) {
//JCF:
CreateAOMF51();
}
lkexit(lkerr ? ER_ERROR : ER_NONE);
return(0);
}
/*)Function int intsiz()
*
* The function intsiz() returns the size of INT32
*
* local variables:
* none
*
* global variables:
* none
*
* functions called:
* none
*
* side effects:
* none
*/
int
intsiz()
{
return(sizeof(a_uint));
}
/*)Function VOID lkexit(i)
*
* int i exit code
*
* The function lkexit() explicitly closes all open
* files and then terminates the program.
*
* local variables:
* none
*
* global variables:
* FILE * jfp file handle for .noi
* FILE * mfp file handle for .map
* FILE * rfp file hanlde for .rst
* FILE * sfp file handle for .rel
* FILE * tfp file handle for .lst
*
* functions called:
* int fclose() c_library
* VOID exit() c_library
* VOID lkfclose() lkbank.c
*
* side effects:
* All files closed. Program terminates.
*/
VOID
lkexit(i)
int i;
{
lkfclose();
#if NOICE
if (jfp != NULL) fclose(jfp);
#endif
if (mfp != NULL) fclose(mfp);
if (rfp != NULL) fclose(rfp);
if (sfp != NULL) { if (sfp != stdin) fclose(sfp); }
if (tfp != NULL) fclose(tfp);
#if SDCDB
if (yfp != NULL) fclose(yfp);
#endif
exit(i);
}
/*)Function link_main()
*
* The function link_main() evaluates the directives for each line of
* text read from the .rel file(s). The valid directives processed
* are:
* X, D, Q, H, M, A, S, T, R, and P.
*
* local variables:
* int c first non blank character of a line
*
* global variables:
* head *headp The pointer to the first
* head structure of a linked list
* head *hp Pointer to the current
* head structure
* int a_bytes T Line address bytes
* int hilo Byte ordering
* int pass linker pass number
* int radix current number conversion radix
*
* functions called:
* char endline() lklex.c
* VOID module() lkhead.c
* VOID newarea() lkarea.c
* VOID newhead() lkhead.c
* sym * newsym() lksym.c
* VOID NoICEmagic() lknoice.c
* VOID reloc() lkreloc.c
*
* side effects:
* Head, area, and symbol structures are created and
* the radix is set as the .rel file(s) are read.
*/
VOID
link_main()
{
char c;
if ((c=endline()) == 0) { return; }
switch (c) {
/* sdld specific */
case 'O': /* For some important sdcc options */
if (is_sdld() && pass == 0) {
if (NULL == optsdcc) {
optsdcc = strsto(&ip[1]);
optsdcc_module = hp->m_id;
}
else {
if (strcmp(optsdcc, &ip[1]) != 0) {
fprintf(stderr,
"?ASlink-Warning-Conflicting sdcc options:\n"
" \"%s\" in module \"%s\" and\n"
" \"%s\" in module \"%s\".\n",
optsdcc, optsdcc_module, &ip[1], hp->m_id);
lkerr++;
}
}
}
break;
/* end sdld specific */
case 'X':
case 'D':
case 'Q':
ASxxxx_VERSION = 3;
a_bytes = 2; /* use default if unspecified */
hilo = 0; /* use default if unspecified */
if (c == 'X') { radix = 16; } else
if (c == 'D') { radix = 10; } else
if (c == 'Q') { radix = 8; }
while ((c = get()) != 0) {
switch(c) {
case 'H':
hilo = 1;
break;
case 'L':
hilo = 0;
break;
case '2':
a_bytes = 2;
break;
case '3':
a_bytes = 3;
break;
case '4':
a_bytes = 4;
break;
default:
break;
}
}
#ifdef LONGINT
switch(a_bytes) {
default:
a_bytes = 2;
case 2:
a_mask = 0x0000FFFFl;
s_mask = 0x00008000l;
v_mask = 0x00007FFFl;
break;
case 3:
a_mask = 0x00FFFFFFl;
s_mask = 0x00800000l;
v_mask = 0x007FFFFFl;
break;
case 4:
a_mask = 0xFFFFFFFFl;
s_mask = 0x80000000l;
v_mask = 0x7FFFFFFFl;
break;
}
#else
switch(a_bytes) {
default:
a_bytes = 2;
case 2:
a_mask = 0x0000FFFF;
s_mask = 0x00008000;
v_mask = 0x00007FFF;
break;
case 3:
a_mask = 0x00FFFFFF;
s_mask = 0x00800000;
v_mask = 0x007FFFFF;
break;
case 4:
a_mask = 0xFFFFFFFF;
s_mask = 0x80000000;
v_mask = 0x7FFFFFFF;
break;
}
#endif
break;
case 'H':
if (pass == 0) {
newhead();
} else {
if (hp == 0) {
hp = headp;
} else {
hp = hp->h_hp;
}
}
sdp.s_area = NULL;
sdp.s_areax = NULL;
sdp.s_addr = 0;
break;
case 'M':
if (pass == 0)
module();
break;
case 'A':
if (pass == 0)
newarea();
if (sdp.s_area == NULL) {
sdp.s_area = areap;
sdp.s_areax = areap->a_axp;
sdp.s_addr = 0;
}
break;
case 'S':
if (pass == 0)
newsym();
break;
case 'T':
case 'R':
case 'P':
if (pass == 0)
break;
reloc(c);
break;
#if NOICE
case ';':
unget(c);
NoICEmagic();
break;
#endif
default:
break;
}
}
/*)Function VOID map()
*
* The function map() opens the output map file and calls the various
* routines to
* (1) output the variables in each area,
* (2) list the files processed with module names,
* (3) list the libraries file processed,
* (4) list base address definitions,
* (5) list global variable definitions, and
* (6) list any undefined variables.
*
* local variables:
* int i counter
* head * hdp pointer to head structure
* lbfile *lbfh pointer to library file structure
*
* global variables:
* area *ap Pointer to the current
* area structure
* area *areap The pointer to the first
* area structure of a linked list
* base *basep The pointer to the first
* base structure
* base *bsp Pointer to the current
* base structure
* lfile *filep The pointer *filep points to the
* beginning of a linked list of
* lfile structures.
* globl *globlp The pointer to the first
* globl structure
* globl *gsp Pointer to the current
* globl structure
* head *headp The pointer to the first
* head structure of a linked list
* lbfile *lbfhead The pointer to the first
* lbfile structure of a linked list
* lfile *linkp pointer to first lfile structure
* containing an input REL file
* specification
* int lop current line number on page
* int mflag Map output flag
* FILE *mfp Map output file handle
* int page current page number
*
* functions called:
* FILE * afile() lkmain.c
* int fprintf() c_library
* VOID lkexit() lkmain.c
* VOID lstarea() lklist.c
* VOID newpag() lklist.c
* VOID chkbank() lkbank.c
* VOID symdef() lksym.c
*
* side effects:
* The map file is created.
*/
VOID
map(void)
{
int i;
struct head *hdp;
struct lbfile *lbfh;
if (mflag == 0) return;
/*
* Open Map File
*/
mfp = afile(linkp->f_idp, "map", 1);
if (mfp == NULL) {
lkexit(ER_FATAL);
}
/*
* Output Map Bank/Area Lists
*/
page = 0;
lop = NLPP;
for (bp = bankp; bp != NULL; bp = bp->b_bp) {
for (ap = areap; ap != NULL; ap = ap->a_ap) {
if (ap->a_bp == bp)
lstarea(ap, bp);
}
}
/*
* List Linked Files
*/
newpag(mfp);
fprintf(mfp, "\nFiles Linked [ module(s) ]\n\n");
hdp = headp;
filep = linkp->f_flp;
while (filep) {
if (strlen (filep->f_idp) > 40)
fprintf(mfp, "%s\n%40s [ ", filep->f_idp, "");
else
fprintf(mfp, "%-40.40s [ ", filep->f_idp);
i = 0;
while ((hdp != NULL) && (hdp->h_lfile == filep)) {
if (i)
fprintf(mfp, ",\n%44s", "");
fprintf(mfp, "%-.32s", hdp->m_id);
hdp = hdp->h_hp;
i++;
}
fprintf(mfp, " ]\n");
filep = filep->f_flp;
}
fprintf(mfp, "\n");
/*
* List Linked Libraries
*/
if (lbfhead != NULL) {
fprintf(mfp, "\nLibraries Linked [ object file ]\n\n");
for (lbfh=lbfhead; lbfh; lbfh=lbfh->next) {
if (strlen (lbfh->libspc) > 40)
fprintf(mfp, "%s\n%40s [ %-.32s ]\n",
lbfh->libspc, "", lbfh->relfil);
else
fprintf(mfp, "%-40.40s [ %-.32s ]\n",
lbfh->libspc, lbfh->relfil);
}
fprintf(mfp, "\n");
}
/*
* List Base Address Definitions
*/
if (basep) {
newpag(mfp);
fprintf(mfp, "\nUser Base Address Definitions\n\n");
bsp = basep;
while (bsp) {
fprintf(mfp, "%s\n", bsp->b_strp);
bsp = bsp->b_base;
}
}
/*
* List Global Definitions
*/
if (globlp) {
newpag(mfp);
fprintf(mfp, "\nUser Global Definitions\n\n");
gsp = globlp;
while (gsp) {
fprintf(mfp, "%s\n", gsp->g_strp);
gsp = gsp->g_globl;
}
}
fprintf(mfp, "\n\f");
chkbank(mfp);
symdef(mfp);
}
/*)Function int parse()
*
* The function parse() evaluates all command line or file input
* linker directives and updates the appropriate variables.
*
* local variables:
* int c character value
* int sv_type save type of processing
* char fid[] file id string
*
* global variables:
* char ctype[] array of character types, one per
* ASCII character
* lfile *lfp pointer to current lfile structure
* being processed by parse()
* lfile *linkp pointer to first lfile structure
* containing an input REL file
* specification
* int mflag Map output flag
* int oflag Output file type flag
* int objflg Linked file/library output object flag
* int pflag print linker command file flag
* FILE * stderr c_library
* int uflag Relocated listing flag
* int xflag Map file radix type flag
* int wflag Wide listing format
* int zflag Disable symbol case sensitivity
*
* Functions called:
* VOID addlib() lklibr.c
* VOID addpath() lklibr.c
* VOID bassav() lkmain.c
* VOID doparse() lkmain.c
* int fprintf() c_library
* VOID gblsav() lkmain.c
* VOID getfid() lklex.c
* int get() lklex.c
* int getnb() lklex.c
* VOID lkexit() lkmain.c
* char * strsto() lksym.c
* int strlen() c_library
* int fndidx() lkmain.c
*
* side effects:
* Various linker flags are updated and the linked
* structure lfile is created.
*/
int
parse()
{
int c;
int sv_type;
char fid[NINPUT];
while ((c = getnb()) != 0) {
/* sdld specific */
if ( c == ';')
return(0);
/* end sdld specific */
if ( c == '-') {
while (ctype[c=get()] & LETTER) {
switch(c) {
case 'C':
if (is_sdld() && !(TARGET_IS_Z80 || TARGET_IS_GB)) {
codesav();
return(0);
}
// else fall through
case 'c':
if (startp->f_type != 0)
break;
startp->f_type = F_STD;
doparse();
return(0);
case 'f':
case 'F':
if (startp->f_type == F_LNK)
return(0);
unget(getnb());
if (*ip == 0)
usage(ER_FATAL);
sv_type = startp->f_type;
startp->f_idp = strsto(ip);
startp->f_idx = fndidx(ip);
startp->f_type = F_LNK;
doparse();
if (sv_type == F_STD) {
cfp = NULL;
sfp = NULL;
startp->f_type = F_STD;
filep = startp;
}
return(0);
case 'I':
if (is_sdld() && !(TARGET_IS_Z80 || TARGET_IS_GB)) {
iramsav();
return(0);
}
// else fall through
case 'i':
oflag = 1;
break;
case 'S':
if (TARGET_IS_8051) {
unget(getnb());
if (ip && *ip)
{
stacksize = expr(0);
if (stacksize > 256) stacksize = 256;
else if (stacksize < 0) stacksize = 0;
}
return(0);
}
// else fall through
case 's':
oflag = 2;
break;
case 't':
case 'T':
oflag = 3;
break;
case 'o':
case 'O':
objflg = 0;
break;
case 'v':
case 'V':
objflg = 1;
break;
case 'M':
/*JCF: memory usage summary output*/
if (is_sdld()) {
sflag = 1;
break;
}
// else fall through
case 'm':
mflag = 1;
break;
#if NOICE
case 'j':
case 'J':
jflag = 1;
break;
#endif
case 'r':
case 'R':
if (is_sdld() && !(TARGET_IS_Z80 || TARGET_IS_GB))
rflag = 1;
else
goto err;
break;
case 'u':
case 'U':
uflag = 1;
break;
case 'X':
if (is_sdld() && !(TARGET_IS_Z80 || TARGET_IS_GB)) {
xramsav();
return(0);
}
// else fall through
case 'x':
xflag = 0;
break;
case 'q':
case 'Q':
xflag = 1;
break;
case 'd':
case 'D':
xflag = 2;
break;
case 'E':
if (TARGET_IS_6808 || TARGET_IS_STM8) {
oflag = 4;
break;
}
// else fall through
case 'e':
return(1);
case 'n':
case 'N':
pflag = 0;
break;
case 'p':
case 'P':
pflag = 1;
break;
case 'b':
case 'B':
bassav();
return(0);
case 'g':
case 'G':
gblsav();
return(0);
case 'k':
case 'K':
addpath();
return(0);
case 'l':
case 'L':
addlib();
return(0);
case 'w':
case 'W':
wflag = 1;
break;
#if SDCDB
case 'Y':
if (TARGET_IS_8051) {
unget(getnb());
packflag=1;
break;
}
// else fall through
case 'y':
yflag = 1;
break;
#endif
case 'z':
case 'Z':
zflag = 1;
break;
default:
err:
fprintf(stderr,
"Unknown option -%c ignored\n", c);
break;
}
}
/* sdld specific */
if ( c == ';')
return(0);
/* end sdld specific */
} else
if (!(ctype[c] & ILL)) {
if (linkp == NULL) {
linkp = (struct lfile *)
new (sizeof (struct lfile));
lfp = linkp;
lfp->f_type = F_OUT;
} else {
lfp->f_flp = (struct lfile *)
new (sizeof (struct lfile));
lfp = lfp->f_flp;
lfp->f_type = F_REL;
}
getfid(fid, c);
lfp->f_idp = strsto(fid);
lfp->f_obj = objflg;
} else {
fprintf(stderr, "Invalid input\n");
lkexit(ER_FATAL);
}
}
return(0);
}
/*)Function VOID doparse()
*
* The function doparse() evaluates all interactive
* command line or file input linker directives and
* updates the appropriate variables.
*
* local variables:
* none
*
* global variables:
* FILE * stdin standard input
* FILE * stdout standard output
* lfile *cfp The pointer *cfp points to the
* current lfile structure
* FILE *sfp The file handle sfp points to the
* currently open file
* char ib[NINPUT] .rel file text line
* char *ip pointer into the .rel file
* lfile *filep The pointer *filep points to the
* beginning of a linked list of
* lfile structures.
* lfile *startp asmlnk startup file structure
* int pflag print linker command file flag
*
* Functions called:
* int fclose() c_library
* int fprintf() c_library
* VOID getfid() lklex.c
* int nxtline() lklex.c
* int parse() lkmain.c
*
* side effects:
* Various linker flags are updated and the linked
* structure lfile may be updated.
*/
VOID
doparse()
{
cfp = NULL;
sfp = NULL;
filep = startp;
while (1) {
ip = ib;
if (nxtline() == 0)
break;
if (pflag && cfp->f_type != F_STD)
fprintf(stdout, "ASlink >> %s\n", ip);
if (*ip == 0 || parse())
break;
}
if((sfp != NULL) && (sfp != stdin)) {
fclose(sfp);
}
sfp = NULL;
startp->f_idp = "";
startp->f_idx = 0;
startp->f_type = 0;
}
/*)Function VOID bassav()
*
* The function bassav() creates a linked structure containing
* the base address strings input to the linker.
*
* local variables:
* none
*
* global variables:
* base *basep The pointer to the first
* base structure
* base *bsp Pointer to the current
* base structure
* char *ip pointer into the REL file
* text line in ib[]
*
* functions called:
* int getnb() lklex.c
* VOID * new() lksym.c
* int strlen() c_library
* char * strcpy() c_library
* VOID unget() lklex.c
*
* side effects:
* The basep structure is created.
*/
VOID
bassav()
{
if (basep == NULL) {
basep = (struct base *)
new (sizeof (struct base));
bsp = basep;
} else {
bsp->b_base = (struct base *)
new (sizeof (struct base));
bsp = bsp->b_base;
}
unget(getnb());
bsp->b_strp = (char *) new (strlen(ip)+1);
strcpy(bsp->b_strp, ip);
}
/*)Function VOID gblsav()
*
* The function gblsav() creates a linked structure containing
* the global variable strings input to the linker.
*
* local variable:
* none
*
* global variables:
* globl *globlp The pointer to the first
* globl structure
* globl *gsp Pointer to the current
* globl structure
* char *ip pointer into the REL file
* text line in ib[]
* int lkerr error flag
*
* functions called:
* int getnb() lklex.c
* VOID * new() lksym.c
* int strlen() c_library
* char * strcpy() c_library
* VOID unget() lklex.c
*
* side effects:
* The globlp structure is created.
*/
VOID
gblsav()
{
if (globlp == NULL) {
globlp = (struct globl *)
new (sizeof (struct globl));
gsp = globlp;
} else {
gsp->g_globl = (struct globl *)
new (sizeof (struct globl));
gsp = gsp->g_globl;
}
unget(getnb());
gsp->g_strp = (char *) new (strlen(ip)+1);
strcpy(gsp->g_strp, ip);
}
/*)Function VOID setgbl()
*
* The function setgbl() scans the global variable lines in the
* globlp structure, evaluates the arguments, and sets a variable
* to this value.
*
* local variables:
* int v expression value
* char id[] base id string
* sym * sp pointer to a symbol structure
*
* global variables:
* char *ip pointer into the REL file
* text line in ib[]
* globl *globlp The pointer to the first
* globl structure
* globl *gsp Pointer to the current
* globl structure
* FILE * stderr c_library
* int lkerr error flag
*
* functions called:
* a_uint expr() lkeval.c
* int fprintf() c_library
* VOID getid() lklex.c
* int getnb() lklex.c
* sym * lkpsym() lksym.c
*
* side effects:
* The value of a variable is set.
*/
VOID
setgbl()
{
int v;
struct sym *sp;
char id[NCPS];
gsp = globlp;
while (gsp) {
ip = gsp->g_strp;
getid(id, -1);
if (getnb() == '=') {
v = (int) expr(0);
sp = lkpsym(id, 0);
if (sp == NULL) {
fprintf(stderr,
"No definition of symbol %s\n", id);
lkerr++;
} else {
if (sp->s_type & S_DEF) {
fprintf(stderr,
"Redefinition of symbol %s\n", id);
lkerr++;
sp->s_axp = NULL;
}
sp->s_addr = v;
sp->s_type |= S_DEF;
}
} else {
fprintf(stderr, "No '=' in global expression");
lkerr++;
}
gsp = gsp->g_globl;
}
}
/*)Function FILE * afile(fn, ft, wf)
*
* char * fn file specification string
* char * ft file type string
* int wf 0 ==>> read
* 1 ==>> write
* 2 ==>> binary write
*
* The function afile() opens a file for reading or writing.
* (1) If the file type specification string ft
* is not NULL then a file specification is
* constructed with the file path\name in fn
* and the extension in ft.
* (2) If the file type specification string ft
* is NULL then the file specification is
* constructed from fn. If fn does not have
* a file type then the default .rel file
* type is appended to the file specification.
*
* afile() returns a file handle for the opened file or aborts
* the assembler on an open error.
*
* local variables:
* int c character value
* FILE * fp filehandle for opened file
* char * p1 pointer to filespec string fn
* char * p2 pointer to filespec string fb
* char * p3 pointer to filetype string ft
*
* global variables:
* char afspec[] constructed file specification string
* int lkerr error flag
*
* functions called:
* int fndidx() lkmain.c
* FILE * fopen() c_library
* int fprintf() c_library
*
* side effects:
* File is opened for read or write.
*/
FILE *
afile(char *fn, char *ft, int wf)
{
char *p1, *p2;
int c;
char * frmt;
FILE *fp;
if (strlen(fn) > (FILSPC-7)) {
fprintf(stderr, "?ASlink-Error- : \"%s\"\n", fn);
lkerr++;
return(NULL);
}
/*
* Skip The Path
*/
strcpy(afspec, fn);
c = fndidx(afspec);
/*
* Skip to File Extension separator
*/
p1 = strrchr(&afspec[c], FSEPX);
/*
* Copy File Extension
*/
p2 = ft ? ft : "";
if (*p2 == 0) {
if (p1 == NULL) {
p2 = LKOBJEXT;
} else {
p2 = strrchr(&fn[c], FSEPX) + 1;
}
}
if (p1 == NULL) {
p1 = &afspec[strlen(afspec)];
}
*p1++ = FSEPX;
while ((c = *p2++) != 0) {
if (p1 < &afspec[FILSPC-1])
*p1++ = c;
}
*p1++ = 0;
/*
* Select Read/Write/Binary Write
*/
switch(wf) {
default:
case 0: frmt = "r"; break;
case 1: frmt = "w"; break;
#ifdef DECUS
case 2: frmt = "wn"; break;
#else
case 2: frmt = "wb"; break;
#endif
}
if ((fp = fopen(afspec, frmt)) == NULL && strcmp(ft,"adb") != 0) { /* Do not complain for optional adb files */
fprintf(stderr, "?ASlink-Error- : \"%s\"\n", wf?"create":"open", afspec);
lkerr++;
}
return (fp);
}
/*)Function int fndidx(str)
*
* char * str file specification string
*
* The function fndidx() scans the file specification string
* to find the index to the file name. If the file
* specification contains a 'path' then the index will
* be non zero.
*
* fndidx() returns the index value.
*
* local variables:
* char * p1 temporary pointer
* char * p2 temporary pointer
*
* global variables:
* none
*
* functions called:
* char * strrchr() c_library
*
* side effects:
* none
*/
int
fndidx(str)
char *str;
{
char *p1, *p2;
/*
* Skip Path Delimiters
*/
p1 = str;
if ((p2 = strrchr(p1, ':')) != NULL) { p1 = p2 + 1; }
if ((p2 = strrchr(p1, '/')) != NULL) { p1 = p2 + 1; }
if ((p2 = strrchr(p1, '\\')) != NULL) { p1 = p2 + 1; }
return((int) (p1 - str));
}
/*)Function int fndext(str)
*
* char * str file specification string
*
* The function fndext() scans the file specification string
* to find the file.ext separater.
*
* fndext() returns the index to FSEPX or the end of the string.
*
* local variables:
* char * p1 temporary pointer
* char * p2 temporary pointer
*
* global variables:
* none
*
* functions called:
* char * strrchr() c_library
*
* side effects:
* none
*/
int
fndext(str)
char * str;
{
char *p1, *p2;
/*
* Find the file separator
*/
p1 = str + strlen(str);
if ((p2 = strrchr(str, FSEPX)) != NULL) { p1 = p2; }
return((int) (p1 - str));
}
/* sdld specific */
/*)Function VOID iramsav()
*
* The function iramsav() stores the size of the chip's internal RAM.
* This is used after linking to check that variable assignment to this
* dataspace didn't overflow into adjoining segments. Variables in the
* DSEG, OSEG, and ISEG are assigned to this dataspace.
*
* local variables:
* none
*
* global variables:
* char *ip pointer into the REL file
* text line in ib[]
* unsigned int size of chip's internal
* iram_size RAM segment
*
* functions called:
* int getnb() lklex.c
* VOID unget() lklex.c
* a_uint expr() lkeval.c
*
* side effects:
* The iram_size may be modified.
*/
VOID
iramsav()
{
unget(getnb());
if (ip && *ip)
iram_size = expr(0); /* evaluate size expression */
else
iram_size = 128; /* Default is 128 (0x80) bytes */
if ((iram_size<=0) || (iram_size>256))
iram_size = 128; /* Default is 128 (0x80) bytes */
}
/*Similar to iramsav but for xram memory*/
VOID
xramsav()
{
unget(getnb());
if (ip && *ip)
xram_size = expr(0); /* evaluate size expression */
else
xram_size = rflag?0x1000000:0x10000;
}
/*Similar to iramsav but for code memory*/
VOID
codesav()
{
unget(getnb());
if (ip && *ip)
code_size = expr(0); /* evaluate size expression */
else
code_size = rflag?0x1000000:0x10000;
}
/*)Function VOID iramcheck()
*
* The function iramcheck() is used at the end of linking to check that
* the internal RAM area wasn't overflowed by too many variable
* assignments. Variables in the DSEG, ISEG, and OSEG are assigned to
* the chip's internal RAM.
*
* local variables:
* none
*
* global variables:
* unsigned int size of chip's internal
* iram_size RAM segment
* struct area linked list of memory
* *areap areas
*
* functions called:
*
* side effects:
*/
VOID
iramcheck()
{
register unsigned int last_addr;
register struct area *ap;
for (ap = areap; ap; ap=ap->a_ap) {
if ((ap->a_size != 0) &&
(!strcmp(ap->a_id, "DSEG") ||
!strcmp(ap->a_id, "OSEG") ||
!strcmp(ap->a_id, "ISEG")
)
)
{
last_addr = ap->a_addr + ap->a_size - 1;
if (last_addr >= iram_size)
fprintf(stderr,
"\nWARNING! Segment %s extends past the end\n"
" of internal RAM. Check map file.\n",
ap->a_id);
}
}
}
/* end sdld specific */
char *usetxt[] = {
"Usage: [-Options] [-Option with arg] file",
"Usage: [-Options] [-Option with arg] outfile file1 [file2 ...]",
"Startup:",
" -p Echo commands to stdout (default)",
" -n No echo of commands to stdout",
"Alternates to Command Line Input:",
" -c ASlink >> prompt input",
" -f file[.lk] Command File input",
"Libraries:",
" -k Library path specification, one per -k",
" -l Library file specification, one per -l",
"Relocation:",
" -b area base address = expression",
" -g global symbol = expression",
"Map format:",
" -m Map output generated as (out)file[.map]",
" -w Wide listing format for map file",
" -x Hexadecimal (default)",
" -d Decimal",
" -q Octal",
"Output:",
" -i Intel Hex as (out)file[.ihx]",
" -s Motorola S Record as (out)file[.s19]",
// " -t Tandy CoCo Disk BASIC binary as (out)file[.bi-]",
#if NOICE
" -j NoICE Debug output as (out)file[.noi]",
#endif
#if SDCDB
" -y SDCDB Debug output as (out)file[.cdb]",
#endif
// " -o Linked file/library object output enable (default)",
// " -v Linked file/library object output disable",
"List:",
" -u Update listing file(s) with link data as file(s)[.rst]",
"Case Sensitivity:",
" -z Disable Case Sensitivity for Symbols",
"End:",
" -e or null line terminates input",
"",
0
};
char *usetxt_8051[] = {
"Usage: [-Options] [-Option with arg] file",
"Usage: [-Options] [-Option with arg] outfile file1 [file2 ...]",
"Startup:",
" -p Echo commands to stdout (default)",
" -n No echo of commands to stdout",
"Alternates to Command Line Input:",
" -c ASlink >> prompt input",
" -f file[.lk] Command File input",
"Libraries:",
" -k Library path specification, one per -k",
" -l Library file specification, one per -l",
"Relocation:",
" -b area base address = expression",
" -g global symbol = expression",
"Map format:",
" -m Map output generated as (out)file[.map]",
" -w Wide listing format for map file",
" -x Hexadecimal (default)",
" -d Decimal",
" -q Octal",
"Output:",
" -i Intel Hex as (out)file[.ihx]",
" -s Motorola S Record as (out)file[.s19]",
#if NOICE
" -j NoICE Debug output as (out)file[.noi]",
#endif
#if SDCDB
" -y SDCDB Debug output as (out)file[.cdb]",
#endif
"List:",
" -u Update listing file(s) with link data as file(s)[.rst]",
"Case Sensitivity:",
" -z Disable Case Sensitivity for Symbols",
"Miscellaneous:\n"
" -I [iram-size] Check for internal RAM overflow",
" -X [xram-size] Check for external RAM overflow",
" -C [code-size] Check for code overflow",
" -M Generate memory usage summary file[.mem]",
" -Y Pack internal ram",
" -S [stack-size] Allocate space for stack",
"End:",
" -e or null line terminates input",
"",
0
};
char *usetxt_6808[] = {
"Usage: [-Options] [-Option with arg] file",
"Usage: [-Options] [-Option with arg] outfile file1 [file2 ...]",
"Startup:",
" -p Echo commands to stdout (default)",
" -n No echo of commands to stdout",
"Alternates to Command Line Input:",
" -c ASlink >> prompt input",
" -f file[.lk] Command File input",
"Libraries:",
" -k Library path specification, one per -k",
" -l Library file specification, one per -l",
"Relocation:",
" -b area base address = expression",
" -g global symbol = expression",
"Map format:",
" -m Map output generated as (out)file[.map]",
" -w Wide listing format for map file",
" -x Hexadecimal (default)",
" -d Decimal",
" -q Octal",
"Output:",
" -i Intel Hex as (out)file[.ihx]",
" -s Motorola S Record as (out)file[.s19]",
" -E ELF executable as file[.elf]",
#if NOICE
" -j NoICE Debug output as (out)file[.noi]",
#endif
#if SDCDB
" -y SDCDB Debug output as (out)file[.cdb]",
#endif
"List:",
" -u Update listing file(s) with link data as file(s)[.rst]",
"Case Sensitivity:",
" -z Disable Case Sensitivity for Symbols",
"Miscellaneous:\n"
" -I [iram-size] Check for internal RAM overflow",
" -X [xram-size] Check for external RAM overflow",
" -C [code-size] Check for code overflow",
" -M Generate memory usage summary file[.mem]",
"End:",
" -e or null line terminates input",
"",
0
};
char *usetxt_z80_gb[] = {
"Usage: [-Options] [-Option with arg] file",
"Usage: [-Options] [-Option with arg] outfile file1 [file2 ...]",
"Startup:",
" -p Echo commands to stdout (default)",
" -n No echo of commands to stdout",
"Alternates to Command Line Input:",
" -c ASlink >> prompt input",
" -f file[.lk] Command File input",
"Libraries:",
" -k Library path specification, one per -k",
" -l Library file specification, one per -l",
"Relocation:",
" -b area base address = expression",
" -g global symbol = expression",
"Map format:",
" -m Map output generated as (out)file[.map]",
" -w Wide listing format for map file",
" -x Hexadecimal (default)",
" -d Decimal",
" -q Octal",
"Output:",
" -i Intel Hex as (out)file[.ihx]",
" -s Motorola S Record as (out)file[.s19]",
#if SDCDB
" -y SDCDB Debug output as (out)file[.cdb]",
#endif
"List:",
" -u Update listing file(s) with link data as file(s)[.rst]",
"Case Sensitivity:",
" -z Disable Case Sensitivity for Symbols",
"End:",
" -e or null line terminates input",
"",
0
};
/*)Function VOID usage(n)
*
* int n exit code
*
* The function usage() outputs to the stderr device the
* linker name and version and a list of valid linker options.
*
* local variables:
* char ** dp pointer to an array of
* text string pointers.
*
* global variables:
* FILE * stderr c_library
*
* functions called:
* int fprintf() c_library
*
* side effects:
* none
*/
VOID
usage(int n)
{
char **dp;
/* sdld specific */
fprintf(stderr, "\n%s Linker %s\n\n", is_sdld() ? "sdld" : "ASxxxx", VERSION);
for (dp = TARGET_IS_8051 ? usetxt_8051 : (TARGET_IS_6808 ? usetxt_6808 : ((TARGET_IS_Z80 || TARGET_IS_GB) ? usetxt_z80_gb : usetxt)); *dp; dp++)
fprintf(stderr, "%s\n", *dp);
/* end sdld specific */
lkexit(n);
}
/*)Function VOID copyfile()
*
* FILE *dest destination file
* FILE *src source file
*
* function will copy source file to destination file
*
*
* functions called:
* int fgetc() c_library
* int fputc() c_library
*
* side effects:
* none
*/
VOID
copyfile (FILE *dest, FILE *src)
{
int ch;
while ((ch = fgetc(src)) != EOF) {
fputc(ch,dest);
}
}