/* literals.c -- STAR Literal Pool

This file is part of STAR, the Saturn Macro Assembler.

   STAR is not distributed by the Free Software Foundation. Do not ask
them for a copy or how to obtain new releases. Instead, send e-mail to
the address below. STAR is merely covered by the GNU General Public
License.

Please send your comments, ideas, and bug reports to
Jan Brittenson <bson@ai.mit.edu>

*/


/* Copyright (C) 1990, 1991 Jan Brittenson.

   STAR is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 1, or (at your option) any
later version.

   STAR is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.

   You should have received a copy of the GNU General Public License
along with STAR; see the file COPYING. If not, to obtain a copy, write
to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
USA, or send e-mail to bson@ai.mit.edu. */


#include <stdio.h>
#include "sects.h"
#include "star.h"
#include "literals.h"


static struct litbuf
  *lit_head,			/* Start of literal pool buffer chain */
  *lit_tail;			/* End of literal pool buffer chain */

static
  lit_shared;			/* Nonzero if literals are ot be shared */


/* Externally available statistics */
long
  lit_offs,			/* Next available pool offset */
  lit_begin,			/* Literal pool start address */
  lit_end;			/* Literal pool end address */


/* Initialize - called once */
void literal_init(shareflag)
  int shareflag;
{
  lit_head = lit_tail = NULL;
  lit_offs = 0;
  lit_begin = lit_end = 0;
  lit_shared = shareflag;
}


/* Literal block creator */
static struct litbuf *new_litbuf(nbytes)
  int nbytes;
{
  register struct litbuf *littmp;
  extern char *malloc();

  if(!(littmp = (struct litbuf *) malloc(sizeof *littmp +
					  sizeof(littmp->data) *
					  (nbytes - 1))))

    fatal("Can't alloc literal pool buffer");

  
  return(littmp);
}


/* Add literal block.
 * Returns address of literal pool entry.
 */
long add_literal(buf, nchars)
  char *buf;
  int nchars;
{
  register struct litbuf *littmp, *lp;
  long tmpoffs = lit_offs, coffs;
  extern pass;


  if(nchars)
    {
      if(lit_shared)
	
	/* Loop and see if we alread have a literal like this */
	for(coffs = lit_begin, lp = lit_head; lp; lp = lp->next)
	  {
	    if(nchars <= lp->nnib && !bcmp(buf, lp->data, nchars))
	      
	      /* Yep - return this */
	      return((pass == 1 ? 0 : coffs));
	    
	    coffs += lp->nnib;
	  }

      /* Initialize and link up */
      littmp = new_litbuf(nchars);
      bcopy(buf, littmp->data, nchars);
      
      littmp->nnib = nchars;
      littmp->next = NULL;
      
      if(!lit_head)
	lit_head = littmp;
      else
	lit_tail->next = littmp;
      
      lit_tail = littmp;
      
      lit_offs += nchars;
    }
  else
    sgnwrn("Null literal");

  if(pass == 1)
    return(0);
  else
    return(lit_begin + tmpoffs);
}


/* Write literal buffer chain to file */
void write_literal_pool(fp)
  FILE *fp;
{
  struct litbuf *lp;
  extern half_byte, half_value;
  register half_flag = half_byte, nib = half_value, ndata;
  char iobuf[ 1024 ], *ioptr;
  
  lit_end = lit_begin + lit_offs;


  /* Loop and add to file */
  if(!half_flag)
    nib = 0;

  ioptr = iobuf;

  for(lp = lit_head; lp; lp = lp->next)
    for(ndata = 0; ndata < lp->nnib; ndata++)

      if(half_flag)
	{
	  if(ioptr == iobuf + sizeof iobuf)
	    {
	      fwrite(iobuf, sizeof iobuf, 1, fp);
	      ioptr = iobuf;
	    }
	  
	  *ioptr++ = nib | (lp->data[ ndata ] << 4);
	  half_flag = FALSE;
	}
      else
	{
	  nib = lp->data[ ndata ];
	  half_flag = TRUE;
	}

  /* Flush IO buffer */
  if(ioptr != iobuf)
    fwrite(iobuf, ioptr-iobuf, 1, fp);

  /* Stow away any remaining loose nibble */
  half_byte = half_flag;
  half_value = nib;
}


/* Destroy literal chain */
void free_literals()
{
  register struct litbuf *lp, *lpx;

  for(lp = lit_head; lp; )
    {
      lpx = lp;
      lp = lp->next;
      free(lpx);
    }

  lit_head = lit_tail = NULL;
}


/* End of pass - update statistics, and reset literal pool */
void literal_end_of_pass()
{
  extern long loc;

  lit_begin = loc;
  lit_end   = lit_begin + lit_offs;
  lit_offs  = 0;

  free_literals();
}
