/* ---------------------------------------------------------------------- */
/*                   Copyright (C) 1991 by Natrlich!                     */
/*                      This file is copyrighted!                         */
/*                Refer to the documentation for details.                 */
/* ---------------------------------------------------------------------- */
#include <stdio.h>
#include "defines.h"
#include "nasm.h"
#include "debug.h"
#include "labels.h"
#include "exprfast.h"

extern label huge *h_local[SEP],  huge *t_local[SEP],  huge *l_local,
             huge *h_global[SEP], huge *t_global[SEP], huge *l_global,
             huge *h_macro[SEP],  huge *t_macro[SEP],  huge *l_macro;
extern int        u_local, u_global, u_macro;
extern char       is_where[], err_defined[];
extern int        _in_macro, runnable, _in_instr;
extern lword      l_hash;


label *find_label( s)
register char  *s;
{
   register label huge  *p;
   register lword       hash;
   register int         res;

   ENTER("find_label");

   l_hash = hash = calc_hash( s + 1);
   p = h_macro[ u_macro = is_where[*s]];
   while( p && (p->hash < hash ||
            (p->hash == hash && (res = strcmp( p->name, s)) < 0)))
      p = p->next;
   l_macro = p;

   if( ! found( p, hash) || res)  /* if not found or at end of table */
   {
      if( *s == '?' || *s == ':')
      {
         l_hash = hash = calc_hash( s + 2);
         p = h_local[ u_local = is_where[ s[1]]];
         while( p && (p->hash < hash ||
                (p->hash == hash && (res = strcmp( p->name, s)) < 0)))
            p = p->next;
         l_local = p;
      }
      else
      {
         p = h_global[ u_global = is_where[*s]];
         while( p && (p->hash < hash ||
                (p->hash == hash && (res = strcmp( p->name, s)) < 0)))
            p = p->next;
         l_global = p;
      }
      if( ! found(p, hash) || res)
      {
         LEAVE();
         return( 0);
      }
   }
   LEAVE();
   return( p);
}


void  undefine( s)
char  *s;
{
   register label huge  *p;
   int                  is_local = (*s == '?' || *s == ':');
   
   u_local = u_global = -1;     /* if we find a macro label this remains -1 */
   if( p = find_label( s))
      if( ! p->refs)
         if( u_local < 0 && u_global < 0)
         {
            auslinker( h_macro, t_macro, l_macro, u_macro, p);
         }
         else
            if( is_local)
            {
                auslinker( h_local, t_local, l_local, u_local, p);
            }
            else
            {
               auslinker( h_global, t_global, l_global, u_global, p);
            }
      else
         nerror("Label has pending forward references");
   else
      nwarning("Label is unknown to me");
}
/* ---------------------------------------------------------- */
/*         This routine is called when a forward label        */
/*         is encountered:                                    */
/*    -- Note that the tables have already been searched --   */
/* e.g.:                                                      */
/*             sta foo                                        */
/* not:  foo   lda foo                                        */
/* not:  foo:  =  56                                          */
/* not:  foo   *=$600                                         */
/* ---------------------------------------------------------- */
label *enter_flabel( s, ex)
char                 *s;
register expr huge   *ex;
{
   register label    *p;
   int               is_local = (*s == '?' || *s == ':');

   ENTER("enter forward label");
#if DEBUG
   fprintf( ESTREAM, "with %s as name, and some s_expr @$%lX\n", s, ex);
#endif
   if( is_local)
      p = llab_alloc();
   else
      p = lab_alloc();
   p->refs = 0;
   p->name = s;
   p->hash = l_hash;
   if( runnable)
      p->type = L_NORMAL | L_REF;
   else
      p->type = L_PC | L_NORMAL | L_REF;
   refer( p, ex);

   if( is_local)
   {
      MESS("linking into local");
      einlinker( h_local, t_local, l_local, u_local, p);
   }
   else
      if( _in_macro)
      {
         MESS("linking into macro");
         einlinker( h_macro, t_macro, l_macro, u_macro, p);
      }
      else
      {
         MESS("linking into global");
         einlinker( h_global, t_global, l_global, u_global, p);
      }
   LEAVE();
   return( p);
}

void  page0decl( s)
register char  *s;
{
   register label huge  *p;
   register expr huge   *ex;
   int                  is_local = (*s == '?' || *s == ':');

   ENTER("page0decl");
   if( runnable)
   {
      nwarning(".ZEXT probably not meaningful with -r option");
      nmessage("But we're gonna do it anyway!");
   }
   if( ! find_label( s))      /* if not found or at end of table */
      if( ! is_local)
      {
         p = lab_alloc();
         p->refs = 0;
         p->name = s;
         p->hash = l_hash;
         p->type = L_ZERO | L_LINKZERO;
         ex = exp_alloc();
         ex->fix = FIX_NOTHING;
         refer( p, ex);
         einlinker( h_global, t_global, l_global, u_global, p);
      }
      else
         nserror("Can't use local labels here", s);
   else
      nserror( err_defined, s);
   LEAVE();
}

/* ---------------------------------------------------------- */
/*   The variety of label dumping routines, really BEG for a  */
/*   a one-that-does-it-all-routine                           */
/* ---------------------------------------------------------- */
void dump_open()
{
   register int         i, j, k;
   int                  f = 0;
   register label huge  *p;
   
   ENTER("dump_open");
   for( k = 0; k != 2; k++)                           
      for( i = 0; i != SEP; i++)                      
         if( p = k ? h_macro[ i] : h_global[ i])
                      do
                   if( dumpable( p))
               {
                  if( ! f)
                  {
                     f = 1;
                     j = 0;
                     printf("Linkable symbols:\n");
                  }
                  if( ++j == 7)
                  {
                     j = 0;
                     putchar( '\n');
                  }
                  printf( "%c%-8.8s ",
                         (char) (p->type & L_ZERO 
                                  ?  (p->refs ? ',' : '#')
                                  :  (p->refs ? ' ' : '*')),  
                          p->name);
               }
            while( p = p->next);      
   puts( "\n");
   LEAVE();
}

/* ---------------------------------------------------------- */
/*     Remove all labels starting with @ from macro table     */
/* ---------------------------------------------------------- */
void clean_mats()
{
   register label huge  *p;
   
   ENTER("dump_open");
   for( p = h_macro[ 1]; p; p = p->next)
      if( p->refs)
         nerror("'@' macro label still has open refs", p->name);
   h_macro[ 1] = 0;
   LEAVE();
}

