/* ---------------------------------------------------------------------- */
/*                   Copyright (C) 1991 by Natuerlich!                    */
/*                      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 OSBIND
#include "code.h"
#define _EXPR_ 1
#include "exprfast.h"
#include "op.h"
#include "ldebug.h"

#if VERSION
ref huge    *rp;
expr huge   *ep;
lexpr huge  *lp;
#endif

#if DEBUG
# define xreturn   \
    LEAVE();       \
    return
#else
#  define xreturn   return
#endif

exp_m huge  *ex_h;

#if ! LINKER
unsigned    branch_line;
#endif

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

/* ---------------------------------------------------------- */
/*   Entrefern von den der referenz list. Ist mal ein Label   */
/*   mit ner value versehen worden, kann die ref abgehakt     */
/*   werden. Danach brauch dieses label nie wieder 'ne ref    */
/* ---------------------------------------------------------- */
void entrefer( p)
label huge  *p;
{
   ref huge             *q;
   register expr huge   *r;

   POINTER_CHECK( p);
   ENTER("entrefer");
   for( q = p->refs; q ; q = q->next)
   {

      POINTER_CHECK( q);
      (r = q->ref)->val = p->val;   /* init expression */
      POINTER_CHECK( r);
      r->label = 0;
#if DEBUG
      dump_expr( r);
#endif
#if ! LINKER
      branch_line = q->line;
#else
      INTEGRITY_CHECK();
#endif
      if( r)
         recalc( r);
   }
   p->refs = 0;
   LEAVE();
}

#if ! LINKER
# define xe_pcrel( x)  ! runnable && e_pcrel( x)
extern int  runnable;
static char fwd_loss[] = "label already known as PC-relative";
#else
# define xe_pcrel       e_pcrel
#endif

/* ---------------------------------------------------------- */
/* Jaja, mal wat in deutsch. Falls ein Label endlich einen    */
/* Wert bekommen hat und inner ref ein entry auf eine expr    */
/* pointed, dann wird versucht von da aus soviel wie m”glich  */
/* von unten nach oben auszuwerten. Hat alles geklappt, dann  */
/* kann man endlich die s-expr dahin schreiben, wo sie eigen- */
/* lich l„ngst sein sollte (Backpatching).                    */
/* Da wir brigens fr jeden Scheiž einen eigenen Mallocer    */
/* haben ist es eigentlich kein Problem in NASM65 einen       */
/* GarbageCollector einzubauen. Das w„rs noch!!               */
/* For our befuddled english-speaking readers. The various    */
/* MESS messages are gibberish.                               */
/* ---------------------------------------------------------- */
void recalc( p)
register expr huge *p;
{
   register fix huge  *f;
   register byte huge *q;

   ENTER("recalc");
   for(;;)
   {
#if DEBUG
      dump_expr( p);
#endif
      if( ! p->fix)
         goto nofix;

      f = p->zonk.fixp;
      POINTER_CHECK( f);
      switch( p->fix)       /* Mmmh sind wir schon so weit ?? */
      {
         case FIX_NOTHING:
            LEAVE();
            return;

         case FIX_WCODE  :
            MESS("Preferring the (1) wonderful");
            q = __program + f->poof.block->index + f->index;
            POINTER_CHECK( q);
            adpoke( q, p->val);   /* pack den Wert rein */
            LEAVE();
            return;

         case FIX_DCODE  :
            MESS("Preferring the wonderful");
            q = __program + f->poof.block->index + f->index;
            POINTER_CHECK( q);
            adbyte( q, p->val);   /* pack den Wert rein */
            LEAVE();
            return;

         case FIX_BRANCH  :
         {
            register sword  i;

            MESS("The worx");
            i = p->val - p->aux;
#if ! LINKER
            if( i < -128 || i > 127)
               nberror("Branch too long", branch_line);
#else
            if( i < -128 || i > 127)
               nerror("Branch too long");
#endif
            q = __program + f->poof.block->index + f->index;
            POINTER_CHECK( q);
            apoke( q, i);       /* pack den Wert rein */
            LEAVE();
            return;
         }

         case FIX_ZCODE  :
         case FIX_SZCODE :
            MESS("Admiring very small wonders");
            q = __program + f->poof.block->index + f->index;
            POINTER_CHECK( q);
            if( xe_pcrel( p))
               cure_patch( f->imm, p->aux);
            if( p->fix == FIX_ZCODE)
            {
               MESS("+");
               apoke( q, p->val);               /* pack den Wert rein */
            }
            else
            {
               extern byte    scrtab[];

               MESS("*");
               apoke( q, scrtab[ p->val]);   /* pack'n Wert rein */
            }
            MESS("-");
            LEAVE();
            return;

         case FIX_LABEL :
         {
            register label huge  *q = f->poof.label;

            MESS("Doing the strange first");
            IMESS("p->t @$%lX", (lword) p->zonk.t, 4);
            q->val = p->val;                    /* ok mach's dem */
#if ! LINKER
            if( ! runnable && (q->type & L_EQU))
            {
               nserror( fwd_loss, q->name);
               goto skip;
            }
#endif
            entrefer( q);
skip:
            LEAVE();
            return;
         }

         default :
            nierror("Invalid fix request");

nofix:
         case 0 :
            MESS("doing gods work 'ere, yessir!");
            p = p->zonk.t;
         {
            register expr huge *l = p->l,
                          huge *r = p->r;

            if( unvalued( l) || (r && unvalued( r)))  /* if L or R */
            {
               LEAVE();
               return;                       /* USED TO BE A BREAK */
            }
            do_calc( l, r, p->op & O_BITS);
            p->val  = l->val;
            if( is_lsbmsb( p->op))
               p->aux = l->aux;
            p->op = l->op;
            p->l  = p->r = 0;       /* leave kids to the garbage man */
         }
      }
   }
}

/* ---------------------------------------------------------- */
/*  Do the expression calculation that needs to be done for   */
/*  statements like     LDA #>FOO&[$FF+NMIEN]/2               */
/*  (never mind the sense...)                                 */
/* ---------------------------------------------------------- */
void  do_calc( l, r, op)
register expr huge   *l, huge *r;
int                  op;
{
#if ! LINKER
   extern int  runnable;
#endif
   static char err[]      = "Can't use arithemtic w/relocatable address",
               div_zero[] = "You can't / or \\ with zero";

   ENTER("do_calc");
   POINTER_CHECK( l);
   POINTER_CHECK( r);
#if ! LINKER
   if( runnable)
      if( r)
         switch( op)
         {
            case O_ADD : l->val += r->val; xreturn;
            case O_SUB : l->val -= r->val; xreturn;
            case O_MUL : l->val *= r->val; xreturn;
            case O_DIV : if( ! r->val)
                            nerror( div_zero);
                         else
                            l->val /= r->val;
                         xreturn;
            case O_MOD : if( ! r->val)
                            nerror( div_zero);
                         else
                            l->val %= r->val;
                         xreturn;
            case O_AND : l->val &= r->val; xreturn;
            case O_EOR : l->val ^= r->val; xreturn;
            case O_OR  : l->val |= r->val; xreturn;
            case O_EQ  : l->val  = l->val == r->val; xreturn;
            case O_BAND: l->val  = l->val && r->val; xreturn;
            case O_BOR : l->val  = l->val || r->val; xreturn;
            case O_NEQ : l->val  = l->val != r->val; xreturn;
            case O_GEQ : l->val  = l->val >= r->val; xreturn;
            case O_LEQ : l->val  = l->val <= r->val; xreturn;
            case O_GT  : l->val  = l->val >  r->val; xreturn;
            case O_LT  : l->val  = l->val <  r->val; xreturn;
         }
      else
         switch( op)
         {
            case O_INC : l->val++; xreturn;
            case O_DEC : l->val--; xreturn;
            case O_BNOT: l->val   = ! l->val; xreturn;
            case O_MIN : l->val   = - l->val; xreturn;
            case O_LSB : l->aux   = l->val;        /* stands for LSB O_LT */
                         l->val  &= 0xFF;
                         xreturn;
            case O_MSB : l->aux   = l->val;        /* stands for MSB O_GT */
            {
               byte huge   *p = (byte huge *) &l->val;
#if ! BIGENDIAN
               p[1] = *p;
               *p   = 0;
#else
               *p   = p[1];
               p[1] = 0;
#endif
               xreturn;
            }
         }
#endif
   if( r)
      switch( (e_pcrel( l) << 1) | e_pcrel( r))
      {
         case 0 :                        /* value  op  value  ->  value */
            MESS("val op val");
            switch( op)
            {
               case O_ADD : l->val += r->val; goto iszerop;
               case O_SUB : l->val -= r->val; 
                            if( r->val & O_ZEROP)
                               goto isvalue;  
                            goto issame;
               case O_MUL : l->val *= r->val; goto isvalue;
               case O_DIV : if( ! r->val)
                               nerror( div_zero);
                            else
                               l->val /= r->val;
                            goto isvalue;
               case O_MOD : if( ! r->val)
                               nerror( div_zero);
                            else
                               l->val %= r->val;
                            goto isvalue;
               case O_AND : l->val &= r->val; goto isvalue;
               case O_EOR : l->val ^= r->val; goto isvalue;
               case O_OR  : l->val |= r->val; goto isvalue;
               case O_EQ  : l->val  = l->val == r->val; goto isvalue;
               case O_BAND: l->val  = l->val && r->val; goto isvalue;
               case O_BOR : l->val  = l->val || r->val; goto isvalue;
               case O_NEQ : l->val  = l->val != r->val; goto isvalue;
               case O_GEQ : l->val  = l->val >= r->val; goto isvalue;
               case O_LEQ : l->val  = l->val <= r->val; goto isvalue;
               case O_GT  : l->val  = l->val >  r->val; goto isvalue;
               case O_LT  : l->val  = l->val <  r->val; goto isvalue;
            }

         case 1 :                        /*  value  op  reladr  ->  ??? */
            MESS("val op reladr");
            switch( op)
            {
               case O_ADD : l->val += r->val; goto ispcrel;
               case O_SUB :
               case O_MUL :
               case O_DIV :
               case O_MOD :
               case O_AND :
               case O_EOR :
               case O_OR  : nerror( err);   l->val = 0; goto isvalue;
               case O_EQ  : l->val  = l->val == r->val; goto isvalue;
               case O_BAND: l->val  = l->val && r->val; goto isvalue;
               case O_BOR : l->val  = l->val || r->val; goto isvalue;
               case O_NEQ : l->val  = l->val != r->val; goto isvalue;
               case O_GEQ : l->val  = l->val >= r->val; goto isvalue;
               case O_LEQ : l->val  = l->val <= r->val; goto isvalue;
               case O_GT  : l->val  = l->val >  r->val; goto isvalue;
               case O_LT  : l->val  = l->val <  r->val; goto isvalue;
            }

         case 2 :                        /*  reladr  op  value  ->  ??? */
            MESS("reladr op val");
            switch( op)
            {
               case O_ADD : l->val += r->val; goto ispcrel;
               case O_SUB : l->val -= r->val; goto ispcrel;
               case O_MUL :
               case O_DIV :
               case O_MOD :
               case O_AND :
               case O_EOR :
               case O_OR  : nerror( err);   l->val = 0; goto isvalue;
               case O_EQ  : l->val  = l->val == r->val; goto isvalue;
               case O_BAND: l->val  = l->val && r->val; goto isvalue;
               case O_BOR : l->val  = l->val || r->val; goto isvalue;
               case O_NEQ : l->val  = l->val != r->val; goto isvalue;
               case O_GEQ : l->val  = l->val >= r->val; goto isvalue;
               case O_LEQ : l->val  = l->val <= r->val; goto isvalue;
               case O_GT  : l->val  = l->val >  r->val; goto isvalue;
               case O_LT  : l->val  = l->val <  r->val; goto isvalue;
            }

         case 3 :
            MESS("reladr op reladr");
            switch( op)
            {
               case O_SUB : l->val -= r->val; goto isvalue;
               case O_ADD :
               case O_MUL :
               case O_DIV :
               case O_MOD :
               case O_AND :
               case O_EOR :
               case O_OR  : nerror( err);   l->val = 0; goto isvalue;
               case O_EQ  : l->val  = l->val == r->val; goto isvalue;
               case O_BAND: l->val  = l->val && r->val; goto isvalue;
               case O_BOR : l->val  = l->val || r->val; goto isvalue;
               case O_NEQ : l->val  = l->val != r->val; goto isvalue;
               case O_GEQ : l->val  = l->val >= r->val; goto isvalue;
               case O_LEQ : l->val  = l->val <= r->val; goto isvalue;
               case O_GT  : l->val  = l->val >  r->val; goto isvalue;
               case O_LT  : l->val  = l->val <  r->val; goto isvalue;
            }
         default:
            nierror("Algorithmic fuck up!");
      }
   else
      if( e_pcrel( l))
         switch( op)
         {
            case O_INC : l->val++; goto ispcrel;
            case O_DEC : l->val--; goto ispcrel;
            case O_BNOT:
            case O_MIN : l->val   = 0;
                         nerror( err);
                         goto isvalue;

            case O_LSB : l->aux   = l->val;        
                         l->val  &= 0xFF;
                         goto ispcrel;
            case O_MSB : l->aux   = l->val;        
            {
                         byte huge  *p = (byte huge *) &l->val;
#if ! BIGENDIAN
                         p[1] = *p;
                         *p   = 0;
#else
                         *p   = p[1];
                         p[1] = 0;
#endif
                         goto ispcrel;
            }
         }
      else
         switch( op)
         {
            case O_INC   : l->val++; goto issame;
            case O_DEC   : l->val--; goto issame;
            case O_BNOT  : l->val = ! l->val; goto isvalue;
            case O_MIN   : nerror( err); l->val = 0; goto isvalue;
            case O_LSB   : l->val  &= 0xFF; goto issame;
            case O_MSB   :                           
            {
               byte huge  *p = (byte huge *) &l->val;
#if ! BIGENDIAN
               p[1] = *p;
               *p   = 0;
#else
               *p   = p[1];
               p[1] = 0;
#endif
            }
            goto isvalue;
         }

ispcrel:
   l->op = O_PCREL | op;
   xreturn;
   
iszerop:
   l->op |= r->op & O_ZEROP;

issame:
   l->op = (l->op & O_BITS) | op;
   xreturn;
   
isvalue:   
   l->op = op;
   xreturn;
}

/* ---------------------------------------------------------- */
/*                   Mallocer for the reflist                 */
/*                                                            */
/*   (Keeping the fingers crossed that this is faster than a) */
/*                       (simple malloc)                      */
/*       For added speed put the first "if" into a #define    */
/* ---------------------------------------------------------- */
   make_mallocer( ref, REFMAX, ref_alloc);

/* ---------------------------------------------------------- */
/*                   Mallocer for expressions                 */
/*                                                            */
/*   (Keeping the fingers crossed that this is faster than a) */
/*                       (simple malloc)                      */
/* ---------------------------------------------------------- */

expr *sexp_alloc()
{
   register exp_m huge   *p;
   extern word          _a_expr, _m_expr;
   extern lword         _s_expr;

#if STATISTICS
   _a_expr++;
#endif
   if( (p = ex_h) && p->free--)
      return( p->tab++);

#if STATISTICS
   _m_expr++;
   _s_expr = sizeof(exp_m) + EXPMAX * sizeof( expr);
#endif
   p         = (exp_m huge *) nmalloc( (long) sizeof(exp_m) +
                                         EXPMAX * sizeof( expr));
   p->free   = (int) EXPMAX - 1;
   p->tab    = (expr huge *) ((char huge *) p + sizeof( exp_m));
   bzero(  p->tab, (long) EXPMAX * sizeof( expr));    /* Clear memory */
   p->before = (exp_m huge *) ex_h;
   ex_h      = p;
   return( p->tab++);
}

/* ---------------------------------------------------------- */
/* Try to free the expression. This will work easily in cases */
/* like  LDA #2  or STA WSYNC (where WSYNC is already known   */
/* Unless we are going to use GC we have to do this 'cause I  */
/* expect approx. 2 expr/line (makes 60 bytes per line)       */
/*    We clear it so that it can be properly reused again     */
/* ---------------------------------------------------------- */
#if DORECLAIM
void    expr_tryfree( e)
register expr huge   *e;
{
   extern word _efrees, _erfrees;

#if STATISTICS
   _efrees++;
#endif
   if( e == (ex_h->tab - 1))
   {
      register lword huge  *q = (lword huge *) e;
#if LINKER
# if EXPRSIZE != 26
      bzero( q, (long) sizeof( expr));
# else
#  if ! LATEPLUSPLUS /* like the good book says/be quick or dead [DLR] */
      *(word huge *)q = (word) (*q++ = *q++ = *q++ = *q++ = *q++ = *q++ = 0);
#  else
      {
         register lword  zero = 0;

         *q++ = zero; *q++ = zero; *q++ = zero; *q++ = zero;
         *q++ = zero; *q++ = zero; *(word huge *)q = 0;
      }
#  endif
# endif
#else
# if EXPRSIZE != 24 || LATEPLUSPLUS
      bzero( q, (long) sizeof( expr));
# else
#  if ! LATEPLUSPLUS
      *q = *q++ = *q++ = *q++ = *q++ = *q++ = 0;
#  else
      {
         register lword  zero = 0;

         *q++ = zero; *q++ = zero; *q++ = zero; *q++ = zero;
         *q++ = zero; *q++ = zero;
      }
#  endif
# endif
#endif
      --ex_h->tab;
      ++ex_h->free;
#if STATISTICS
      _erfrees++;
#endif
      if( e->l)
      {
         if( e->r)
            expr_tryfree( e->r);
         expr_tryfree( e->l);
      }
   }
}
#endif

#if DEBUG
#include <stdio.h>

dump_expr( p)
expr huge   *p;
{
   if( p)
   {
      fprintf( ESTREAM, "\t\tEXPR @$%8.8lX\n", p);
      fprintf( ESTREAM, "\t\tOp='%c'  Val=$%4.4X  Label=$%8.8X  ",
                  p->op, p->val, p->label);
      fprintf( ESTREAM, "Aux=$%4.4X Fixtype=%d\n",
                  p->aux, p->fix);
      fprintf( ESTREAM, "\t\tTop/Left/Rite [ $%8.8lX | $%8.8lX | $%8.8lX ]\n\n",
                  p->zonk.t, p->l, p->r);
      if( ! p->fix)
         dump_expr( p->zonk.t);
   }
}
#endif


/* [EOF][EOF][EOF][EOF][EOF][EOF][EOF][EOF][EOF][EOF][EOF][EOF][EOF][EOF]

-- Kleiner Diskurs

   Deutschland heute (1/1990):

       "Ich _weiž_ nicht ob 2+2 4 oder 5 ist
        Ich _will_ es auch gar nicht wissen
        Aus dieser Art der Fragestellung  dringt
        doch schon wieder diese rationale
        Einstellung zur Umwelt auf, von der wir doch wissen
        wo sie uns hin fhrt.

        Es ist doch jetzt einmal an der Zeit zu begreifen,
        daž wichtige Ver„nderungen durchgefhrt werden mssen.
        Und wenn die Herren Politiker sich mal wieder
        nicht dazu entschliežen k”nnen, die Probleme der
        heutigen Zeit konkret anzufassen, dann muss eben der
        Druck von der Strasse kommen" <schluck> <schnief>
               <heul>

                  APPLAUS!!
*/

