/************************************************************************
*
* lnkloader - Loads objects from asm7090 for linking.
*
* Changes:
*   05/21/03   DGP   Original.
*	
************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <ctype.h>
#include <errno.h>

#include "lnkdef.h"

extern FILE *lstfd;
extern int listmode;
extern int pc;
extern int errcount;
extern int absentry;
extern int relentry;
extern Memory memory[MEMSIZE];

extern int errno;

int
lnkloader (FILE *fd, char *module, int loadpt, int pass)
{
   FILE *lfd;
   int status = 0;
   int curraddr;
   char inbuf[82];

#ifdef DEBUGLOADER
   printf ("lnkloader: module = '%s', loadpd = %5.5o\n", module, loadpt);
#endif

   curraddr = loadpt;

   while (fgets (inbuf, sizeof(inbuf), fd))
   {
      SymNode *s;
      char *op = inbuf;
      int i;

      for (i = 0; i < CHARSPERREC; i += WORDTAGLEN)
      {
	 char otag;
	 char item[16];
	 uint32 ldata[2];

	 otag = *op++;
	 strncpy (item, op, 12);
	 item[12] = '\0';
#ifdef DEBUGLOADER
	 printf ("loadpt = %5.5o, curraddr = %5.5o\n",
		  loadpt, curraddr);
	 printf ("otag = %c, item = %s\n", otag, item);
#endif
	 if (otag == EXTRN_TAG || otag == GLOBAL_TAG)
	 {
	    char *bp = item;

	    ldata[0] = 0;
	    ldata[1] = strtol (&item[7], NULL, 8);
	    item[MAXSYMLEN] = '\0';
	    for (; *bp; bp++) if (isspace(*bp)) *bp = '\0';
	    
	 }
	 else
	 {
	    ldata[1] = strtol (&item[7], NULL, 8);
	    item[7] = '\0';
	    ldata[0] = strtol (item, NULL, 8);
	 }


#ifdef DEBUGLOADER
	 printf ("ldata = %7.7o %5.5o\n", ldata[0], ldata[1]);
#endif

         switch (otag)
	 {
	 case BSS_TAG:
	    memory[curraddr].memhi = ldata[0];
	    memory[curraddr].memlo = ldata[1];
	    memory[curraddr].tag = otag;
	    curraddr += ldata[1] & 077777;
	    break;
	 case DATA_TAG:
	 case ABSINST_TAG:
	    memory[curraddr].memhi = ldata[0];
	    memory[curraddr].memlo = ldata[1];
	    memory[curraddr].tag = otag;
	    curraddr++;
	    break;
	 case RELINST_TAG:
	    memory[curraddr].memhi = ldata[0];
	    memory[curraddr].memlo = ldata[1] + loadpt;
	    memory[curraddr].tag = otag;
	    memory[curraddr].relocatable = TRUE;
	    curraddr++;
	    break;
	 case ORG_TAG:
	 case ABSENTRY_TAG:
	    errcount++;
	    if (listmode)
	    {
	       printheader (lstfd);
	       fprintf (lstfd, "Linking of absolute modules not supported\n");
	    }
	    else
	       printf ("lnk7090: Linking of absolute modules not supported\n");
	    status = -1;
	    break;
	 case RELENTRY_TAG:
	    relentry = ldata[1] + loadpt;
	    break;
	 case EXTRN_TAG:
	    if (pass == 2)
	    {
	       if ((s = symlookup (item, module, FALSE)) == NULL)
	       {
		  errcount++;
		  if (listmode)
		  {
		     printheader (lstfd);
		     fprintf (lstfd, "Symbol %s undefined\n", item);
		  }
		  else
		     printf ("lnk7090: Symbol %s undefined\n", item);
	          status = -1;
	       }
	       else
	       {
		  int refaddr = ldata[1] + loadpt;

	          while (refaddr)
		  {
		     int k;
		     k = memory[refaddr].memlo;
		     memory[refaddr].memlo = s->value;
		     if (k == 0) memory[refaddr].tag = RELINST_TAG;
		     refaddr = k;
		  }
	       }
	    }
	    break;
	 case GLOBAL_TAG:
	    if (pass == 1)
	    {
	       s = symlookup (item, module, TRUE);
	       s->value = ldata[1] + loadpt;
	    }
	    else if (listmode)
	    {
	       printheader (lstfd);
	       fprintf (lstfd, ENTRYFORMAT,
			ldata[1] + loadpt, item);
	    }
	    break;
	 default: ; /* we ignore any other tags */
	 }
	 op += 12;
      }
   }
   pc = curraddr;
   return (status);

}
