/* ---------------------------------------------------------------------- */
/*                   Copyright (C) 1991 by Natuerlich!                    */
/*                      This file is copyrighted!                         */
/*                Refer to the documentation for details.                 */
/* ---------------------------------------------------------------------- */
#define LINKER 1
#include "defines.h"
#include "nasm.h"
#include "debug.h"
#include "labels.h"
#include "object.h"
#include "seg.h"
#include "imm.h"
#include OSBIND
#include NMALLOC_H
#include "process.h"
#include "exprfast.h"

#include "ldebug.h"


#if ! VERSION
extern char       x1[], x2[], x3[], x4[], x5[];
#endif

extern byte huge        *__program, huge *__p, huge *ptohead;
extern byte             __c;
extern word             __pc, __x;
extern lword            __lx;
extern imm huge         *hip, huge *cip;
extern seg huge         *h_seg, huge *sp;
extern int              relocatable,
                        bootable,
                        pageflag;
extern word             alignment,
                        origin;

extern byte       huge  *pb;
extern e_dropped  huge  *pe;
extern s_dropped  huge  *ps;
extern linksymbol huge  *py;
extern i_dropped  huge  *pi;
extern r_dropped  huge  *pr;
extern f_dropped  huge  *pf;
extern lword      bytes, sbytes, ibytes, rbytes, ebytes, ybytes, fbytes;
extern word       endpc, diff;

ref               huge  *rhead;

seg               huge  **pas;
imm               huge  **pai;
expr              huge  **pae;


void seg_link()
{
   extern word                plus1;
   register s_dropped  huge   *p = ps;
   register seg huge          **q;
   word                       i, index;

   ENTER("seg_link");
   plus1 = 0;
   index = (word) (__p - __program);
   if( ! sp && p->type == S_DS)
      nerror("You can't start off with a .DS segment");
   pas = q = nmalloc( sbytes * sizeof( seg *));

   for( i = (word) sbytes; i; i--, p++)
   {            /* V--- changin' the val of s_dropped as well */
      build_seg( p->index + index, p->type, p->size);
      endpc += p->size;
      *q++ = sp;
      POINTER_CHECK( q[-1]);
   }
   LEAVE();
}

static byte sizetab[256] =
{
   1,2,0,0,2,2,2,0, 1,2,1,0,3,3,3,0,
   2,2,2,0,2,2,2,0, 1,3,1,0,3,3,3,0,
   3,2,0,0,2,2,2,0, 1,2,1,0,3,3,3,0,
   2,2,2,0,2,2,2,0, 1,3,1,0,3,3,3,0,
   1,2,0,0,0,2,2,0, 1,2,1,0,3,3,3,0,
   2,2,2,0,0,2,2,0, 1,3,1,0,0,3,3,0,
   1,2,0,0,2,2,2,0, 1,2,1,0,3,3,3,0,
   2,2,2,0,2,2,2,0, 1,3,1,0,3,3,3,0,

   2,2,0,0,2,2,2,0, 1,0,1,0,3,3,3,0,
   2,2,2,0,2,2,2,0, 1,3,1,0,3,3,3,0,
   2,2,2,0,2,2,2,0, 1,2,1,0,3,3,3,0,
   2,2,2,0,2,2,2,0, 1,3,1,0,3,3,3,0,
   2,2,0,0,2,2,2,0, 1,2,1,0,3,3,3,0,
   2,2,2,0,0,2,2,0, 1,3,1,0,0,3,3,0,
   2,2,0,0,2,2,2,0, 1,2,1,0,3,3,3,0,
   2,2,2,0,0,2,2,0, 1,3,1,0,0,3,3,0
};


void code_reloc()
{
   register s_dropped huge *p = ps;
   register word           rdiff = diff,
                           val, i;
   register byte huge      *src;
   lword                   j  = sbytes;


   ENTER("code_reloc");
   while( j--)
   {
      src = pb + p->index;
#if ! VERSION
      if( j || p->type != S_DS)
         POINTER_CHECK( src);
#endif
      POINTER_CHECK( p);
      POINTER_CHECK( __p);
      i = p->size;
      switch( p++->type)
      {
         case S_RCODE :
            while( i--)
               switch( sizetab[ *src])
               {
                  case 0  :
                     nferror("object code is impure");

                  case 2  :
                     i--;
                     deposit( *src++);
                  case 1  :
                     deposit( *src++);
                     break;

                  case 3  :
                     deposit( *src++);
                     val = pdpeek( src);
                     if( inrange( val, DEFORG - 1, endpc))
                        val += rdiff;
                     wdeposit( val);
                     i -= 2;
               }
            break;

         case S_SDATA :
            while( i--)
            {
               deposit( *src++);
            }
            continue;

         case S_RDATA :
            while( i)
            {
               if( val = pdpeek( src))
                  val += rdiff;
               wdeposit( val);
               i -= 2;
            }
            break;

         case S_TDATA :
            while( i)
            {
               if( val = pdpeek( src))
                  val += rdiff;
               wddeposit( val);
               i -= 2;
            }
         case S_RSTOP :    /* not really implemented */
            break;

         case S_DS    :
            if( ! bootable)
            {
               dpoke( ptohead, __pc - 1);
               __pc += i;
               dpoke( __p, __pc);
               _advance( 4);
               ptohead = __p - 2;
            }
            else
            {
               register int   j;

               for( j = i; j; j--)
               {
                  deposit( 0);
               }
            }
            break;

         default      :
            nierror("Whoa! *Serious* problems with segmentation");
      }
   }
   LEAVE();
}


void  imm_link()
{
   register i_dropped huge *p = pi;
   register imm huge       *q;
   imm huge                **x;
   register word           i, tmp;

   ENTER("imm_link");
   x = pai = nmalloc( (ibytes + 1) * sizeof( imm *));
   *x++ = 0;
   for( i = (word) ibytes; i; p++, i--)
   {
      *x++ = q = imm_alloc();
      POINTER_CHECK( p);
      POINTER_CHECK( q);
      q->block = pas[ p->block];
      POINTER_CHECK( q->block);
      if( q->val = p->val)
         q->val += diff;
      q->type = p->type;
      q->offset = p->offset;
      if( tmp = q->val)
      {
         if( q->type)
            tmp >>= 8;
         poke( __program + q->block->index + p->offset, tmp);
      }
      q->next = 0;
      cip = hip ? (cip->next = q) : (hip = q);
   }
   LEAVE();
}

/* This function is just for Debugging */
#if ! VERSION
void  lerr_imms()
{
   register imm huge *p;

   if( p = hip)
      do
      {
         POINTER_CHECK( p);
         if( ! p->val)
            nierror("There are imms w/o a value");
      }
      while( p = p->next);
}
#endif

void ref_link()
{
   register r_dropped huge *p = pr;
   register ref huge       *q, 
                huge       *old;
   register word           i = (word) rbytes;

   ENTER("ref_link");
   if( i = (word) rbytes)
   {
      POINTER_CHECK( p);
      do
      {
         q         = ref_alloc();
         POINTER_CHECK( q);
         q->ref    = (expr *) *p++;      /* yet unknown */
         q->hacked = 0;
         old       = rhead ? (old->next = q) : (rhead = q); /* That's OK */
      }
      while( --i);
      q->next = 0;
   }
   LEAVE();
   ADD_WATCH( &rhead, sizeof( char *), "rhead");
}


static void  fix_link( p, q, r)
register e_dropped huge *p;
register expr huge      *q;
register f_dropped huge *r;
{
   register fix huge *f = fix_alloc();

   ENTER("fix_link");
   POINTER_CHECK( p);
   POINTER_CHECK( q);
   POINTER_CHECK( r);
   POINTER_CHECK( f);
   if( q->fix == FIX_LABEL)
      f->poof.label = (label *) p->t;     /* yet unknown */
   else
      f->poof.block = pas[ (word) r->poof];
   f->imm       = pai[ r->imm];
   f->index     = r->index;
   q->zonk.fixp = f;
   LEAVE();
}


void  exp_link()
{
   register e_dropped huge *p;
   register expr huge      *q;
   register ref  huge      *x;
   expr          huge      **y;
   f_dropped     huge      *r;
   register word           i;

   ENTER("exp_link");
   if( p = pe)
   {
      y = pae = nmalloc( (ebytes + 1) * sizeof( expr *));
      *y++ = 0;
      r    = pf;

      for( i = 1; i <= ebytes; i++, p++)
      {
         *y++ = q = exp_alloc();
         POINTER_CHECK( p);
         POINTER_CHECK( q);
         if( valued( p) && e_pcrel( p))
            q->val = p->val + diff;
         else
            q->val = p->val;
         q->aux   = p->aux + diff;
         q->op    = p->op;
         q->label = p->label;
         q->l     = (expr *) p->l;     /* yet unknown */
         q->r     = (expr *) p->r;     /* yet unknown */
         if( q->fix = p->fix)
            fix_link( p, q, r++);
         else
            q->zonk.t = (expr *) p->t; /* yet unknown */


         for( x = rhead; x; x = x->next)
            if( ! x->hacked && (word) x->ref == i)
            {
               POINTER_CHECK( x);
               POINTER_CHECK( q);
               x->ref    = q;
               x->hacked = 1;
               break;
            }
      }

      MESS("the second routine");
      {
         register expr huge   *p;
         register word        i;

         for( i = (word) ebytes, y = pae + 1; i; i--, y++)
         {
            p = *y;
            POINTER_CHECK( p);
            if( ! p->fix)
            {
#if ! VERSION
               if( p->hacked & 0x4)
                  nierror("Overwriting pointer with bogus address");
               p->hacked |= 0x4;
#endif                     
               p->zonk.t = pae[ (word) p->zonk.t];
               POINTER_CHECK( p->zonk.t);
            }
#if ! VERSION            
            if( p->hacked & 0x3)
               nierror("Overwriting pointer with bogus address");
            p->hacked |= 3; 
#endif            
            p->l = pae[ (word) p->l];
            p->r = pae[ (word) p->r];
            POINTER_CHECK( p->l);
            POINTER_CHECK( p->r);
         }
      }
   }
   LEAVE();
}


/* ---------------------------------------------------------- */
/*      The first symbol link stage. What is happening ?      */
/* We already hook up the as of yet untabled symbols to the   */
/* already pointered ref (excepting of course >>EXPR<<)       */
/* ---------------------------------------------------------- */
void  sym_link()
{
   register linksymbol huge   *p;
   register ref        huge   *r, huge *old;
   register word              i,norefs;

   ENTER("sym_link");
   for( r = rhead, p = py, i = (word) ybytes; i; i--, p++)
   {
      MESS("loop");
      POINTER_CHECK( p);
      INTEGRITY_CHECK();
      if( norefs = (lword) p->refs)
      {
         MESS("-de-loop");
         for( p->refs = r; norefs > 1; r = r->next, norefs--);
         POINTER_CHECK( r);
         old = r;
         r   = r->next;
         POINTER_CHECK( r);
         old->next = 0;
      }
      else
         if( ! (p->type & L_ZERO))
         {
            MESS("-and-loop");
            POINTER_CHECK( p);
            INTEGRITY_CHECK();
            p->val += diff;               /* Else update the value */
            INTEGRITY_CHECK();
         }
   }
   DEL_WATCH( &rhead);
   LEAVE();
}


#define l_unvalued( x)  (x)->refs
#define l_valued( x)    ! l_unvalued( x)

void sym2_link()
{
   register linksymbol huge   *p = py;
   register label      huge   *q;
   register lword             i  = ybytes;
   char                       *s;

   ENTER("sym2_link");
   while( i--)
   {
      MESS("A");
      POINTER_CHECK( p);
      s = str_alloc( SIGNIFICANT + 1);            /* improve dat */
      {
         register char huge *src = p->name,
                       huge *dst = s;

#if SIGNIFICANT == 8
         *dst++ = *src++;  /* SIGNIFICANT == 8 */
         *dst++ = *src++;
         *dst++ = *src++;
         *dst++ = *src++;
         *dst++ = *src++;
         *dst++ = *src++;
         *dst++ = *src++;
         *dst++ = *src++;
         *dst   =0;
#else
         register int   j = SIGNIFICANT;

         while( j--)
            *dst++ = *src++;
         *dst = 0;
#endif
      }
      if( ! (q = find_label( s)))
         (q = enter_llabel( s, p->val, p->type))->refs = p->refs;
      else
        if( ! (q->type & p->type & L_MASK1))
            nserror( "label type mismatch", s);
         else
            if( l_valued( q))
               if( l_valued( p))
                  nserror("Doubly defined labeled", s);
               else
               {
                  POINTER_CHECK( q);

                  /* Case: LABEL has value, we got some refs */
                  q->refs = p->refs;
                  entrefer( q);
               }
            else
               if( l_valued( p))
               {
                  POINTER_CHECK( q);
                  /* Case: Label has refs, we got the value */
                  q->val  = p->val;
                  entrefer( q);
               }
               else
               {
                  /* Just append our reflist */
                  register ref huge  *x = q->refs;

                  POINTER_CHECK( q);
                  while( x->next)
                     x = x->next;
                  POINTER_CHECK( x);
                  x->next = p->refs;
               }

      LABEL_CHECK( q);
      if( l_unvalued( p))
      {
         register word  i;
         fix huge       *f;

         for( i = (word) ebytes; i; i--)
            if( pae[i]->fix == FIX_LABEL &&
                ! (f = pae[i]->zonk.fixp)->hacked &&
                (word) f->poof.label == p->no)
            {
               POINTER_CHECK( f);
               POINTER_CHECK( q);
               f->poof.label = q;
               f->hacked     = 1;
               break;
            }
      }
      p++;
   }
   LEAVE();
}

