/* ---------------------------------------------------------------------- */
/*                   Copyright (C) 1991 by NatЃrlich!                     */
/*                      This file is copyrighted!                         */
/*                Refer to the documentation for details.                 */
/* ---------------------------------------------------------------------- */
#define DEBUG  0
#define LOCALDEBUG  DEBUG
#include <stdio.h>
#include "defines.h"
#include "nasm.h"
#include "y_tab.h"
#include "labels.h"
#include "debug.h"
#include <setjmp.h>
#include "buffer.h"
#if VERSION
#include "inputfst.h"
#include "exprfast.h"
#endif
#include OSBIND
#include NMALLOC_H
#include "code.h"

#define MB_SIZE   (sizeof( buffer) + sizeof( macro))
#define NIL   ((char *) 0)

#if __NSTDC__ && TURBO
static void    mac_done( buffer huge *),
               lxsyntax( char *);
static int     mac_get( void),
               *more_space( void);
extern int     (*gettoken)(void);
#else
static void    mac_done(),
               lxsyntax();
static int     mac_get( void);
extern int     (*gettoken)();
#endif


static macro huge *header[SEP],  huge *tailer[SEP],  huge *rover;
extern char       is_where[], ovf_loss[];
int               freshflag = 1,
                  _call_macro;
extern char       c;
extern jmp_buf    syn_panic;


#if DEBUG
#if LOCALDEBUG
int         syndebug;
#else
extern int  syndebug;
#endif
#endif

extern int           _in_if, show_expansion, show_mload;
extern lword         yylval, l_hash;
extern buffer huge   *bp, huge *hp;
int                  used, _in_macro;


/* ---------------------------------------------------------- */
/* This is a rather wasteful, but nevertheless _fast_ way of  */
/* Sucking in macros. Only once, when the boundary has been   */
/* crossed, there is a need to copy over stuff.               */
/* ---------------------------------------------------------- */
typedef struct mac_m_
{
   int huge             *s;
   struct mac_m_ huge   *before;
} mac_m;

static mac_m huge *c_h;
word              tok_remain;
static int huge   *start;      /* points to the beginning of macro */


static int   *more_space()
{
   register mac_m huge  *p;
   register lword       rem;
#if STATISTICS
   extern word       _m_macro;
   extern lword      _s_macro;
   _m_macro++;
   _s_macro = MACSPACE + sizeof( mac_m);
#endif

   ENTER("more_space");
   p    = (mac_m huge *) nmalloc( MACSPACE + sizeof( mac_m));
   p->s = (int *) ((char huge *) p + sizeof( mac_m));
   if( ! (p->before = (mac_m huge *) c_h))                /* First time ?? */
   {
      c_h = p;
      tok_remain = MACTOKMAX;
      LEAVE();
      return( start = p->s);
   }
#if ! VERSION
   if( (size_t) ((char huge *) c_h->s + MACSPACE - (char huge *) start) !=
       (lword)  ((char huge *) c_h->s + MACSPACE - (char huge *) start))
      nierror("Conversion did indeed lose some bytes in MACRO.C:more_space");
#endif
   if( start == c_h->s)
      nferror("ONE macro can't possibly be THAT long!");
   bcopy( start, p->s,
         rem = (lword) ((char huge *) c_h->s + MACSPACE - (char huge *) start));

   tok_remain = (word) ((MACSPACE - rem) / (sizeof( int) + sizeof( lword)));
   c_h = p;
   LEAVE();
   return( (int *) ((char huge *) (start = p->s) + rem));
}

/* ---------------------------------------------------------- */
/*          Functions to suck in a macro and store it.        */
/*   HIDE your register variables, if a LONGJMP is about to   */
/*   happen. Therefore no and remain are static outside.      */
/* ---------------------------------------------------------- */
static unsigned    no = 0;          /* bc/o LONGJMPs          */
void   load_macro( name)
char  *name;
{
   register int         tok;
   register int huge    *s = start;

   ENTER("load_macro");
   no = 0;
   freshflag = 1;                                  /* new yylex caller */
   if( find_macro( name))
   {
      nerror("This old assembler can't handle macro redefinitions, so you better choose another name");
      while( (tok = yylex()) && tok != T_ENDM);
      if( ! tok)
         nferror("Reached end of file while skipping to .ENDM");
      return;
   }
   if( (tok = yylex()) != T_EOL)
   {
      nwarning("Garbage after .macro <name> definition!");
      while( (tok = yylex()) != T_EOL);
   }
   inc_line();
   if( show_mload)
      printf("Loading macro definition \"%s\"\n", name);

   for(;;)
   {
      switch( tok = yylex())
      {
         case 0         :
            MESS("Case 0");
            nferror("Reached end of file before an .ENDM");

         case T_MACRO   :
            MESS("Case T_MACRO");
            nerror("U can't define a macro in a macro, ignoring until .ENDM");
            while( (tok = yylex()) && tok != T_ENDM);
            if( ! tok)
               nferror("...unfortunately that doesn't exist");
            break;

         case T_ENDM    :
            MESS("Case T_ENDM");
         {
            register buffer huge *p;
            register macro  huge *q;

            p = (buffer huge *) nmalloc( (long) MB_SIZE);
            q = (macro huge *) ((char huge *) p + sizeof( buffer));
            p->buflist   = p->p = (byte *) start;
            p->name      = name;
            p->remain    = p->oremain = no;
            p->type      = BUF_ALLOC | BUF_TOKEN | BUF_MACRO;
            p->multi.fill= (void (*)(buffer *)) dummy;   /* **-PORT #1-**/
            p->done      = mac_done;
            p->get       = (int (*)()) mac_get;          /* **-PORT #1-**/
            MESS("Filling q");
            q->name      = p->name = name;
            q->hash      = l_hash;
            q->buf       = p;
            q->inuse     = p->line = 0;
            MESS("Linking in...");
            einlinker( header, tailer, rover, used, q);
            start        = (int huge *) s;
            freshflag    = 1;           /* a new yylex caller will appear */
            LEAVE();
            return;
         }

         case T_INCLUDE :
            MESS("T_INCLUDE");
            if( ! (tok = yylex()))
               nferror("Unexpected EOF in the middle of a macro definition");
            if( tok != T_FILE)
               nerror("Valid file specifier expected after .INCLUDE");
                     include( (char *) yylval, ".s65");
            if( (tok = yylex()) != T_EOL)
            {
               nwarning("Garbage after .include <file> definition!");
               while( (tok = yylex()) != T_EOL);
            }
            break;

         default        :
            while( ! tok_remain--)
               s = more_space();
            if( (*s++ = tok) == T_EOL)
               bp->line++;
            *(lword huge *)s = yylval;
            s += sizeof( lword) / sizeof( int);
            no++;
      }
   }
}


#if DEBUG
#define fdreturn( val)  \
{                       \
   int   foo = (val);   \
                        \
   prtname( foo);       \
   LEAVE();             \
   return( foo);        \
}
#else
#define fdreturn     return
#endif

static int  mac_get()
{
   static int           tok;
   register buffer huge *P = bp;

   ENTER("mac_get");
   if( freshflag)                /* Have we set syn_panic already ??    */
   {
      freshflag = 0;             /* No clr flag                         */
#if DEBUG
      SAVESTATE( &syndebug);
#endif
      if( setjmp( syn_panic))    /* Do the setjmp ONCE                  */
      {
         int foo;
#if DEBUG
         SETBACK(syndebug);
#endif
         MESS("\007\t\tsyntax panic occurred");
         while( P->remain--)                 /* Something left ?        */
         {
            foo   = *(int huge *)P->p;
            P->p += sizeof( int) + sizeof( lword);
            if( foo)
               break;
         }
      }
   }
   if( P->remain--)                    /* Something left ?    */
   {
#if ! PHILOSOPHICAL_PROBLEM
      tok    = *((int huge *) P->p)++;
      yylval = *((lword huge *) P->p)++;
#else
      tok    = *(int huge *) P->p;
      P->p   = (void huge *) ((int *) P->p + 1);
      yylval = *(lword huge *) P->p;
      P->p   = (void huge *) ((lword huge *) P->p + 1);
#endif
      if( tok < HIGHEST || _in_if)     /* not a parameter ??   */
         fdreturn( tok);               /* then return that     */
      {
         register lexpr huge  *l;

         if( tok >= T_MLPARA)
         {
            register label huge  *q;

            MESS("Using the label indirection");
            if( ! (q = find_label( (char huge *) yylval)) || q->refs)
              lxsyntax("Foward references of macro indices. Tsk Tsk");
            yylval = (lword) q->val;
         }
         if( yylval > P->parms.list->expr->val)
            lxsyntax("Missing macro parameter error");
         l = lex_get( P->parms.list, (word) yylval);
         if( _call_macro)
         {
            yylval = (lword) l;
            return( T_PARA);
         }
         
         if( (tok & 1) == (T_MSPARA & 1))                /* OPTIMIZE */
         {
            yylval = (lword) l->string;
            IMESS("Supplying $%lx as string value", yylval, 4);
            fdreturn( T_STRING);
         }
         l = (lexpr huge *) l->expr;
         yylval = (lword) copyexpr( (expr *) l);
/*         ((expr *)yylval)->fix = 0;      */              /* That true ? */
         IMESS("Supplying $%lx for an expression", yylval, 4);
         fdreturn( T_EXPR);
      }
   }
#if DEBUG
   dump_buffer( bp);
#endif
   MESS("Killing buffer");
   kill_buffer( hp);                   /* Install new buffer      */
#if DEBUG
   dump_buffer( bp);
#endif
   fdreturn( T_EOL);
}


macro *find_macro( s)
register char  *s;
{
   register macro huge  *p;
   register lword       hash;
   register int         res;

   ENTER("find_macro");
   IMESS("Looking for \"%s\"", (lword) s, 4);
   l_hash = hash = calc_hash( s + 1);
   p = header[ used = is_where[*s]];
   while( p && (p->hash < hash ||
          (p->hash == hash && (res = strcmp( p->name, s)) < 0)))
      p = p->next;
   rover = p;

   if( ! found( p, hash) || res)  /* if not found or at end of table */
   {
      LEAVE();
      return( 0);
   }
   LEAVE();
   return( p);
}


void  do_macro( name, lex)
char        *name;
lexpr huge  *lex;
{
   register macro huge  *q;
   register buffer huge *p;
   int                  flag = __p - __program >= MAXMODULE;

   ENTER("do_macro");
   IMESS("Executing macro \"%s\"", (lword) name, 4);
   if( q = find_macro( name))
   {
      lex->string = make_string( name);      /* ARGH! make that better */
      if( ! q->buf->oremain)
      {
         LEAVE();
         return;
      }
      if( q->inuse)
      {
         MESS("q seems to be in use");
                if( q->inuse == 1)
                 nwarning("Macro recursion (allowed but maybe unwanted)");
         if( flag)
            nferror("Probably a macro recursion caused the code buffer\
 overflow");
         p          = (buffer huge *) nmalloc( (long) sizeof( buffer));
         bcopy((char *) q->buf, (char *) p, (long) sizeof( buffer));
         p->buflist = p->p = q->buf->buflist;
         p->remain  = p->oremain;
         p->name    = q->name;
         p->type    = BUF_MACRO | BUF_TOKEN | BUF_FREE | BUF_ALLOC;
      }
      else
         p = q->buf;

      if( flag)
         nferror( ovf_loss);

      q->inuse++;
      IMESS("p->remain = %ld", p->remain, 4);
      if( (p->before = bp))
      {
         bp->next = p;
/*         if( ! (bp->type & BUF_TOKEN))
            bp->_aux2  = c;
*/
      }
      p->multi.dad  = q;
      p->parms.list = lex;
      p->line       = 0;
      p->next       = 0;
      p->get        = (int (*)()) (gettoken = mac_get); /* **-PORT #1-**/
      p->done       = mac_done;
      bp            = p;
      MESS("MACRO has been linked into buffer list");
#if DEBUG
      dump_buffer( bp);
#endif
      freshflag     = 1;
      _in_macro++;

      if( show_expansion)
      {
         int    i = 1;
         
         tabout();
         printf( "line %4.4d, expanding macro: \"%s\"\n", 
                p->before->line, name);
         tab();
         printf( "\t#parameters : %d\n", lex->expr->val);
         
         while( lex = lex->next)
         {
            tab();
            printf("\t%%%d : %c $%04X   %%$%d : \"%s\"\n", 
                   i,
                   valued( lex->expr) ? ' ' : 'F',
                   lex->expr->val,
                   i,
                   lex->string + 2);
            i++;
         }
      }
   }
   else
      nserror( "Macro is undefined", name);
   LEAVE();
}


void  call_macro( s)
char     *s;
{
   nwarning("unimplemented directive");
/* 
   if( bp->type & BUF_MACRO)
      do_macro( s, slex_ch( lex_pl( NIL, bp->parms.list->expr), 
                           bp->parms.list->next));
   else
      nerror("You can use .CALL only in macro definitions");
*/
}


void  rept_macro( ex, name, lex)
expr huge   *ex;
char        *name;
lexpr huge  *lex;
{
   if( unvalued( ex))
      nerror("No foward references for repeat factor allowed");
   else
   {
      word  i;

      for( i = ex->val; i; i--)
         do_macro( name, lex);
   }
}

static void   mac_done( p)
register buffer huge *p;
{
   ENTER("mac_done");
   p->line   = 0;
   p->p      = p->buflist;
   p->remain = p->oremain;
   p->multi.dad->inuse--;                  /* incomplete */
   _in_macro--;                            /* (later: ;) */
   if( show_expansion)
   {
        tabin();
        printf( "Done with macro: \"%s\"\n", p->name);
   }
   LEAVE();
}


static void    lxsyntax( err)
char  *err;
{
   nerror( err);
   longjmp( syn_panic, 1);
}

/* ; Huh? What? Why?? Tell me more... I don't see the problem */