/* ---------------------------------------------------------------------- */
/*                   Copyright (C) 1991 by NatЃrlich!                     */
/*                      This file is copyrighted!                         */
/*                Refer to the documentation for details.                 */
/* ---------------------------------------------------------------------- */
#include "defines.h"
#include "nasm.h"
#include "debug.h"
#include "labels.h"
#include  NMALLOC_H
#include "exprfast.h"
#include OSBIND
#include "code.h"
#include "op.h"

extern byte    scrtab[];
extern int     runnable;
int            _in_doteq;

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

#if ! VERSION
/* ---------------------------------------------------------- */
/* There is this unfortunate problems with labels. They might */
/* be defined anywhere and with nasm65 even unknown (linked   */
/* from elsewhere). That leads to problems when generating    */
/* code. REFER and DREFER (pronounce REEFER and DEEREEFER)    */
/*    "Don't fear the REFER" (Blue Oyster Cult (almost))      */
/* build up lists of places, where labels have been used and  */
/* whether the label had a value then.                        */
/*    drefer means define and refer                           */
/* ---------------------------------------------------------- */
/* I NEVER USE XREFs                                          */
/* I NEVER LOOK AT THE SYMBOLTABLE AT THE END OF THE LISTING  */
/* WHY SHOULD I WASTE MEMORY FOR FEATURES I DON'T WANT        */
/* Therefore I don't need drefer just refer. That also means  */
/* I can compress the "ref" to 8 bytes. Now if I could get */
/* rid of that one pointer.                                   */
/* ---------------------------------------------------------- */
void refer( l, ex)
label huge  *l;
expr huge   *ex;
{
   register ref huge    *p = ref_alloc();
   extern buffer huge   *bp;

   ENTER("refer");
   IMESS("label @$%lX", (lword) l, 4);
   p->ref   = ex;       /* Note expression that needs fixing  */
   p->line  = bp->line; /* (*) */
   p->next  = l->refs;  /* put it in the first slot...        */
   l->refs  = p;
   l->type |= L_REF;
   LEAVE();
}
#endif

/* ---------------------------------------------------------- */
/* Recursively copy expression (deep copy or shallow copy ?)  */
/* and if needed refer as of yet unkown labels.               */
/* code looks very nice, but can conceivably use some optimi- */
/* zation.                                                    */
/* ---------------------------------------------------------- */
expr *copyexpr( p)
register expr huge  *p;
{
   ENTER("copyexpr");
   if( ! p)
   {
      LEAVE();
      return( 0);
   }

   {
      register expr huge *q = exp_alloc();

#if EXPRSIZE != 24 || PHILOSOPHICAL_PROBLEM
      bcopy( p, q, (lword) sizeof( expr));
#else
      *((lword huge *) q)++ = *((lword huge *) p)++;  /* label          */
      *((lword huge *) q)++ = *((lword huge *) p)++;  /* no & val       */
      *((lword huge *) q)   = *(lword huge *) p;      /* aux & op & fix */
      (char huge *) q -= 8;
      (char huge *) p -= 8;
#endif
      if( q->label)                        /* Tell label that we need a  */
         refer( q->label, q);              /* fix (if we need a fix)     */
      if( q->l = copyexpr( p->l))
         q->l->zonk.t = q;
      if( q->r = copyexpr( p->r))
         q->r->zonk.t = q;
      LEAVE();
      return( q);
   }
}

#if ! VERSION
/* ----------------------------------------------------------- */
/*        Create an s_expression node by planting a value      */
/*                   (could be a label as well)                */
/* ----------------------------------------------------------- */
expr *ival_pl( value)
word  value;
{
   expr huge   *p = exp_alloc();

   ENTER("ival_pl");
   MESS("Wolf! Right here and now!");
   p->val = value;
   LEAVE();
   return( p);
}
#endif

expr *lval_pl( name)
char huge   *name;
{
   register expr huge   *p = exp_alloc();
   register label huge  *q;

   ENTER("lval_pl");
   MESS("Wolf! Right here and now!");
   if( q = find_label( name))
   {
      if( q->refs)
      {
         p->label = q;
         refer( q, p);
      }
      else
         p->val = q->val;
      IMESS("p->op %u", (lword) p->op, 2);
      p->op  = ((q->type & L_PC)   ? O_PCREL : 0) |
               ((q->type & L_ZERO) ? O_ZEROP : 0);
   }
   else
      if( runnable && _in_doteq)
         enter_elabel( name, ival_pl( __pc), L_NORMAL);
      else
      {
         p->label = enter_flabel( name, p);
         if( ! runnable)
            p->op = O_PCREL;       /* cause fwd then is PC rel */
      }
   LEAVE();
   return( p);
}

/* ----------------------------------------------------------- */
/*     Create an s_expression node by planting an operation    */
/*   There is still a bug in this as expressions like foo+2+3  */
/*            are not simplified if foo is unvalued            */
/* ----------------------------------------------------------- */
expr *op_pl( op, left, rite)
register int         op;
register expr huge   *left, huge *rite;
{
   register int   lop;
   int            rop;

   ENTER("op_pl");
   if( (lop = left->op) == (O_PCREL | (O_MSB & O_LSB)) ||
       (rite && (rop = rite->op) == (O_PCREL | (O_MSB & O_LSB))))
   {
      nerror("You can't calculate with a short PC relative expression");
      return( left);
   }
   if( valued( left))         /* if left is value */
   {
      if( ! rite || valued( rite))
      {
         MESS("Just calcing it");
         do_calc( left, rite, op);                      /* calc it */
         expr_tryfree( rite);
         IMESS("After do_calc %ld", (lword) lop, 4);
         LEAVE();
         return( left);
      }
   }
   else
      if( rite && valued( rite))
         switch( lop)
         {
            case O_ADD :
               if( is_addsub( op))
               {
                  if( valued( left->l))
                     do_calc( left->l, rite, op);
                  else
                     if( valued( left->r))
                        do_calc( left->r, rite, op);
                     else
                        break;
                  expr_tryfree( rite);
                  return( left);
               }
               break;
               
            case O_SUB :
               if( op == O_ADD)
               {
                  if( valued( left->l))
                     do_calc( left->l, rite, O_ADD);
                  else
                     if( valued( left->r))
                        do_calc( left->r, rite, O_SUB);
                     else
                        break;
               }
               else
                  if( op == O_SUB)
                  {
                     if( valued( left->l))
                        do_calc( left->l, rite, O_SUB);
                     else
                        if( valued( left->r))
                           do_calc( left->r, rite, O_ADD);
                        else
                           break;
                  }
                  else
                     break;
               expr_tryfree( rite);
               return( left);

            case O_MUL :
               if( op == O_MUL)
               {
                  if( valued( left->l))
                     do_calc( left->l, rite, op);
                  else
                     if( valued( left->r))
                        do_calc( left->r, rite, op);
                     else
                        break;
                  expr_tryfree( rite);
                  return( left);
               }
               break;

            case O_DIV :
               if( op == O_DIV)
               {
                  if( valued( left->l))
                     do_calc( left->l, rite, op);
                  else
                     if( valued( left->r))
                        do_calc( left->r, rite, O_MUL);
                     else
                        break;
                  expr_tryfree( rite);
                  return( left);
               }
         }

   MESS("Creating a node");
   {
      register expr huge   *p;

      if( rite && valued( rite) && rite->val == 1)
      {
         if( is_addsub( op))
         {
            p    = rite;
            rite = 0;
            op   = op == O_ADD ? O_INC : O_DEC;
            goto asusual;
         }
      }
      p = exp_alloc();
      
asusual:
      lop &= O_PCREL;
      rop &= O_PCREL;
      switch( op)
      {
         case O_INC :
         case O_DEC :
         case O_MSB :
         case O_LSB :
            p->op = lop;
            break;
   
         case O_ADD :
            p->op = lop ^ rop;
            break;
   
         case O_SUB :
         {
            static char what[] = { 0, 0, 0x80, 0 };
            
            p->op = what[ ((lop != 0) << 1) | (rop != 0) ];
         }
      }
         
      if( (left->op | (rite ? rite->op : 0)) & O_ZEROP)
         switch( op)
         {
            case O_SUB :
               if( rite->op & O_ZEROP)
                  break;
            case O_ADD :
               if( ! p->op)
            case O_INC :
            case O_DEC :
            case O_LSB :
                  p->op |= O_ZEROP;
         }
         
      p->op        |= op;
      p->l          = left;
      left->fix     = 0;         /* for macros */
      left->zonk.t  = p;
      if( p->r = rite)
         rite->zonk.t = p;
      LEAVE();
      return( p);
   }
}

#if ! VERSION
lexpr *lex_pl( s, ex)
expr huge   *ex;
char        *s;
{
   register lexpr    *p = lex_alloc();

   ENTER("lex_pl");
   IMESS("planting string $%8.8lX", (lword) s, 4);
   IMESS("    with expr   $%8.8lX", (lword) ex, 4);
   p->string = s;
   p->expr   = ex;
   p->next   = 0;
   LEAVE();
   return( p);
}
#endif

lexpr *slex_ch( l, r)
register lexpr huge  *l, huge *r;
{
   ENTER("slex_ch");
   IMESS("Chaining $%8.8lX", (lword) r, 4);
   IMESS("      to $%8.8lX", (lword) l, 4);
   l->next = r;
   LEAVE();
   return( l);
}


int   lex_cnt( l)
register lexpr huge  *l;
{
   register int   i = 0;

   ENTER("lex_cnt");
   while( l)
   {
      i++;
      l = l->next;
   }
   LEAVE();
   return( i);
}

/* ---------------------------------------------------------- */
/*                       Should be a macro                    */
/*                   Error checking elsewhere                 */
/* ---------------------------------------------------------- */
lexpr *lex_get( l, no)
register lexpr huge  *l;
register word  no;
{
   ENTER("lex_get");
   while( l && no--)
      l = l->next;
   IMESS("Returning L-expression @$%lx", (lword) l, 4);
   LEAVE();
   return( l);
}

/* ---------------------------------------------------------- */
/*                   Mallocer for lexpressions                */
/* make this mallocing scheme a macro sometime in the future..*/
/*                                                            */
/*   (Keeping the fingers crossed that this is faster than a) */
/*                       (simple malloc)                      */
/* ---------------------------------------------------------- */
   make_mallocer( lexpr, LEXPMAX, lex_alloc);

/* (*) This isn't correct and fails when branches are made out of
       a buffer (like macro) or the buffer was changed.
*/