/* ---------------------------------------------------------------------- */
/*                   Copyright (C) 1991 by Natrlich!                     */
/*                      This file is copyrighted!                         */
/*                Refer to the documentation for details.                 */
/* ---------------------------------------------------------------------- */
#define __BIG_GENERATOR__
#include "defines.h"
#include "nasm.h"
#include OSBIND
#include "labels.h"
#include "seg.h"
#include NMALLOC_H
#include "object.h"
#include "fix.h"
#include "process.h"
#include "code.h"
#include "op.h"

/*#if DEBUG*/
#include <stdio.h>
#include "debug.h"
/*#endif*/

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

extern byte       scrtab[];
extern int        runnable, better, show_open;
extern seg huge   *h_seg, huge *sp;
extern imm huge   *cip;
byte huge         *__program, huge *__p, huge *ptohead;
byte              __c;
word              __pc, __x;
lword             __lx;


static char
   invadr[]    = "Instruction can't use that addressing mode",
   data_loss[] = "Something's amiss after instruction",
   modi_loss[] = "No forward references in the modifier",
   org_loss[]  = "Can't set  *  with a forward reference",
   ds_loss[]   = "No fwd reference with a reserve-memory directive";


void pro_init()
{
   ENTER("pro_init");
   __p  = __program = nmalloc( MAXMODULE + 6 + MAX_SAFETY);
   __pc = DEFORG;             /* default org */
   if( runnable)
   {
      dpoke( __p, 0xFFFF);
      _advance( 2);
      dpoke( __p, __pc);
      _advance( 4);
   }
   build_rseg( S_UNKNOWN, 0);
   LEAVE();
}

void pro_exit()
{
   ENTER("pro_exit");
   if( h_seg == sp && sis_unknown( sp))
      nerror("Not a single byte was generated");
   if( runnable)
      if( sis_unknown( sp))
         _retreat( 4);
      else
      {
         register byte huge *p = &__program[ sp->index - 2];

         dpoke( p, __pc - 1);
      }
   else
      if( sis_code( sp))
         sp->size = calc_index();
   LEAVE();
}

#if ! VERSION
void do_patch( p)
register expr huge   *p;
{
   if( ! runnable)
      if( e_pcrel( p))
         if( unvalued( p))
         {
            save_patch( calc_index(), p->op, 0);
         }
         else
            save_patch( calc_index(), p->op & O_BITS, p->aux);
}
#else
# define do_patch( p)                                             \
   if( ! runnable)                                                \
      if( e_pcrel( p))                                            \
         if( unvalued( p))                                        \
         {                                                        \
            save_patch( calc_index(), p->op, 0);                  \
         }                                                        \
         else                                                     \
            save_patch( calc_index(), p->op & O_BITS, p->aux)
#endif


static void wgen_error()
{
   nerror( data_loss);
   wdeposit( 0);
}

static void gen_error()
{
   nerror( data_loss);
   deposit( 0);
}


void  generate( tab, mode, ex)
register byte huge   *tab;
register int         mode;
register expr huge   *ex;
{
   register int   flag = 0;

   ENTER("generate");
   IMESS("ex     = $%lX", (lword) ex, 4);
   check_fseg( sp, S_RCODE);
   if( mode == C_ABS && tab[ mode] == 255)
      mode = C_RELA;
   if( ex)
      if( ((flag = valued( ex)) && ex->val < 0x100) || ex->op & O_ZEROP)
         if( mode >= C_ABS && mode <= C_RELY)
            mode -= (C_ABS - C_ABS0);

   switch( mode)
   {
      case C_IMPL  :
      case C_ACCU  :
         if( tab[ mode] == 255)
         {
            deposit( 0);      /* is a BRK */
         }
         else
         {
            cmddeposit( tab, mode);
         }
         break;

      case C_IMM   :
         cmddeposit( tab, C_IMM);
         ex_chk( ex);
         do_patch( ex);
         ex_deposit( ex, cip, flag);
         break;

      case C_RELA  :
         cmddeposit( tab, mode);
         if( flag)
         {
            if( (mode = ex->val - __pc - 1) < -127)
               nerror("Backward branch out of range");
            deposit( mode);
            expr_tryfree( ex);
            break;
         }
         ex_chk( ex);
         fix_up( ex, (imm huge *) 0, FIX_BRANCH);
         deposit( 0);
         ex->aux = __pc;
         break;

      case C_INDX  :
         if( tab[ mode] == 124)
         {
            deposit( 124);
            wex_chk( ex);
            wex_deposit( ex, flag);
            break;
         }

      case C_INDY  :
         cmddeposit( tab, mode);
         ex_chk( ex);
         ex_deposit( ex, 0, flag);
         if( ex->val >= 0x100)
            nerror("(adr),y or (adr,x) adr was not ZEROPAGE.");
                                    /* might have been a warning as well */
         break;

      case C_RELX0 :
      case C_RELY0 :
      case C_ABS0  :
         if( __c = tab[mode])
         {
            deposit( __c);
            ex_chk( ex);
            ex_deposit( ex, 0, flag);
            break;
         }
         mode += (C_ABS - C_ABS0);     /* and fall thru */

      case C_RELX  :
      case C_RELY  :
      case C_ABS   :
there:
         cmddeposit( tab, mode);
         wex_chk( ex);
         wex_deposit( ex, flag);
         break;

      case C_IND   :
         if( tab[ mode] == 108)        /* is it a jump */
            goto there;
         cmddeposit( tab, mode);
         ex_chk( ex);
         ex_deposit( ex, 0, flag);
         if( ex->val >= 0x100)
            nerror("(adr) - 65C02 BTW - was not ZEROPAGE.");

   }
   LEAVE();
   return;
}


void  dropbytes( ex, l, cflag)
expr huge            *ex;
register lexpr huge  *l;
int              cflag;
{
   register word       i,
                       offset = 0;
   register byte huge  *p;

   ENTER("dropbytes");
   if( ex)
      if( valued( ex))
         offset = ex->val;
      else
      {
         nerror( modi_loss);
         LEAVE();
         return;
      }

   check_fseg( sp, S_SDATA);
   while( l)
   {
      if( p = (byte huge *) l->string)
      {
         i = pdbeek( p);
         IMESS("dropping string of size %d", (lword) i, 2);
         if( i)
         {
            while( i-- > 1)
            {
               deposit( *p++ + offset);
            }
            if( cflag)
            {
               deposit( (*p++ ^ 0x80) + offset);
            }
            else
            {
               deposit( *p++ + offset);
            }
         }
         else
            nwarning("empty string");
      }
      else
      {
         register expr huge *p;

         p = l->expr;
         do_patch( p);
         if( valued( p))
         {
            deposit( p->val + offset);
            expr_tryfree( p);
         }
         else
         {
            fix_up( p, cip, FIX_ZCODE);
            deposit( offset);
         }
      }
      l = l->next;
   }
   LEAVE();
}

void  dropsbytes( ex, l)
expr huge            *ex;
register lexpr huge  *l;
{
   register word        i,
                        offset = 0;
   register byte huge   *p;

   ENTER("dropsbytes");
   if( ex)
      if( valued( ex))
         offset = ex->val;
      else
      {
         nerror( modi_loss);
         LEAVE();
         return;
      }

   check_fseg( sp, S_SDATA);
   while( l)
   {
      if( p = (byte *) l->string)
      {
         i = pdbeek( p);
         IMESS("dropping string of size %d", (lword) i, 2);
         if( i)
            do
            {
               deposit( scrtab[*p++] + offset);
            }
            while( --i);
         else
            nwarning("empty string");
      }
      else
      {
         register expr huge *p;

         p = l->expr;
         MESS("was expr");
         do_patch( p);
         if( valued( p))      /* get rid of switch */
         {
            deposit( scrtab[ p->val] + offset);
            expr_tryfree( p);
         }
         else
         {
            check_fseg( sp, e_pcrel( p) ? S_RDATA : S_SDATA);
            fix_up( p, cip, FIX_SZCODE);
            deposit( offset);
         }
      }
      l = l->next;
   }
   LEAVE();
}


void  dropwords( l)
register lexpr huge  *l;
{
   register expr huge   *p;

   ENTER("dropwords");
   while( l)
   {
      p = l->expr;
      MESS("cheking expr");
      if( valued( p))      /* get rid of switch */
      {
         if( e_pcrel( p))
         {
            check_fseg( sp, S_RDATA);
         }
         else
         {
            check_fseg( sp, S_SDATA);
         }
         wdeposit( p->val);
         expr_tryfree( p);
      }
      else
      {
         check_fseg( sp, e_pcrel( p) ? S_RDATA : S_SDATA);
         fix_up( p, (imm *) 0, FIX_WCODE);
         IMESS("FIX_WCODE @$%8.8lX", (lword) __p, 4);
         wdeposit( 0);
      }
      l = l->next;
   }
   LEAVE();
}


void  dropdbytes( l)
register lexpr huge  *l;
{
   register expr huge   *p;

   ENTER("dropdbytes");
   IMESS("Got lexpr @$%8.8X", (lword) l, 4);
   while( l)
   {
      p = l->expr;
      IMESS("Got expr @$%8.8X", (lword) p, 4);
      if( valued( p))              /* get rid of switch */
      {
         MESS("wddepositing value");
         if( e_pcrel( p))
         {
            check_fseg( sp, S_TDATA);
         }
         else
         {
            check_fseg( sp, S_SDATA);
         }
         wddeposit( p->val);
         expr_tryfree( p);
      }
      else
      {
         MESS("needs fixup");
         check_fseg( sp, e_pcrel( p) ? S_TDATA : S_SDATA);
         fix_up( p, (imm *) 0, FIX_DCODE);
         IMESS("FIX_DCODE @$%8.8lX", (lword) __p, 4);
         wdeposit( 0);
      }
      l = l->next;
   }
   LEAVE();
}

static warnflag;

void  dropfloat( s)
char  *s;
{
   extern char    page0[];
   int   i;

   ENTER("dropfloat");
   if( ! warnflag++)
      nwarning("Floats are for losers");
   check_fseg( sp, S_SDATA);
   if( ab_ascin( s))
      nserror("Floating point value malformed", s);
   else
      for( i = 0; i != 6; i++)
         deposit( page0[ 0xD4 + i]);
   LEAVE();
}


void real_setorg( newpc)
register word  newpc;
{
   ENTER("real_setorg");
   if( sis_unknown( sp))           /* if old *= didn't do anything */
   {
      register byte huge *p = &__program[ sp->index - 4];

      __pc = newpc;  /* reset START */
      dpoke( p, __pc);
   }
   else
      if( better)
      {
         register word  diff;

         if( (diff = newpc - __pc) < DS_THRESHOLD)
         {
            check_fseg( sp, S_SDATA);
            advance( diff);
            LEAVE();
            return;
         }
      }
      else
      {
         register byte huge *p = &__program[ sp->index - 2];

         dpoke( p, __pc - 1);
         __pc = newpc;
         dpoke( __p, __pc);
         _advance(4);
         build_rseg( S_UNKNOWN, 0);
      }
   LEAVE();
}


void setorg( ex, offset)
register expr  *ex;
register word  offset;
{
   ENTER("setorg");
   if( runnable)
      if( valued( ex))                        /* get rid of switch */
      {
         real_setorg( ex->val + offset);
         expr_tryfree( ex);
      }
      else
         nerror( org_loss);
   else
      nerror("You can't use origins in relocatable code");
   LEAVE();
}


void reserve( ex)
register expr *ex;
{
   ENTER("reserve");
   if( valued( ex))
   {
      register word  val = ex->val;

      if( runnable)
      {
         real_setorg( val + __pc);
         expr_tryfree( ex);
      }
      else
      {
         check_dseg( sp, S_DS, val);
         __pc += val;
/*         advance( val); */
      }
   }
   else
      nerror( ds_loss);
   LEAVE();
}

static obj_h   header;
#if ! VERSION
extern char x1[], x2[], x3[], x4[], x5[], x6[];
#endif
extern word  alignment;

void write_results( fd)
{
   lword  bytes = calc_ind( __p);
   static char    err[] = "Write to output file failed (Disk full??)";

   ENTER("write_results");
   if( runnable)
   {
      if( Fwrite( fd, bytes, __program) != bytes)
         nferror( err);
   }
   else
   {
      register byte huge *p  = (byte *) &header;
      lword              max = MAXMODULE,
                         ebytes, ibytes, rbytes, sbytes, ybytes, fbytes;

      plbyte(  p, OBJMAGIC);     /* sort of stupid */
      fpdbyte( p, DVERSION);
      fpdbyte( p, ASMREVISION);
      fpdbyte( p, alignment);
      fpdbyte( p, bytes);
      clean_labels();
      if( (sbytes = seg_size()) > max)
         max = sbytes;
      plbyte( p, sbytes);
      if( (ibytes = imm_size()) > max)
         max = ibytes;
      plbyte( p, ibytes);
      if( (rbytes = rel_size()) > max)
         max = rbytes;
      plbyte( p, rbytes);
      if( (ebytes = exp_size()) > max)
         max = ebytes;
      plbyte( p, ebytes);
      if( (ybytes = sym_size()) > max)
         max = ybytes;
      plbyte( p, ybytes);
      if( (fbytes = fix_size()) > max)
         max = fbytes;
      plbyte( p, fbytes);

      if( Fwrite( fd, sizeof( obj_h), &header) != sizeof( obj_h)     ||
          Fwrite( fd, bytes, __program) != bytes)
         nferror( err);
      if( max != MAXMODULE)
      {
         nfree( (void *) __program);
         __program = nmalloc( max);
      }
      if(
#if ! VERSION
          Fwrite( fd, 10L, x1) != 10                                 ||
#endif
          ( seg_makbuf(), Fwrite( fd, sbytes, __program) != sbytes)  ||
#if ! VERSION
          Fwrite( fd, 10L, x2) != 10                                 ||
#endif
          ( ibytes &&
           (imm_makbuf(), Fwrite( fd, ibytes, __program) != ibytes)) ||
#if ! VERSION
          Fwrite( fd, 10L, x3) != 10                                 ||
#endif
          ( rbytes &&
           (rel_makbuf(), Fwrite( fd, rbytes, __program) != rbytes)) ||
#if ! VERSION
          Fwrite( fd, 10L, x4) != 10                                 ||
#endif
          ( ebytes &&
           (exp_makbuf(), Fwrite( fd, ebytes, __program) != ebytes)) ||
#if ! VERSION
          Fwrite( fd, 10L, x5) != 10                                 ||
#endif
          ( ybytes &&
           (sym_makbuf(), Fwrite( fd, ybytes, __program) != ybytes)) ||
#if ! VERSION
          Fwrite( fd, 10L, x6) != 10                                 ||
#endif
          ( fbytes &&
           (fix_makbuf(), Fwrite( fd, fbytes, __program) != fbytes)))
         nferror( err);
   }
   Fclose( fd);
   if( show_open)
      dump_open();
   LEAVE();
}
