/* ---------------------------------------------------------------------- */
/*                   Copyright (C) 1991 by Natrlich!                     */
/*                      This file is copyrighted!                         */
/*                Refer to the documentation for details.                 */
/* ---------------------------------------------------------------------- */
/*    In the faint hope that it will help searching speed, labels will    */
/*      be alphabetisized (also I do have already code for that...)       */
/* ---------------------------------------------------------------------- */
#include <stdio.h>
#include "defines.h"
#include "nasm.h"
#include "debug.h"
#include "labels.h"
#define  KEINE_CODE_INNEREIEN
#include "code.h"
#include "fix.h"
#include "exprfast.h"
#include "op.h"

#if ! DORECLAIM
# define  expr_tryfree( x)
#endif

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 lword      l_hash;

extern char    err_defined[], warn_0fwd[], warn_equfwd[], buf[];
extern int     _in_macro, runnable;
int            _in_instr;
extern char    *str_alloc();

/* ---------------------------------------------------------- */
/*             This routine is called when a label            */
/* that is PC-relative is encountered:                        */
/*       foo:  lda #56                                        */
/*       bar   *= $600                                        */
/* not:  foo   = 46                                           */
/* not:        lda foo                                        */
/* ---------------------------------------------------------- */
void  enter_pclabel( s)
register char  *s;
{
   register label huge  *p;
   int                  is_local = (*s == '?' || *s == ':');

   ENTER("enterlabel");
#if DEBUG
   fprintf( ESTREAM, "\t\tWith %s as name @$%4.4X\n", s, __pc);
#endif
   u_local = u_global = -1;   /* if we find a macro label this remains -1 */
   if( ! (p = find_label( s)))
   {
      if( is_local)
         p = llab_alloc();
      else
         p = lab_alloc();
      p->name = s;
      p->hash = l_hash;
      p->type = __pc < 0x100 ? (L_ZERO | L_PC) : L_PC;
      p->val  = __pc;
      p->refs = 0;

      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);
         }
   }
   else
   {
      IMESS("p->refs =  $%8.8X", (lword) p->refs, 4);
      if( p->refs)
      {
         if( __pc < 0x100)
            nswarning( warn_0fwd, p->name);
         p->val   = __pc;
         p->type |= L_PC;
         if( ! _in_macro && u_local < 0 && u_global < 0) /* was assumed   */
         {                                               /* macro, but is */
            auslinker( h_macro, t_macro, l_macro, u_macro, p);  /* global */
            (void) find_label( s);
            if( is_local)
            {
               MESS("linking into local");
               einlinker( h_local, t_local, l_local, u_local, p);
            }
            else
            {
               MESS("linking into global");
               einlinker( h_global, t_global, l_global, u_global, p);
            }
         }           
         if( p->type & L_EQU)
            nswarning( warn_equfwd, p->name);
         entrefer( p);
      }
      else
         if( _in_macro)
         {
            p->val   = __pc;
            p->type |= L_PC;
         }
         else
            nserror( err_defined, s);
   }
   LEAVE();
}

/* ---------------------------------------------------------- */
/*         This routine is called when a equ label            */
/*         or .= label is encountered:                        */
/* e.g.:                                                      */
/*       bar  .= $600                                         */
/*       foo   = 462                                          */
/*       bar  == 67                                           */
/* not:        lda foo                                        */
/* not:  foo:  lda #56                                        */
/* not:  foo   *=$600                                         */
/* ---------------------------------------------------------- */
static expr  edummy =
{
   0,
   0, 0, 0,
   0, FIX_NOTHING,
   0,
   0,
   0
};

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

   ENTER("enterlabel/expression");
#if DEBUG
   fprintf( ESTREAM, "Name=%s type=$%4.4X + some s_expr @$%lX\n", s, type, ex);
#endif
   if( ! (p = find_label( s)))        /* if not found or at end of table */
   {
      if( is_local)
         p = llab_alloc();
      else
         p = lab_alloc();
      p->refs = 0;
      p->name = s;
      p->hash = l_hash;
      if( unvalued( ex))         /* top is not value */
      {
jump1984:
         if( type == L_ZERO && e_pcrel( ex))
            nerror("You can't assign a fwd ref to a zeropage label");
#if LOCALDEBUG
         fprintf( ESTREAM, "label %s is yet undefined\n", s);
#endif
         p->type = type | (e_pcrel( ex) ? L_PC : 0);
         fix_lup( ex, p);
         (void) refer( p, &edummy);    /* casting for prettyness */
      }
      else
      {
         p->val = ex->val;
         if( type)
         {
            if( type & L_ZERO && p->val >= 0x100)
               nswarning( "zeropage label >= $100", s);
         }
         else
            if( p->val < 0x100 || ex->op & O_ZEROP)
               type |= L_ZERO;
         if( e_pcrel( ex))
            type |= L_PC;
         if( (type & (L_PC | L_ZERO)) == (L_PC | L_ZERO))
            nserror("Conflict between PC relativism and zeropageism", s);
         p->type = type;
         expr_tryfree( 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);
   }
   else
      if( type & L_EQU)
      {
         if( ! (p->type & L_EQU))
            nswarning( ".= assignment for a normal label", s);
         if( valued( ex))
         {
            p->val = ex->val;
            if( e_pcrel( ex))
               p->type |= L_PC;
            if( p->refs)
            {
               if( p->type & L_EQU)
                  nswarning( warn_equfwd, p->name);
               entrefer( p);
            }
            expr_tryfree( ex);
            p->type |= L_EQU;
         }
         else
            if( p->refs)
            {
               ex->fix = FIX_NOTHING;
               nserror( "Label still has open references", p->name);
            }
            else
               goto jump1984;
      }
      else
         if( p->refs)
            if( valued( ex))   
            {
               if( ex->val < 0x100)
                  nswarning( warn_0fwd, s);
               p->val  = ex->val;
               if( (e_pcrel( ex) ? L_PC : 0) ^ (p->type & L_PC))
                  nserror("Label was assumed to be pc relative", s); 
               if( p->type & L_EQU)
                  nswarning( warn_equfwd, s);
               entrefer( p);
               expr_tryfree( ex);
            }
            else
               goto jump1984;
         else
            nserror( err_defined, s);
   LEAVE();
   return( p);
}

void def_label( name, val)       /* Pyromania */
char     *name;
word    val;
{
#if ! VERSION
   name = strcpy( str_alloc( strlen( name) + 1), name);
#endif
   (void) enter_elabel( name, ival_pl( val), L_SPECIAL);
}

is_ref(s)
char  *s;
{
   label huge  *q;

   if( ! (q = find_label( s)))
      return( 0);
   return( q->type & L_REF);
}


is_def(s)
char  *s;
{
   label huge  *q;
   return( (q = find_label( s)) && ! q->refs);
}

void openrefs( table, message)
label huge  **table;
char        *message;
{
   register int         i, j, f = 0;
   register label huge  *p;

   ENTER("openrefs");
   for( i = 0; i != SEP; i++)
   {
      p = table[ i];
      while( p)
      {
         if( p->refs)
         {
            if( ! f)
            {
               f = 1;
               j = 0;
               sprintf( buf, "Some %s labels remain undefined", message);
               nerror( buf);
               putc( '\t', ESTREAM);
            }
            if( j++ == 4)
            {
               j = 0;
               putc( '\n', ESTREAM);
               putc( '\t', ESTREAM);
            }
            fputs( p->name, ESTREAM);
            putc( '\t', ESTREAM);
         }
         p = p->next;
      }
   }
   if( f)
      putc( '\n', ESTREAM);
   LEAVE();
}


void err_undefs()
{
   register int         i, j, k,
                        f = 0;
   register label huge  *p;
   
   ENTER("err_undefs");
   if( runnable)
   {
      openrefs( h_global, "global");
      openrefs( h_macro,  "macro");
   }               
   else
      for( k = 0; k != 2; k++)
         for( i = 0; i != SEP;)
            if( p =  k ? h_macro[ i++] : h_global[i++])
               do
                  if( ! (! p->refs || q_pcrel( p) || 
                         (p->type & (L_SPECIAL | L_LINKZERO)) ))
                  {
                     if( ! f)
                     {
                        f = 1;
                        j = 0;
                        nerror( "Some global non PC-relative labels remain undefined");
                        putc( '\t', ESTREAM);
                     }
                     if( j++ == 4)
                     {
                        j = 0;
                        putc( '\n', ESTREAM);
                        putc( '\t', ESTREAM);
                     }
                     fputs( p->name, ESTREAM);
                     putc( '\t', ESTREAM);
                  }
               while( p = p->next);
   if( f)
      putc( '\n', ESTREAM);
   LEAVE();
}


void do_local()
{
   register int   i;

   ENTER("do_local");
#if LOCALDEBUG
   show_all();
#endif
   openrefs( h_local, "local");
   llab_free();
   for( i = 0; i != SEP; i++)
      h_local[ i] =  t_local[ i] = 0;
   LEAVE();
}


#if LOCALDEBUG || DEBUG
#define oref   p->refs ? '*' : ' '
static char format[] =
   "%c\"%8.8s\" type=$%2.2X value=$%4.4X hash=$%8.8lX\n";

void show_all()
{
   label huge  *p;
   int         i;

   fprintf( ESTREAM, "Dumping global table:\n");
   for( i = 0; i != SEP; i++)
      if( p = h_global[ i])
      {
         do
            fprintf( ESTREAM, format, oref, p->name,
                              p->type, p->val, p->hash);
         while( p = p->next);
         putc( '\n', ESTREAM);
      }

   fprintf( ESTREAM, "\n\nDumping local table:\n");
   for( i = 0; i != SEP; i++)
      if( p = h_local[ i] )
      {
         do
            fprintf( ESTREAM, format, oref, p->name,
                              p->type, p->val, p->hash);
         while( p = p->next);
         putc( '\n', ESTREAM);
      }

   fprintf( ESTREAM, "\n\nDumping macro label table:\n");
   for( i = 0; i != SEP; i++)
      if( p = h_macro[ i])
      {
         do
            fprintf( ESTREAM, format, oref, p->name,
                              p->type, p->val, p->hash);
         while( p = p->next);
         putc( '\n', ESTREAM);
      }
}
#endif



