1412 lines
28 KiB
C
1412 lines
28 KiB
C
/* lkmain.c */
|
|
/*
|
|
* (C) Copyright 1989-1995
|
|
* All Rights Reserved
|
|
*
|
|
* Alan R. Baldwin
|
|
* 721 Berkeley St.
|
|
* Kent, Ohio 44240
|
|
*/
|
|
|
|
/*
|
|
* Extensions: P. Felber
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
//#include <alloc.h>
|
|
#include "aslink.h"
|
|
#include <stdlib.h>
|
|
|
|
#ifndef SDK_VERSION_STRING
|
|
#define SDK_VERSION_STRING "3.0.0"
|
|
#endif
|
|
#ifndef TARGET_STRING
|
|
#define TARGET_STRING "gbz80"
|
|
#endif
|
|
|
|
/*)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(fn,ft,wf)
|
|
* VOID bassav()
|
|
* VOID gblsav()
|
|
* VOID link()
|
|
* VOID lkexit()
|
|
* VOID main(argc,argv)
|
|
* VOID map()
|
|
* int parse()
|
|
* VOID setbas()
|
|
* VOID setgbl()
|
|
* VOID usage()
|
|
*
|
|
* lkmain.c contains the following local variables:
|
|
* char * usetext[] array of pointers to the
|
|
* command option tect lines
|
|
*
|
|
*/
|
|
|
|
/*)Function VOID 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 functiond getline_() 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 setbase(),
|
|
* 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 four supported formats.
|
|
*
|
|
* local variables:
|
|
* char * p pointer to an argument string
|
|
* int c character from argument string
|
|
* int i 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 mflag Map output flag
|
|
* int oflag Output file type flag
|
|
* FILE *ofp Output file handle
|
|
* for word formats
|
|
* FILE *ofph Output file handle
|
|
* for high byte format
|
|
* FILE *ofpl Output file handle
|
|
* for low byte format
|
|
* 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 asmlnk startup file structure
|
|
* FILE * stdin c_library
|
|
* FILE * stdout c_library
|
|
*
|
|
* functions called:
|
|
* FILE * afile() lkmain.c
|
|
* int fclose() c_library
|
|
* int fprintf() c_library
|
|
* int getline_() lklex.c
|
|
* VOID library() lklibr.c
|
|
* VOID link() lkmain.c
|
|
* VOID lkexit() lkmain.c
|
|
* VOID lnkarea() lkarea.c
|
|
* VOID map() lkmain.c
|
|
* VOID new() lksym.c
|
|
* int parse() lkmain.c
|
|
* VOID reloc() lkreloc.c
|
|
* VOID search() lklibr.c
|
|
* VOID setbas() lkmain.c
|
|
* VOID setgbl() lkmain.c
|
|
* VOID symdef() lksym.c
|
|
* VOID usage() 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).
|
|
*/
|
|
|
|
#ifdef SDK
|
|
int binary = 0;
|
|
#endif /* SDK */
|
|
#ifdef GAMEBOY
|
|
char *default_basep[] = {
|
|
"_CODE=0x0200",
|
|
"_DATA=0xC0A0",
|
|
NULL
|
|
};
|
|
|
|
char *default_globlp[] = {
|
|
/* DMA transfer must start at multiples of 0x100 */
|
|
".OAM=0xC000",
|
|
".STACK=0xE000",
|
|
".refresh_OAM=0xFF80",
|
|
|
|
".init=0x0000",
|
|
|
|
NULL
|
|
};
|
|
#endif /* GAMEBOY */
|
|
|
|
int
|
|
main(argc, argv)
|
|
char *argv[];
|
|
{
|
|
register char *p;
|
|
register int c, i;
|
|
|
|
#ifdef GAMEBOY
|
|
nb_rom_banks = 2;
|
|
nb_ram_banks = 0;
|
|
mbc_type = 0;
|
|
symflag=0;
|
|
|
|
for(i = 0; default_basep[i] != NULL; i++) {
|
|
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;
|
|
}
|
|
bsp->b_strp = default_basep[i];
|
|
}
|
|
for(i = 0; default_globlp[i] != NULL; i++) {
|
|
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;
|
|
}
|
|
gsp->g_strp = default_globlp[i];
|
|
}
|
|
#endif /* GAMEBOY */
|
|
#ifndef SDK
|
|
fprintf(stdout, "\n");
|
|
#endif /* SDK */
|
|
|
|
startp = (struct lfile *) new (sizeof (struct lfile));
|
|
|
|
pflag = 1;
|
|
for (i=1; i<argc; ++i) {
|
|
p = argv[i];
|
|
if (*p == '-') {
|
|
while (ctype[c = *(++p)] & LETTER) {
|
|
switch(c) {
|
|
|
|
case 'c':
|
|
case 'C':
|
|
startp->f_type = F_STD;
|
|
break;
|
|
|
|
case 'f':
|
|
case 'F':
|
|
startp->f_type = F_LNK;
|
|
break;
|
|
|
|
case 'n':
|
|
case 'N':
|
|
pflag = 0;
|
|
break;
|
|
|
|
case 'p':
|
|
case 'P':
|
|
pflag = 1;
|
|
break;
|
|
|
|
default:
|
|
usage();
|
|
}
|
|
}
|
|
|
|
#ifdef SDK
|
|
if(c == '-') {
|
|
startp->f_type = F_CMD;
|
|
startp->f_idp = (char *)&argv[i+1];
|
|
break;
|
|
}
|
|
#endif /* SDK */
|
|
|
|
} else {
|
|
if (startp->f_type == F_LNK) {
|
|
startp->f_idp = p;
|
|
}
|
|
}
|
|
}
|
|
if (startp->f_type == F_INV)
|
|
usage();
|
|
if (startp->f_type == F_LNK && startp->f_idp == NULL)
|
|
usage();
|
|
#ifdef SDK
|
|
if (startp->f_type == F_CMD && startp->f_idp == NULL)
|
|
usage();
|
|
#endif /* SDK */
|
|
|
|
cfp = NULL;
|
|
sfp = NULL;
|
|
filep = startp;
|
|
while (1) {
|
|
ip = ib;
|
|
if (getline_() == 0)
|
|
break;
|
|
if (pflag && sfp != stdin)
|
|
fprintf(stdout, "%s\n", ip);
|
|
if (*ip == '\0' || parse())
|
|
break;
|
|
}
|
|
if (sfp)
|
|
fclose(sfp);
|
|
if (linkp == NULL)
|
|
usage();
|
|
#ifdef SDK
|
|
if (linkp->f_flp == NULL)
|
|
usage();
|
|
#endif /* SDK */
|
|
|
|
#ifdef GAMEBOY
|
|
for(i = 1; i < nb_rom_banks; i++) {
|
|
bsp->b_base = (struct base *)new(sizeof(struct base));
|
|
bsp = bsp->b_base;
|
|
bsp->b_strp = (char *)malloc(18);
|
|
sprintf(bsp->b_strp, "_CODE_%d=0x4000", i);
|
|
}
|
|
for(i = 0; i < nb_ram_banks; i++) {
|
|
bsp->b_base = (struct base *)new(sizeof(struct base));
|
|
bsp = bsp->b_base;
|
|
bsp->b_strp = (char *)malloc(18);
|
|
sprintf(bsp->b_strp, "_DATA_%d=0xA000", i);
|
|
}
|
|
#endif /* GAMEBOY */
|
|
|
|
syminit();
|
|
for (pass=0; pass<2; ++pass) {
|
|
cfp = NULL;
|
|
sfp = NULL;
|
|
#ifdef SDK
|
|
filep = linkp->f_flp;
|
|
#else /* SDK */
|
|
filep = linkp;
|
|
#endif /* SDK */
|
|
hp = NULL;
|
|
radix = 10;
|
|
|
|
while (getline_()) {
|
|
ip = ib;
|
|
link();
|
|
}
|
|
if (pass == 0) {
|
|
/*
|
|
* Search libraries for global symbols
|
|
*/
|
|
search();
|
|
/*
|
|
* Set area base addresses.
|
|
*/
|
|
setbas();
|
|
/*
|
|
* Link all area addresses.
|
|
*/
|
|
lnkarea();
|
|
/*
|
|
* Process global definitions.
|
|
*/
|
|
setgbl();
|
|
/*
|
|
* Check for undefined globals.
|
|
*/
|
|
symdef(stderr);
|
|
#ifdef SDK
|
|
if (symflag)
|
|
sym();
|
|
#endif
|
|
/*
|
|
* Output Link Map.
|
|
*/
|
|
if (mflag)
|
|
map();
|
|
/*
|
|
* Open output file
|
|
*/
|
|
if (oflag == 1) {
|
|
#ifdef SDK
|
|
ofp = afile(linkp->f_idp, "ihx", 1);
|
|
#else /* SDK */
|
|
ofp = afile(linkp->f_idp, "IHX", 1);
|
|
#endif /* SDK */
|
|
if (ofp == NULL) {
|
|
lkexit(1);
|
|
}
|
|
} else
|
|
if (oflag == 2) {
|
|
#ifdef SDK
|
|
ofp = afile(linkp->f_idp, "s19", 1);
|
|
#else /* SDK */
|
|
ofp = afile(linkp->f_idp, "S19", 1);
|
|
#endif /* SDK */
|
|
if (ofp == NULL) {
|
|
lkexit(1);
|
|
}
|
|
#ifdef SDK
|
|
} else
|
|
if (oflag == 3) {
|
|
binary = 1;
|
|
ofp = afile(linkp->f_idp, "", 1);
|
|
binary = 0;
|
|
if (ofp == NULL) {
|
|
lkexit(1);
|
|
}
|
|
#endif /* SDK */
|
|
}
|
|
} else {
|
|
/*
|
|
* Link in library files
|
|
*/
|
|
library();
|
|
reloc('E');
|
|
}
|
|
}
|
|
lkexit(lkerr);
|
|
|
|
/* Never get here. */
|
|
return 0;
|
|
}
|
|
|
|
/*)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 * mfp file handle for .map
|
|
* FILE * ofp file handle for .ihx/.s19
|
|
* 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
|
|
*
|
|
* side effects:
|
|
* All files closed. Program terminates.
|
|
*/
|
|
|
|
VOID
|
|
lkexit(i)
|
|
int i;
|
|
{
|
|
if (mfp != NULL) fclose(mfp);
|
|
if (ofp != NULL) fclose(ofp);
|
|
if (rfp != NULL) fclose(rfp);
|
|
if (sfp != NULL) fclose(sfp);
|
|
if (tfp != NULL) fclose(tfp);
|
|
exit(i);
|
|
}
|
|
|
|
/*)Function link()
|
|
*
|
|
* The function link() 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 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 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()
|
|
{
|
|
register int c;
|
|
|
|
if ((c=endline()) == 0) { return; }
|
|
switch (c) {
|
|
|
|
case 'X':
|
|
radix = 16;
|
|
break;
|
|
|
|
case 'D':
|
|
radix = 10;
|
|
break;
|
|
|
|
case 'Q':
|
|
radix = 8;
|
|
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;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
if (c == 'X' || c == 'D' || c == 'Q') {
|
|
if ((c = get()) == 'H') {
|
|
hilo = 1;
|
|
} else
|
|
if (c == 'L') {
|
|
hilo = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*)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
|
|
* 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 symdef() lksym.c
|
|
*
|
|
* side effects:
|
|
* The map file is created.
|
|
*/
|
|
|
|
#ifndef MLH_MAP
|
|
VOID
|
|
map()
|
|
{
|
|
register i;
|
|
register struct head *hdp;
|
|
register struct lbfile *lbfh;
|
|
|
|
/*
|
|
* Open Map File
|
|
*/
|
|
#ifdef SDK
|
|
mfp = afile(linkp->f_idp, "map", 1);
|
|
#else /* SDK */
|
|
mfp = afile(linkp->f_idp, "MAP", 1);
|
|
#endif /* SDK */
|
|
if (mfp == NULL) {
|
|
lkexit(1);
|
|
}
|
|
|
|
/*
|
|
* Output Map Area Lists
|
|
*/
|
|
page = 0;
|
|
lop = NLPP;
|
|
ap = areap;
|
|
while (ap) {
|
|
lstarea(ap);
|
|
ap = ap->a_ap;
|
|
}
|
|
/*
|
|
* List Linked Files
|
|
*/
|
|
newpag(mfp);
|
|
fprintf(mfp, "\nFiles Linked [ module(s) ]\n\n");
|
|
hdp = headp;
|
|
#ifdef SDK
|
|
filep = linkp->f_flp;
|
|
#else /* SDK */
|
|
filep = linkp;
|
|
#endif /* SDK */
|
|
while (filep) {
|
|
fprintf(mfp, "%-16s", filep->f_idp);
|
|
i = 0;
|
|
while ((hdp != NULL) && (hdp->h_lfile == filep)) {
|
|
if (i % 5) {
|
|
fprintf(mfp, ", %8.8s", hdp->m_id);
|
|
} else {
|
|
if (i) {
|
|
fprintf(mfp, ",\n%20s%8.8s", "", hdp->m_id);
|
|
} else {
|
|
fprintf(mfp, " [ %8.8s", hdp->m_id);
|
|
}
|
|
}
|
|
hdp = hdp->h_hp;
|
|
i++;
|
|
}
|
|
if (i)
|
|
fprintf(mfp, " ]");
|
|
fprintf(mfp, "\n");
|
|
filep = filep->f_flp;
|
|
}
|
|
/*
|
|
* List Linked Libraries
|
|
*/
|
|
if (lbfhead != NULL) {
|
|
fprintf(mfp,
|
|
"\nLibraries Linked [ object file ]\n\n");
|
|
for (lbfh=lbfhead; lbfh; lbfh=lbfh->next) {
|
|
fprintf(mfp, "%-32s [ %16.16s ]\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");
|
|
symdef(mfp);
|
|
}
|
|
#else
|
|
VOID map()
|
|
{
|
|
register struct head *hdp;
|
|
register struct lbfile *lbfh;
|
|
|
|
/*
|
|
* Open Map File
|
|
*/
|
|
#ifdef SDK
|
|
mfp = afile(linkp->f_idp, "map", 1);
|
|
#else /* SDK */
|
|
mfp = afile(linkp->f_idp, "MAP", 1);
|
|
#endif /* SDK */
|
|
if (mfp == NULL) {
|
|
lkexit(1);
|
|
}
|
|
|
|
/*
|
|
*Output Map Area Lists
|
|
*/
|
|
page = 0;
|
|
lop = NLPP;
|
|
ap = areap;
|
|
while (ap) {
|
|
lstarea(ap);
|
|
ap = ap->a_ap;
|
|
}
|
|
/*
|
|
* List Linked Files
|
|
*/
|
|
hdp = headp;
|
|
#ifdef SDK
|
|
filep = linkp->f_flp;
|
|
#else /* SDK */
|
|
filep = linkp;
|
|
#endif /* SDK */
|
|
if (filep) {
|
|
fprintf( mfp, "MODULES\n");
|
|
}
|
|
while (filep) {
|
|
fprintf(mfp, "\tFILE %s\n", filep->f_idp);
|
|
while ((hdp != NULL) && (hdp->h_lfile == filep)) {
|
|
if (strlen(hdp->m_id)>0)
|
|
fprintf(mfp, "\t\tNAME %s\n", hdp->m_id);
|
|
hdp = hdp->h_hp;
|
|
}
|
|
filep = filep->f_flp;
|
|
}
|
|
/*
|
|
* List Linked Libraries
|
|
*/
|
|
if (lbfhead != NULL) {
|
|
fprintf(mfp, "LIBRARIES\n");
|
|
for (lbfh=lbfhead; lbfh; lbfh=lbfh->next) {
|
|
fprintf(mfp, "\tLIBRARY %s\n"
|
|
"\t\tMODULE %s\n",
|
|
lbfh->libspc, lbfh->relfil);
|
|
}
|
|
}
|
|
/*
|
|
* List Base Address Definitions
|
|
*/
|
|
if (basep) {
|
|
fprintf(mfp, "USERBASEDEF\n");
|
|
bsp = basep;
|
|
while (bsp) {
|
|
fprintf(mfp, "\t%s\n", bsp->b_strp);
|
|
bsp = bsp->b_base;
|
|
}
|
|
}
|
|
/*
|
|
* List Global Definitions
|
|
*/
|
|
if (globlp) {
|
|
fprintf(mfp, "USERGLOBALDEF\n");
|
|
gsp = globlp;
|
|
while (gsp) {
|
|
fprintf(mfp, "\t%s\n", gsp->g_strp);
|
|
gsp = gsp->g_globl;
|
|
}
|
|
}
|
|
symdef(mfp);
|
|
#ifdef SDK
|
|
if (mfp!=NULL) {
|
|
fclose(mfp);
|
|
mfp = NULL;
|
|
}
|
|
#endif
|
|
}
|
|
#endif /* MLH_MAP */
|
|
|
|
#ifdef SDK
|
|
/* PENDING */
|
|
VOID lstareatosym(struct area *xp);
|
|
|
|
VOID sym()
|
|
{
|
|
/*
|
|
* Open sym File
|
|
*/
|
|
mfp = afile(linkp->f_idp, "sym", 1);
|
|
if (mfp == NULL) {
|
|
lkexit(1);
|
|
}
|
|
fprintf( mfp, "; no$gmb format .sym file\n"
|
|
"; Generated automagically by ASxxxx linker %s (SDK " SDK_VERSION_STRING ")\n"
|
|
, VERSION );
|
|
/*
|
|
* Output sym Area Lists
|
|
*/
|
|
page = 0;
|
|
lop = NLPP;
|
|
ap = areap;
|
|
while (ap) {
|
|
lstareatosym(ap);
|
|
ap = ap->a_ap;
|
|
}
|
|
if (mfp!=NULL) {
|
|
fclose(mfp);
|
|
mfp = NULL;
|
|
}
|
|
}
|
|
#endif /* SDK */
|
|
|
|
/*)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
|
|
* 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 pflag print linker command file flag
|
|
* FILE * stderr c_library
|
|
* int uflag Relocated listing flag
|
|
* int xflag Map file radix type flag
|
|
*
|
|
* Functions called:
|
|
* VOID addlib() lklibr.c
|
|
* VOID addpath() lklibr.c
|
|
* VOID bassav() lkmain.c
|
|
* int fprintf() c_library
|
|
* VOID gblsav() lkmain.c
|
|
* VOID getfid() lklex.c
|
|
* char getnb() lklex.c
|
|
* VOID lkexit() lkmain.c
|
|
* char * strcpy() c_library
|
|
* int strlen() c_library
|
|
*
|
|
* side effects:
|
|
* Various linker flags are updated and the linked
|
|
* structure lfile is created.
|
|
*/
|
|
|
|
int
|
|
parse()
|
|
{
|
|
register int c;
|
|
char fid[NINPUT];
|
|
|
|
while ((c = getnb()) != 0) {
|
|
if ( c == '-') {
|
|
while (ctype[c=get()] & LETTER) {
|
|
switch(c) {
|
|
|
|
case 'i':
|
|
case 'I':
|
|
oflag = 1;
|
|
break;
|
|
|
|
case 's':
|
|
case 'S':
|
|
oflag = 2;
|
|
break;
|
|
#ifdef GAMEBOY
|
|
case 'y':
|
|
case 'Y':
|
|
c = get();
|
|
if(c == 'O' || c == 'o')
|
|
nb_rom_banks = expr(0);
|
|
else if(c == 'A' || c == 'a')
|
|
nb_ram_banks = expr(0);
|
|
else if(c == 'T' || c == 't')
|
|
mbc_type = expr(0);
|
|
else if(c == 'N' || c == 'n') {
|
|
int i = 0;
|
|
if(getnb() != '=' || getnb() != '"') {
|
|
fprintf(stderr, "Syntax error in -YN=\"name\" flag\n");
|
|
lkexit(1);
|
|
}
|
|
while((c = get()) != '"' && i < 16) {
|
|
cart_name[i++] = c;
|
|
}
|
|
if(i < 16)
|
|
cart_name[i] = 0;
|
|
else
|
|
while(get() != '"')
|
|
;
|
|
} else if(c == 'P' || c == 'p') {
|
|
patch *p = patches;
|
|
|
|
patches = (patch *)malloc(sizeof(patch));
|
|
patches->next = p;
|
|
patches->addr = expr(0);
|
|
if(getnb() != '=') {
|
|
fprintf(stderr, "Syntax error in -YHaddr=val flag\n");
|
|
lkexit(1);
|
|
}
|
|
patches->value = expr(0);
|
|
} else {
|
|
fprintf(stderr, "Invalid option\n");
|
|
lkexit(1);
|
|
}
|
|
break;
|
|
|
|
#endif /* GAMEBOY */
|
|
#ifdef SDK
|
|
case 'j':
|
|
case 'J':
|
|
++symflag;
|
|
break;
|
|
case 'z':
|
|
case 'Z':
|
|
oflag = 3;
|
|
break;
|
|
#endif /* SDK */
|
|
case 'm':
|
|
case 'M':
|
|
++mflag;
|
|
break;
|
|
|
|
case 'u':
|
|
case 'U':
|
|
uflag = 1;
|
|
break;
|
|
|
|
case 'x':
|
|
case 'X':
|
|
xflag = 0;
|
|
break;
|
|
|
|
case 'q':
|
|
case 'Q':
|
|
xflag = 1;
|
|
break;
|
|
|
|
case 'd':
|
|
case 'D':
|
|
xflag = 2;
|
|
break;
|
|
|
|
case 'e':
|
|
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);
|
|
|
|
default:
|
|
fprintf(stderr, "Invalid option\n");
|
|
lkexit(1);
|
|
}
|
|
}
|
|
} else
|
|
if (ctype[c] != ILL) {
|
|
if (linkp == NULL) {
|
|
linkp = (struct lfile *)
|
|
new (sizeof (struct lfile));
|
|
lfp = linkp;
|
|
} else {
|
|
lfp->f_flp = (struct lfile *)
|
|
new (sizeof (struct lfile));
|
|
lfp = lfp->f_flp;
|
|
}
|
|
getfid(fid, c);
|
|
lfp->f_idp = (char *) new (strlen(fid)+1);
|
|
strcpy(lfp->f_idp, fid);
|
|
lfp->f_type = F_REL;
|
|
} else {
|
|
fprintf(stderr, "Invalid input");
|
|
lkexit(1);
|
|
}
|
|
}
|
|
return(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:
|
|
* char 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 setbas()
|
|
*
|
|
* The function setbas() scans the base address lines in hte
|
|
* basep structure, evaluates the arguments, and sets beginning
|
|
* address of the specified areas.
|
|
*
|
|
* local variables:
|
|
* int v expression value
|
|
* char id[] base id string
|
|
*
|
|
* 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
|
|
* char *ip pointer into the REL file
|
|
* text line in ib[]
|
|
* int lkerr error flag
|
|
*
|
|
* functions called:
|
|
* Addr_T expr() lkeval.c
|
|
* int fprintf() c_library
|
|
* VOID getid() lklex.c
|
|
* char getnb() lklex.c
|
|
* int symeq() lksym.c
|
|
*
|
|
* side effects:
|
|
* The base address of an area is set.
|
|
*/
|
|
|
|
VOID
|
|
setbas()
|
|
{
|
|
register int v;
|
|
char id[NCPS];
|
|
|
|
bsp = basep;
|
|
while (bsp) {
|
|
ip = bsp->b_strp;
|
|
getid(id, -1);
|
|
if (getnb() == '=') {
|
|
v = expr(0);
|
|
for (ap = areap; ap != NULL; ap = ap->a_ap) {
|
|
if (symeq(id, ap->a_id))
|
|
break;
|
|
}
|
|
if (ap == NULL) {
|
|
#ifndef SDK
|
|
fprintf(stderr,
|
|
"No definition of area %s\n", id);
|
|
lkerr++;
|
|
#endif /* SDK */
|
|
} else {
|
|
ap->a_addr = v;
|
|
}
|
|
} else {
|
|
fprintf(stderr, "No '=' in base expression");
|
|
lkerr++;
|
|
}
|
|
bsp = bsp->b_base;
|
|
}
|
|
}
|
|
|
|
/*)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:
|
|
* char 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 hte
|
|
* 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:
|
|
* Addr_T expr() lkeval.c
|
|
* int fprintf() c_library
|
|
* VOID getid() lklex.c
|
|
* char getnb() lklex.c
|
|
* sym * lkpsym() lksym.c
|
|
*
|
|
* side effects:
|
|
* The value of a variable is set.
|
|
*/
|
|
|
|
VOID
|
|
setgbl()
|
|
{
|
|
register int v;
|
|
register struct sym *sp;
|
|
char id[NCPS];
|
|
|
|
gsp = globlp;
|
|
while (gsp) {
|
|
ip = gsp->g_strp;
|
|
getid(id, -1);
|
|
if (getnb() == '=') {
|
|
v = expr(0);
|
|
sp = lkpsym(id, 0);
|
|
if (sp == NULL) {
|
|
#ifndef SDK
|
|
fprintf(stderr,
|
|
"No definition of symbol %s\n", id);
|
|
lkerr++;
|
|
#endif /* SDK */
|
|
} else {
|
|
#ifndef SDK
|
|
if (sp->s_flag & S_DEF) {
|
|
fprintf(stderr,
|
|
"Redefinition of symbol %s\n", id);
|
|
lkerr++;
|
|
sp->s_axp = NULL;
|
|
}
|
|
#endif /* SDK */
|
|
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 read(0)/write(1) flag
|
|
*
|
|
* 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
|
|
* char fb[] constructed file specification string
|
|
* 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:
|
|
* int lkerr error flag
|
|
*
|
|
* functions called:
|
|
* FILE * fopen() c_library
|
|
* int fprintf() c_library
|
|
*
|
|
* side effects:
|
|
* File is opened for read or write.
|
|
*/
|
|
|
|
FILE *
|
|
afile(fn, ft, wf)
|
|
char *fn;
|
|
char *ft;
|
|
{
|
|
register char *p1, *p2, *p3;
|
|
register int c;
|
|
FILE *fp;
|
|
char fb[FILSPC];
|
|
|
|
p1 = fn;
|
|
p2 = fb;
|
|
p3 = ft;
|
|
while ((c = *p1++) != 0 && c != FSEPX) {
|
|
if (p2 < &fb[FILSPC-4])
|
|
*p2++ = c;
|
|
}
|
|
*p2++ = FSEPX;
|
|
if (*p3 == 0) {
|
|
if (c == FSEPX) {
|
|
p3 = p1;
|
|
} else {
|
|
#ifdef SDK
|
|
p3 = "rel";
|
|
#else /* SDK */
|
|
p3 = "REL";
|
|
#endif /* SDK */
|
|
}
|
|
}
|
|
while ((c = *p3++) != 0) {
|
|
if (p2 < &fb[FILSPC-1])
|
|
*p2++ = c;
|
|
}
|
|
*p2++ = 0;
|
|
#ifdef SDK
|
|
if ((fp = fopen(fb, wf?(binary?"wb":"w"):(binary?"rb":"r"))) == NULL) {
|
|
#else /* SDK */
|
|
if ((fp = fopen(fb, wf?"w":"r")) == NULL) {
|
|
#endif /* SDK */
|
|
fprintf(stderr, "%s: cannot %s.\n", fb, wf?"create":"open");
|
|
lkerr++;
|
|
}
|
|
return (fp);
|
|
}
|
|
|
|
char *usetxt[] = {
|
|
#ifdef SDK
|
|
"Distributed with SDK " SDK_VERSION_STRING ", built on " __DATE__ " " __TIME__,
|
|
"Compile options: SDK Target " TARGET_STRING
|
|
#ifdef INDEXLIB
|
|
" INDEXLIB"
|
|
#endif
|
|
"\n",
|
|
#endif
|
|
"Startup:",
|
|
#ifdef SDK
|
|
" -- [Commands] Non-interactive command line input",
|
|
#endif /* SDK */
|
|
" -c Command line input",
|
|
" -f file[LNK] File input",
|
|
" -p Prompt and echo of file[LNK] to stdout (default)",
|
|
" -n No echo of file[LNK] to stdout",
|
|
#ifdef SDK
|
|
"Usage: [-Options] outfile file [file ...]",
|
|
#else /* SDK */
|
|
"Usage: [-Options] file [file ...]",
|
|
#endif /* SDK */
|
|
"Librarys:",
|
|
" -k Library path specification, one per -k",
|
|
" -l Library file specification, one per -l",
|
|
"Relocation:",
|
|
" -b area base address = expression",
|
|
" -g global symbol = expression",
|
|
#ifdef GAMEBOY
|
|
" -yo Number of rom banks (default: 2)",
|
|
" -ya Number of ram banks (default: 0)",
|
|
" -yt MBC type (default: no MBC)",
|
|
" -yn Name of program (default: name of output file)",
|
|
" -yp# Patch one byte in the output GB file (# is: addr=byte)",
|
|
#endif /* GAMEBOY */
|
|
"Map format:",
|
|
" -m Map output generated as file[MAP]",
|
|
#ifdef SDK
|
|
" -j no$gmb symbol file generated as file[SYM]",
|
|
#endif /* SDK */
|
|
" -x Hexidecimal (default)",
|
|
" -d Decimal",
|
|
" -q Octal",
|
|
"Output:",
|
|
" -i Intel Hex as file[IHX]",
|
|
" -s Motorola S19 as file[S19]",
|
|
#ifdef SDK
|
|
#ifdef GAMEGEAR
|
|
" -z Gamegear image as file[GG]",
|
|
#else
|
|
" -z Gameboy image as file[GB]",
|
|
#endif /* GAMEGEAR */
|
|
#endif /* SDK */
|
|
"List:",
|
|
" -u Update listing file(s) with link data as file(s)[.RST]",
|
|
"End:",
|
|
" -e or null line terminates input",
|
|
"",
|
|
0
|
|
};
|
|
|
|
/*)Function VOID usage()
|
|
*
|
|
* The function usage() outputs to the stderr device the
|
|
* assembler name and version and a list of valid assembler 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()
|
|
{
|
|
register char **dp;
|
|
|
|
fprintf(stderr, "\nASxxxx Linker %s\n\n", VERSION);
|
|
for (dp = usetxt; *dp; dp++)
|
|
fprintf(stderr, "%s\n", *dp);
|
|
lkexit(1);
|
|
}
|