/************************************************************************
*
* lnkloader - Loads objects from asm7090 for linking.
*
* Changes:
*   05/21/03   DGP   Original.
*   12/28/04   DGP   New object tags.
*	
************************************************************************/

#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];
	 t_int64 ldata;

	 otag = *op++;
	 if (otag == ' ') break;
	 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
	 switch (otag)
	 {
	 case IDT_TAG:
	 case ABSEXTRN_TAG:
	 case RELEXTRN_TAG:
	 case ABSGLOBAL_TAG:
	 case RELGLOBAL_TAG:
	    {
	       char *bp = item;

#ifdef WIN32
	       sscanf (&item[6], "%I64o", &ldata);
#else
	       sscanf (&item[6], "%llo", &ldata);
#endif
	       item[MAXSYMLEN] = '\0';
	       for (; *bp; bp++) if (isspace(*bp)) *bp = '\0';
	    }
	    break;

	 default:
#ifdef WIN32
	    sscanf (item, "%I64o", &ldata);
#else
	    sscanf (item, "%llo", &ldata);
#endif
	 }


#ifdef DEBUGLOADER
#ifdef WIN32
	 printf ("ldata = %12.12i64o\n", ldata);
#else
	 printf ("ldata = %12.12llo\n", ldata);
#endif
#endif

         switch (otag)
	 {
	 case IDT_TAG:
	    break;

	 case BSS_TAG:
	    memory[curraddr].word = ldata;
	    memory[curraddr].tag = otag;
	    curraddr += (int)(ldata & ~ADDRMASK);
	    break;

	 case RELORG_TAG:
	    memory[curraddr].word = ldata + loadpt;
	    memory[curraddr].tag = otag;
	    curraddr = (int)(ldata & ~ADDRMASK) + loadpt;
	    break;

	 case ABSDATA_TAG:
	    memory[curraddr].word = ldata;
	    memory[curraddr].tag = otag;
	    curraddr++;
	    break;

	 case RELADDR_TAG:
	    memory[curraddr].word = ldata + loadpt;
	    memory[curraddr].tag = otag;
	    memory[curraddr].reladdr = TRUE;
	    curraddr++;
	    break;

	 case RELDECR_TAG:
	    memory[curraddr].word = ldata + (loadpt << 18);
	    memory[curraddr].tag = otag;
	    memory[curraddr].reldecr = TRUE;
	    curraddr++;
	    break;

	 case RELBOTH_TAG:
	    memory[curraddr].word = ldata + loadpt + (loadpt << 18);
	    memory[curraddr].tag = otag;
	    memory[curraddr].relboth = TRUE;
	    curraddr++;
	    break;

	 case ABSORG_TAG:
	 case ABSGLOBAL_TAG:
	 case ABSENTRY_TAG:
	    if (pass == 2)
	    {
	       errcount++;
	       if (listmode)
	       {
		  printheader (lstfd);
		  fprintf (lstfd,
		     "Linking of absolute modules not supported\n");
	       }
	       else
	       {
		  fprintf (stderr, 
			"lnk7090: Linking of absolute modules not supported\n");
	       }
#ifdef DEBUGLOADER
	       printf (
		     "lnk7090: Linking of absolute modules not supported\n");
#endif
	    }
	    status = -1;
	    break;

	 case RELENTRY_TAG:
	    relentry = (int)((ldata & ~ADDRMASK) + loadpt);
	    break;

	 case RELEXTRN_TAG:
	    if (pass == 2)
	    {
	       if (!(s = symlookup (item, module, FALSE)))
	       {
		  errcount++;
		  if (listmode)
		  {
		     printheader (lstfd);
		     fprintf (lstfd, "Symbol %s undefined\n", item);
		  }
		  else
		  {
		     fprintf (stderr, "lnk7090: Symbol %s undefined\n", item);
		  }
#ifdef DEBUGLOADER
		  printf ("lnk7090: Symbol %s undefined\n", item);
#endif
	          status = -1;
	       }
	       else
	       {
		  int refaddr = (int)((ldata & ~ADDRMASK) + loadpt);

	          while (refaddr)
		  {
		     int k;

		     k = (int)(memory[refaddr].word & ~ADDRMASK);
		     /* NOTE: Must handle DECR & BOTH */
		     memory[refaddr].word =
			   (memory[refaddr].word & ADDRMASK) | s->value;
		     if (k == 0) memory[refaddr].tag = RELADDR_TAG;
		     refaddr = k;
		  }
	       }
	    }
	    break;

	 case RELGLOBAL_TAG:
	    if (pass == 1)
	    {
	       s = symlookup (item, module, TRUE);
	       s->value = (int)((ldata & ~ADDRMASK) + loadpt);
	    }
	    else if (listmode)
	    {
	       printheader (lstfd);
	       fprintf (lstfd, ENTRYFORMAT,
			(int)((ldata & ~ADDRMASK) + loadpt), item);
	    }
	    break;
	 default: ; /* we ignore any other tags */
	 }
	 op += 12;
      }
   }
   pc = curraddr;
   return (status);

}
