/* lkmain.c

   Copyright (C) 1989-1995 Alan R. Baldwin
   721 Berkeley St., Kent, Ohio 44240

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, 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 <http://www.gnu.org/licenses/>. */

/*
 * Extensions: P. Felber
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "aslink.h"

#ifndef SDK_VERSION_STRING
#define SDK_VERSION_STRING      "3.0.0"
#endif
#ifndef TARGET_STRING
#define TARGET_STRING           "gbz80"
#endif

#ifdef WIN32T
#include <time.h>

void Timer(int action, char * message)
{
        static double start, end, total=0.0;
    static const double secs_per_tick = 1.0 / CLOCKS_PER_SEC;

    if(action==0) start=clock()*secs_per_tick;
    else if(action==1)
    {
        end=clock() * secs_per_tick;
                printf("%s \t%f seconds.\n", message, (end-start));
                total+=end-start;
    }
    else
    {
                printf("Total time: \t%f seconds.\n", total);
                total=0.0;
    }
}
#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_main()
 *              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 functions lk_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     lk_getline()    lklex.c
 *              VOID    library()       lklibr.c
 *              VOID    link_main()     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(int argc, char *argv[])
{
        register char *p;
        register int c, i;

#ifdef WIN32T
    Timer(0, "");
#endif

#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 (lk_getline() == 0)
                        break;
                if (pflag && sfp != stdin)
                        fprintf(stdout, "%s\n", ip);
                if (*ip == '\0' || parse())
                        break;
        }

        if (sfp) {
                fclose(sfp);
                sfp = NULL;
        }

        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();

        if (dflag){
            SaveLinkedFilePath(linkp->f_idp); //Must be the first one...
            dfp = afile(linkp->f_idp,"cdb",1);
            if (dfp == NULL)
                lkexit(1);
        }

        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 (lk_getline()) {
                        ip = ib;
                        link_main();
                }
                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 requested.
                         */
                        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');
                }
        }
#ifdef WIN32T
    Timer(1, "Linker execution time");
#endif

        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(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);
        if (dfp != NULL) fclose(dfp);
        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     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_main()
{
        register char c;

        if ((c=endline()) == 0) { return; }
        switch (c) {

        case 'O': /*For some important sdcc options*/
                if (pass == 0)
                {
                        if(strlen(sdccopt)==0)
                        {
                                strcpy(sdccopt, &ip[1]);
                                strcpy(sdccopt_module, curr_module);
                        }
                        else
                        {
                                if(strcmp(sdccopt, &ip[1])!=0)
                                {
                                        fprintf(stderr,
                                                "?ASlink-Warning-Conflicting sdcc options:\n"
                                                "   \"%s\" in module \"%s\" and\n"
                                                "   \"%s\" in module \"%s\".\n",
                                                sdccopt, sdccopt_module, &ip[1], curr_module);
                                        lkerr++;
                                }
                        }
                }
                break;

        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)
        {
            strcpy(curr_module, &ip[1]);
                        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 int 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 == ';')
                        return(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':
                                        oflag = 3;
                                        break;
#endif /* SDK */
#ifndef GAMEBOY
                                case 'z':
                                        dflag = 1;
                                        return(0);
#endif
                                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);
                                }
                        }
                        if (c == ';')
                                return(0);
                } else if (ctype[c] & ILL) {
                        fprintf(stderr, "Invalid input");
                        lkexit(1);
                } else {
                        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;
                }
        }
        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 the
 *      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,
                                "ASlink-Warning-No definition of area %s\n", id);
                                lkerr++;
#endif /* SDK */
                        } else {
                                ap->a_addr = v;
                        }
                } else {
                        fprintf(stderr, "ASlink-Warning-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 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:
 *              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(char *fn, char *ft, int wf)
{
#if 0
        register char *p1, *p2, *p3;
        register int c;
#else
        int i;
#endif
        FILE *fp;
        char fb[PATH_MAX];

#if 0
        p1 = fn;
        p2 = fb;
        p3 = ft;
        while ((c = *p1++) != 0 && c != FSEPX) {
                if (p2 < &fb[PATH_MAX-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;
#else
        /*Look backward the name path and get rid of the extension, if any*/
        i=strlen(fn);
        for(; (fn[i]!='.')&&(fn[i]!='\\')&&(fn[i]!='/')&&(i>0); i--);
        if( (fn[i]=='.') && *ft && strcmp(ft, "lnk") )
        {
                strncpy(fb, fn, i);
                fb[i]=0;
        }
        else
        {
                strcpy(fb, fn);
        }

        /*Add the extension*/
        if (fb[i] != '.')
        {
                strcat(fb, ".");
#ifdef SDK
                strcat(fb, strlen(ft)?ft:"rel");
#else
                strcat(fb, strlen(ft)?ft:"REL");
#endif
        }
#endif

#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 */
            if (strcmp(ft,"adb"))/*Do not complaint for optional adb files*/
            {
                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:",
        "  -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",
#if 0
#ifdef SDK
        "Usage: [-Options] outfile file [file ...]",
#else /* SDK */
        "Usage: [-Options] file [file ...]",
#endif /* SDK */
#endif
        "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",
#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   Hexadecimal (default),  -d  Decimal,  -q  Octal",
        "Output:",
        "  -i   Intel Hex as file[IHX]",
        "  -s   Motorola S19 as file[S19]",
#ifndef GAMEBOY
        "  -z   Produce SDCdb debug as file[cdb]",
#endif
#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);
}

/*)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 (dest,src)
FILE *src,*dest ;
{
    int ch;

    while ((ch = fgetc(src)) != EOF) {
        fputc(ch,dest);
    }
}
