/* MAKOVL.C     Overlay builder/manager

       This code is in the public domain.
       Created from L2.C 81.10.29 Gyro

This is a variation on L2.C that creates relocatable overlays
according to an overlay descriptor file.

Command format:

       makovl <root name> <overlay name> {<CRL file>} [-l {<CRL file>}] [-nd]

Overlay descriptor file, <root name>.DES, contains:

       <overlay slot size> <relocation info size>
       <CRL file> ... (default search list)
       ... (more default search list)
       <blank line>
       <overlay name> <slot number>
               <function name>  <comments or whatnot>
               <function name>  <comments or whatnot>
               ... (an entry for each top-level function in the overlay)
       <blank line>
       <overlay name> <slot number>
               <function name>  <comments or whatnot>
               <function name>  <comments or whatnot>
               ...
       ... (an entry for each overlay in the file)


Overlay segments are of length <overlay slot size> bytes, of which the
last <relocation info size> bytes holds a list of relocation offsets.
This is a null-terminated string of byte values giving the difference
between successive addresses to be relocated; a value of 255 means to
add 255 to the next byte value to get the offset.  The first offset is
relative to -1 (so that we can relocate the first word of the
overlay).  At the beginning of the overlay is a table of addresses of
top-level functions, one address for each function listed for that
overlay in the descriptor file.

The -l option works as for L2: CRL files listed before the -l are
loaded in entirety; those after are just scanned for needed functions.
Any functions specified in the default search list in the overlay
descriptor file are also scanned, unless the -nd (no default) option
is given.

The overlay, once created, is written into <root name>.OVL, at address
<slot number> * <overlay slot size>.

*/


/**************** Globals ****************/

#define SDOS                            /* comment this out for CP/M */

#include "bdscio.h"                     /* for i/o buffer defs */

#define NUL             0
#define FLAG            char
#define repeat  while (1)

#define STDOUT          1


/* function table */
struct funct {
       char fname[9];
       FLAG flinkedp;                  /* in memory already? */
       char *faddr;                    /* address of first ref link if not linked */
       } ftab [300];
int nfuncts;                            /* no. of functions in table */

#define LINKED          255             /* (flinkedp) function really here */
#define EXTERNAL        254             /* function defined in separate symbol table */

char fdir [512];                        /* CRL file function directory */

/* command line parameters etc. */
char rootname[15];                      /* name of root program */
char ovlname[40];                       /* name of overlay to be built */
int nprogs, nlibs;
char progfiles [20] [15];       /* program file names */
char libfiles [30] [15];                /* library file names */
FLAG srchdefs;                          /* search default libraries? */

/* overlay description */
int ovlsize, relsize;           /* size of overlay slot & of relocation info */
int ovlslot;                            /* slot # to put overlay in */
char topfuncts[10][32];         /* names of top level functions */
int ntops;                              /* number of top level functions */
char *relstart;                 /* beginning of relocation info */
char *relnext;                          /* next relocation value */
char *reladdr;                          /* last address relocated */


/* useful things to have defined */
struct inst {
       char opcode;
       char *address;
       };

union ptr {
       unsigned u;                     /* an int */
       unsigned *w;                    /* a word ptr */
       char *b;                                /* a byte ptr */
       struct inst *i;         /* an instruction ptr */
       };


/* Link control variables */

union ptr codend;                       /* last used byte of code buffer + 1 */
union ptr acodend;                      /* actual code-end address */
unsigned origin;                        /* origin of code */
unsigned buforg;                        /* origin of code buffer */

char *lspace;                           /* space to link in */
char *lspcend;                          /* end of link area */
char *lodstart;                 /* beginning of current file */


/* i/o buffer */
struct iobuf {
       int fd;
       int isect;                      /* currently buffered sector */
       int nextc;                      /* index of next char in buffer */
       char buff [128];
       } ibuf, obuf;

/* BDS C i/o buffer */
char bdsbuf[BUFSIZ];

/* seek opcodes */
#define ABSOLUTE 0
#define RELATIVE 1

#define INPUT 0
#define UPDATE 2

#define TRUE (-1)
#define FALSE 0
#define NULL 0

/* 8080 instructions */
#define LHLD 0x2A
#define LXIH 0x21
#define SPHL 0xF9
#define JMP  0xC3
#define CALL 0xCD

/* strcmp7 locals, made global for speed */
char _c1, _c2, _end1, _end2;

/**************** End of Globals ****************/



main (argc, argv)
       int argc;
       char **argv;
{
       puts ("Mark of the Unicorn Overlay Builder, vsn. 1.0\n");
       setup (argc, argv);
       linkprog();
       linklibs();
       listfuns();
       wrtovl();
       }


setup (argc, argv)                      /* initialize function table, etc. */
       int argc;
       char **argv;
{
       nprogs = 0;
       nlibs = 0;
       origin = buforg = 0;
       nfuncts = 0;
       srchdefs = TRUE;
       cmdline (argc, argv);
       lspace = endext();
       lspcend = topofmem() - 1024;
       getdesc();
       loadsyms();
       }


cmdline (argc, argv)            /* process command line */
       int argc;
       char **argv;
{
       int i, progp;

       if (argc <= 2) {
               puts ("Usage is:\n");
               puts ("  makovl <root name> <overlay name> {<CRL file>} [-l {<CRL file>}]");
               exit (1);
               }
       strcpy (&rootname, argv[1]);
       strcpy (&ovlname, argv[2]);
       progp = TRUE;
       for (i = 3; i < argc; ++i) {
               if (argv[i][0] == '-') {
                       if (!strcmp (argv[i], "-L")) progp = FALSE;
                       else if (match (argv[i], "-ND")) srchdefs = FALSE;
                       else printf ("Unknown option: '%s'\n", argv[i]);
                       }
               else {
                       if (progp) strcpy (&progfiles[nprogs++], argv[i]);
                       else strcpy (&libfiles[nlibs++], argv[i]);
                       }
               }
       }


getdesc()                                       /* get & process overlay description */
{
       makeext (&rootname, "DES");
       if (fopen (&rootname, &bdsbuf) < 0)
               Fatal ("Can't open overlay descriptor file '%s'.\n", &rootname);
       if (fscanf (&bdsbuf, "%d%d", &ovlsize, &relsize) < 2
           ||  ovlsize <= relsize) {
               Fatal ("Error in descriptor file: bad overlay/relocation sizes.\n");
               }
       getlibs();
       findovl();
       readfuns();
       fclose (&bdsbuf);
       }


getlibs()                                       /* get default search list */
{
       char line[MAXLINE], *cp, *dest;

       repeat {
               if (!fgets (&line, &bdsbuf)  ||  !line[0]) break;
               if (!srchdefs) continue;
               cp = &line;
               repeat {
                       while (isspace (*cp)) ++cp;
                       if (!*cp) break;
                       dest = &libfiles[nlibs++];
                       while (*cp  &&  !isspace (*cp)) *dest++ = *cp++;
                       *dest = NUL;
                       }
               }
       }


findovl()                                       /* find description of overlay */
{
       char tname[40];
       int nread;
       FLAG skipping, found;

       skipping = found = FALSE;
       while (!found
                 &&  (nread = fscanf (&bdsbuf, "%s%d", &tname, &ovlslot)) >= 0) {
               if (nread) {
                       if (!skipping) {
                               if (match (&tname, &ovlname)) found = TRUE;
                               else skipping = TRUE;
                               }
                       }
               else skipping = FALSE;
               }
       if (!found)
               Fatal ("Can't find description of overlay '%s'.\n", &ovlname);
       if (nread < 2)
               Fatal ("No slot number present for overlay '%s'.\n", &tname);
       printf ("Building overlay %s, for slot %d.\n", &tname, ovlslot);
       }


readfuns()
{
       char tname[40];
       struct funct *fnct;

       ntops = 0;
       RelInit();
       codend.b = lspace;                              /* build table of functions */
       puts ("Functions included:\n");
       while (fscanf (&bdsbuf, "%s", &tname) > 0) {
               printf ("%-10s", &tname);
               upcase (&tname);
               tname[8] = NUL;                 /* limit size */
               fnct = intern (&tname);
               *codend.w = fnct->faddr;                /* link in reference */
               RelAccum (codend.w);            /* relocate this address */
               fnct->faddr = codend.w++;
               ntops++;
               }
       puts ("\n\n");
       }


linkprog()                              /* link in all program files */
{
       int i;
       union ptr dirtmp;
       struct funct *fnct;

       for (i=0; i<nprogs; ++i) {
               makeext (&progfiles[i], "CRL");
               if (copen (&ibuf, progfiles[i]) < 0) {
                       printf ("Can't open %s\n", progfiles[i]);
                       continue;
                       }
               printf ("Loading %s\n", &progfiles[i]);
               readprog (i==0);
               for (dirtmp.b=&fdir; *dirtmp.b != 0x80;) {
                       fnct = intern (dirtmp.b);                       /* for each module */
                       skip7 (&dirtmp);                                        /* in directory */
                       if (!fnct->flinkedp)
                               linkmod (fnct, lodstart + *dirtmp.w - 0x205);
                       else {
                               puts ("  Duplicate program function '");
                               puts (&fnct->fname);
                               puts ("', not linked.\n");
                               }
                       dirtmp.w++;
                       }                                                               /* intern & link it */
               cclose (&ibuf);
               }
       }


linklibs()                              /* link in library files */
{
       int ifile;

       for (ifile = 0; ifile < nlibs; ++ifile) scanlib (ifile);
       while (missingp()) {
               puts ("Enter the name of a file to be searched: ");
               gets (&libfiles[nlibs]);
               upcase (&libfiles[nlibs]);
               scanlib (nlibs++);
               }
       acodend.b = codend.b - lspace + buforg;         /* save that number! */
       }


missingp()                              /* are any functions missing?  print them out */
{
       int i, foundp;

       foundp = FALSE;
       for (i=0; i<nfuncts; ++i)
               if (!ftab[i].flinkedp) {
                       if (!foundp) puts ("*** Missing functions:\n");
                       puts (&ftab[i].fname);
                       puts ("\n");
                       foundp = TRUE;
                       }
       return (foundp);
       }


readprog (mainp)                        /* read in a program file */
       FLAG mainp;
{
       char extp;                                                      /* was -e used? */
       char *extstmp;
       union ptr dir;
       unsigned len;

       if (cread (&ibuf, &fdir, 512) < 512)                    /* read directory */
               Fatal ("-- read error!\n");
       cseek (&ibuf, 5, RELATIVE);
       for (dir.b=&fdir; *dir.b != 0x80; nextd (&dir));        /* find end of dir */
       ++dir.b;
       len = *dir.w - 0x205;
       readobj (len);
       }


readobj (len)                           /* read in an object (program or lib funct) */
       unsigned len;
{
       if (codend.b + len >= lspcend) Fatal ("-- out of memory!\n");
       lodstart = codend.b;
       if (cread (&ibuf, lodstart, len) < len)
               Fatal ("-- read error (read 0x%x)!\n", len);
       }


scanlib (ifile)
       int ifile;
{
       int i;
       union ptr dirtmp;

       makeext (&libfiles[ifile], "CRL");
       if (copen (&ibuf, libfiles[ifile]) < 0) {
               printf ("Can't open %s\n", libfiles[ifile]);
               return;
               }
       printf ("Scanning %s\n", &libfiles[ifile]);
       if (cread (&ibuf, &fdir, 512) < 512)    /* read directory */
               Fatal ("-- Read error (directory)!\n");
       for (i=0; i<nfuncts; ++i) {                     /* scan needed functions */
               if (!ftab[i].flinkedp
                   && (dirtmp.b = dirsearch (&ftab[i].fname))) {
                       readfunct (dirtmp.b);
                       linkmod (&ftab[i], lodstart);
                       }
               }
       cclose (&ibuf);
       }


readfunct (direntry)                    /* read a function (from a library) */
       union ptr direntry;
{
       unsigned start, len;

       skip7 (&direntry);
       start = *direntry.w++;
       skip7 (&direntry);
       len = *direntry.w - start;
       if (cseek (&ibuf, start, ABSOLUTE) < 0)
               Fatal (" -- read error (seek 0x%x)!", start);
       readobj (len);
       }


linkmod (fnct, modstart)                        /* link in a module */
       struct funct *fnct;
       union ptr       modstart;                                       /* loc. of module in memory */

{
       union ptr temp,
                       jump,                                   /* jump table temp */
                       body,                                   /* loc. of function in memory */
                       code,                                   /* loc. of code proper in mem. */
                       finalloc;                                       /* runtime loc. of function */
       unsigned flen, nrelocs, jtsiz, offset;

       fnct->flinkedp = LINKED;
       finalloc.b = codend.b - lspace + buforg;
       chase (fnct->faddr, finalloc.b);
       fnct->faddr = finalloc.b;
       body.b = modstart.b + strlen(modstart.b) + 3; /* loc. of function body */
       jump.i = body.i + (*modstart.b ? 1 : 0);
       for (temp.b = modstart.b; *temp.b; skip7(&temp)) {
               jump.i->address = intern (temp.b);
               ++jump.i;
               }
       ++temp.b;
       flen = *temp.w;
       code.b = jump.b;
       temp.b = body.b + flen;                         /* loc. of reloc parameters */
       nrelocs = *temp.w++;
       jtsiz = code.b - body.b;
       offset = code.b - codend.b;
       while (nrelocs--) relocate (*temp.w++, body.b, jtsiz,
                                                  finalloc.b, offset, flen);
       flen -= jtsiz;
       movmem (code.b, codend.b, flen);
       codend.b += flen;
       }


relocate (param, body, jtsiz, base, offset, flen)       /* do a relocation!! */
       unsigned param, jtsiz, base, offset, flen;
       union ptr body;
{
       union ptr instr,                                        /* instruction involved */
                       ref;                                            /* jump table link */
       struct funct *fnct;

       if (param == 1  &&  jtsiz) return;              /* don't reloc jt skip */
       instr.b = body.b + param - 1;
       if (instr.i->address >= jtsiz) {
               instr.i->address += base - jtsiz;
               RelAccum (body.b + param - offset);
               }
       else {
               ref.b = instr.i->address + body.u;
               if (instr.i->opcode == LHLD) {
                       instr.i->opcode = LXIH;
                       --ref.b;
                       }
               fnct = ref.i->address;
               instr.i->address = fnct->faddr;         /* link in */
               if (!fnct->flinkedp)
                       fnct->faddr = instr.b + 1 - offset;     /* new list head */
               if (fnct->flinkedp != EXTERNAL) RelAccum (instr.b + 1 - offset);
               }
       }


intern (name)                           /* intern a function name in the table */
       char *name;
{
       struct funct *fptr;

       if (*name == 0x9D) name = "MAIN";               /* Why, Leor, WHY??? */
       for (fptr = &ftab[nfuncts-1]; fptr >= ftab; --fptr)
               if (!strcmp7 (name, fptr->fname)) break;
       if (fptr < ftab) {
               fptr = &ftab[nfuncts];
               strcpy7 (fptr->fname, name);
               str7tont (fptr->fname);
               fptr->flinkedp = FALSE;
               fptr->faddr = NULL;
               ++nfuncts;
               }
       return (fptr);
       }


dirsearch (name)                        /* search directory for a function */
       char *name;
{
       union ptr temp;

       for (temp.b = &fdir; *temp.b != 0x80; nextd (&temp))
               if (!strcmp7 (name, temp.b)) return (temp.b);
       return (NULL);
       }


nextd (ptrp)                            /* move this pointer to the next dir entry */
       union ptr *ptrp;
{
       skip7 (ptrp);
       ++(*ptrp).w;
       }


chase (head, loc)                       /* chase chain of refs to function */
       union ptr head;
       unsigned loc;
{
       union ptr temp;

       while (head.w) {
               temp.w = *head.w;
               *head.w = loc;
               head.u = temp.u;
               }
       }


RelInit()                                       /* initialize relocation accumulator */
{
       relstart = relnext = lspace;
       lspace += relsize;                              /* allocate some memory for relocs */
       reladdr = lspace - 1;
       }


RelAccum (addr)                 /* note another relocation */
       char *addr;
{
       unsigned delta;

       if (relnext - relstart >= relsize) {
               Deadly ("Out of relocation bytes (limit is %d)\n", relsize);
               relnext = relstart;
               }
       delta = addr - reladdr;
       if (delta >= 255) {
               *relnext++ = 255;
               *relnext++ = delta - 255;
               }
       else *relnext++ = delta;
       reladdr = addr;
       }


RelFini()                                       /* end of relocations */
{
       printf ("Relocation bytes used: %d (limit is %d)\n",
                          relnext - relstart, relsize);
       *relnext = NUL;
       }


wrtovl()                                        /* write overlay into slot */
{
       int ovlfd;

       RelFini();
       if (codend.b > lspace + ovlsize - relsize)
               Deadly ("Code too long!\n");
       movmem (relstart, lspace + ovlsize - relsize, relsize);
       makeext (&rootname, "OVL");
       if ((ovlfd = open (&rootname, UPDATE)) < 0)
               Fatal ("Can't open overlay file '%s'.\n", rootname);
       if (seek (ovlfd, ovlslot * ovlsize / 128, ABSOLUTE) == -1
           ||  write (ovlfd, lspace, ovlsize / 128) == -1)
               Fatal ("Disk write error.  Check slot # and size of .OVL file.\n");
       close (ovlfd);
       }


listfuns()                              /* list functions linked */
{
       int i, compar();

       for (i = 0; i < ntops; ++i) ftab[i].flinkedp = i;
       while (i < nfuncts  &&  ftab[i].flinkedp == EXTERNAL) i++;
       arydel (&ftab, nfuncts, sizeof(*ftab), ntops, i);
       nfuncts -= i - ntops;
       qsort (&ftab, nfuncts, sizeof(*ftab), &compar);
       ftab[nfuncts].faddr = acodend.b;
       puts ("\nContents of overlay:\n");
       puts ("Entry no.   Name        Start     Length\n");
       for (i = 0; i < nfuncts; ++i) {
               if (ftab[i].flinkedp < 255) printf ("%8d", ftab[i].flinkedp);
               else puts ("        ");
               printf ("    %-8s    0x%4x    0x%4x\n",
                          ftab[i].fname, ftab[i].faddr,
                          ftab[i+1].faddr - ftab[i].faddr);
               }
       printf ("End address: 0x%x\n", acodend.u);
       i = ovlsize - relsize - acodend.u;
       printf ("Code bytes remaining: 0x%x = %d.\n\n", i, i);
       }


compar (f1, f2)                 /* compare two symbol table entries by name */
       struct funct *f1, *f2;
{
/*      return (strcmp (&f1->fname, &f2->fname));       alphabetical order */
       return (f1->faddr > f2->faddr);                 /* memory order */
       }


loadsyms()                              /* load base symbol table (for overlay) */
{                                               /* symbol table must be empty! */
       int nread;
       FLAG done;
       char *c;

       makeext (&rootname, "SYM");
       if (fopen (&rootname, &bdsbuf) < 0)
               Fatal ("Can't open %s.\n", &rootname);
       done = FALSE;
       while (!done) {
               nread = fscanf (&bdsbuf, "%x%s%x%s%x%s%x%s",
                                        &(ftab[nfuncts].faddr), &(ftab[nfuncts].fname),
                                        &(ftab[nfuncts+1].faddr), &(ftab[nfuncts+1].fname),
                                        &(ftab[nfuncts+2].faddr), &(ftab[
nfuncts+2].fname),
                                        &(ftab[nfuncts+3].faddr), &(ftab[nfuncts+3].fname));
               nread /= 2;
               if (nread < 4) done = TRUE;
               while (nread-- > 0) ftab[nfuncts++].flinkedp = EXTERNAL;
               }
       fclose (&bdsbuf);
       }


makeext (fname, ext)            /* force a file extension to ext */
       char *fname, *ext;
{
       while (*fname && (*fname != '.')) {
               *fname = toupper (*fname);              /* upcase as well */
               ++fname;
               }
       *fname++ = '.';
       strcpy (fname, ext);
       }


strcmp7 (s1, s2)                        /* compare two bit-7-terminated strings */
       char *s1, *s2;                  /* also works for non-null NUL-term strings */
{
/*      char c1, c2, end1, end2;                (These are now global for speed) */

       repeat {
               _c1 = *s1++;
               _c2 = *s2++;
               _end1 = (_c1 & 0x80) | !*s1;
               _end2 = (_c2 & 0x80) | !*s2;
               if ((_c1 &= 0x7F) < (_c2 &= 0x7F)) return (-1);
               if (_c1 > _c2  ||  (_end2  &&  !_end1)) return (1);
               if (_end1  &&  !_end2) return (-1);
               if (_end1  &&  _end2) return (0);
               }
       }


strcpy7 (s1, s2)                        /* copy s2 into s1 */
       char *s1, *s2;
{
       do {
               *s1 = *s2;
               if (!*(s2+1)) {                         /* works even if */
                       *s1 |= 0x80;                            /* s2 is null-term */
                       break;
                       }
               ++s1;
               } while (!(*s2++ & 0x80));
       }


skip7 (ptr7)                            /* move this pointer past a string */
       char **ptr7;
{
       while (!(*(*ptr7)++ & 0x80));
       }


str7tont (s)                            /* add null at end */
       char *s;
{
       while (!(*s & 0x80)) {
               if (!*s) return;                /* already nul term! */
               s++;
               }
       *s = *s & 0x7F;
       *++s = NUL;
       }


puthex (n, obuf)                        /* output a hex word, with leading 0s */
       unsigned n;
       char *obuf;
{
       int i, nyb;

       for (i = 3; i >= 0; --i) {
               nyb = (n >> (i * 4)) & 0xF;
               nyb += (nyb > 9) ? 'A' - 10 : '0';
               putc (nyb, obuf);
               }
       }


Fatal (arg1, arg2, arg3, arg4)  /* lose, lose */
       char *arg1, *arg2, *arg3, *arg4;
{
       printf (arg1, arg2, arg3, arg4);
       exit (1);
       }


Deadly (arg1, arg2, arg3, arg4) /* partially-recoverable error */
       char *arg1, *arg2, *arg3, *arg4;
{
       printf (arg1, arg2, arg3, arg4);
       puts ("   -- overlay being generated anyway, for debugging.\n");
       }


exit (status)                           /* exit the program */
       int status;
{
       if (status == 1) {
#ifdef SDOS
               unlink ("a:$$$$.cmd");
#else
               unlink ("a:$$$.sub");
#endif
               }
       bios (1);                                       /* bye! */
       }


/* End of MAKOVL.C  --  link and store relocatable overlay */