/* parse.c
 * Takes an RDF formatted file as input.  Parses to multiple output streams:
 * - Verilog test code to test all registers in simulation testbench
 * - C code for in-house debug tool.. set/clear/pre-load/test of every register/bit
 * - Interleaf (think MS Word) formatted table for chip documentation showing
 *   all bit/register descriptions, default values, etc.
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "parse.h"

char version[] = "2.00";

/* external refernces, mostly output routines */
extern void i_stub_copy(FILE *fo);
extern void i_cell_out(FILE *fo, char* data, char straddle_val, long rulings);
extern void i_stub_copy();

extern void print_out_text_data_from_structure(FILE *fo_text, struct reg_struct *reg_ptr, 
					       int, struct block_struct *);
extern void new_hit_pass1(struct block_struct *);

extern void ileaf_pass1( struct block_struct *block_ptr, uchr mode, FILE *fo_i);
extern void ileaf_pre2(FILE *fo1, char *arg);
void reference_search_and_replace(char *ref, uint block_offset);


static struct file_struct top_file;
static struct file_struct *current_file;

char filename[60];
char res_str[200];

struct asic_struct top;
int lif_flag;
char stubs_dir[] = "/projects/hit/tools/sparc/stubs/"; 
/* char stubs_dir[] = "/projects/vobs/hit/parser/stubs/"; */
int non_unique_warning;
int bitfield_warning;
int mask_warning;
int offset_warning;
int empty_bit_warning;
int rept_warning;
int block_warning;
int hini_warning;
int warning;
int errors;
int block_length_warning;
char sort_data = 1;
uchr text_out;
char label[20];
char block_copy_flag;

FILE *fo_hit_log;

#define ERR_NO_FIND    1
#define NO_NO_FIND_ERR 2

void print_results()
{
   fprintf(stderr,"\n\n%d Registers processed.\n", top.total_num_regs);
   fprintf(stderr,"There were %d error(s)\n", errors);
   fprintf(stderr,"There were %d offset warnings(s)\n", offset_warning);
   fprintf(stderr,"There were %d non-unique warnings(s)\n", non_unique_warning);
   fprintf(stderr,"There were %d bitfield warnings(s)\n", bitfield_warning);
   fprintf(stderr,"There were %d rept name warnings(s)\n", rept_warning);
   fprintf(stderr,"There were %d block name warnings(s)\n", block_warning);
   fprintf(stderr,"There were %d mask warnings(s)\n", mask_warning);
   fprintf(stderr,"There were %d empty bit description field warning(s)\n", empty_bit_warning);
   fprintf(stderr,"There were %d excessive-registers-in-block warnings(s)\n", block_length_warning);
   fprintf(stderr,"There were %d HIT warnings(s)\n", hini_warning);
   if(hini_warning != 0)
         fprintf(stderr,"Please see log file for list of registers\n", hini_warning);

   fprintf(stderr,"There were %d other warnings(s)\n", warning);
   if(empty_bit_warning != 0)
   {
      fprintf(stderr,"\nEmpty bit desc warnings are often a case of too many/few TABs\non a line.  Only use");
      fprintf(stderr," as many tabs as arguments you are skipping.\n");
   }

   fprintf(stderr,"\nThis was parse_rdf version: %s\n",version);
   fprintf(stderr,"Version history (release notes) are available in /projects/hit/tools/sparc/parse_release_notes .\n\n");

}
/*****************************************************************************
** generic error/exit routine.  print error message to stderr, then exit
*****************************************************************************/
void error(FILE *error_stream, char *err)
{
   errors++;
   fprintf(error_stream,"\r\nError: %s\n",err);
   print_results();
   fflush(stderr); 
   exit(1);
}
/*****************************************************************************
** This routine copies a string from src to dst, copying at most LIMIT
** characters(originally so nicknames can be limited to a certain number
** of characters for Interleaf printing purposes, taking in name_for_msg as 
** a possible error string
** removing any of several undesirable characters during the
** copy (replacing them with _) and possibly adjusting the case to LOWER
*****************************************************************************/
void parse_copy(char* dst, char* src, int limit, char* name_for_msg, int kill_BADCHARS,
		int copy_adjust)
{
   int len, end;

   /* kill trailing spaces working bkwds from end of string*/
   end = strlen(src)-1;
   while(src[end] == ' ')
   {
      src[end] = '\0';
      end = strlen(src)-1;
   }

   len = 0;
   /* while not end of line, and lmimt not reahed, copy */
   while((len < limit) && (src[len] != '\0') && (src[len] != (char)NULL))
   {
      /* possibly remove charcters that are illegal in C variable names */
      /* since the nicknames are used in C as variable names */
      if((kill_BADCHARS == KILL_BADCHARS) && ((src[len] == ' ') ||(src[len] == '/')||
	    (src[len] == '+')||(src[len] == '*')||(src[len] == ',') ||
	    (src[len] == '#')||(src[len] == '-') ))
	 dst[len] = '_';
      /* can adjust the case of the character, so far only lowercase is */
      /* needed as a special case, but uppercase could be added easily */
      else switch(copy_adjust)
      {
	 case COPY: dst[len] = src[len];
	    break;
	 case LOWER: if(dst[len] != '#')
	    dst[len] = tolower(src[len]);
	 else dst[len] = src[len];

	    break;
	 case UPPER: dst[len] = toupper(src[len]);
	    break;
	 default: error(stderr,"Unknown state in switch in _parse_copy.\n");
	    break;
      }
      len++;
   }
   /* if the limit is exceeded flag a warning so the user can adjust if they want to */
   if(strlen(src) >= limit)
   {
      fprintf(stderr,"+++++++++----->>>>>Warning: %s length truncated\n", name_for_msg);
      warning++;
      dst[len-1] = '\0';
   }
   else
      dst[len] = '\0';
}
/****************************************************************************
** This routine checks to make sure that a nickname is unique
** if it is found not be unique, it will add an _ and recheck to see
** if it is now unique.
*****************************************************************************/
void check_unique(struct reg_struct *check_ptr, char name_or_nick)
{
   struct block_struct *block_ptr;
   struct reg_struct *reg_ptr;
   char errstr[80];

   block_ptr = top.block_head;

   while(block_ptr != NULL)  /* walk each block */
   {
      reg_ptr = block_ptr->head_reg;
      while(reg_ptr != NULL)  /* walk each reg in the block */
      {
	 if(name_or_nick == NICK)
	 {
	    if((strcmp(check_ptr->reg_nick_name, reg_ptr->reg_nick_name) == 0) &&
	       (reg_ptr != check_ptr))
	    {
	       strcat(check_ptr->reg_nick_name,"_");  /* tack on a _ to try to make the name unique */
	       fprintf(stdout,"+++++++++----->>>>>Warning: Non-unique nick name found: %s\n",check_ptr->reg_nick_name);
	       non_unique_warning++;
	       check_unique(reg_ptr,NICK);
	    }
	 }
	 else
	 {
	    if((strcmp(check_ptr->reg_name, reg_ptr->reg_name) == 0) &&
	       (reg_ptr != check_ptr))
	    {
	       sprintf(errstr,"+++++++++----->>>>>Error: Non-unique name found: %s.\n", check_ptr->reg_name);
	       error(stderr,errstr);
	    }
	 }
	 reg_ptr = reg_ptr->next_reg;
      }
      block_ptr = block_ptr->next_block;
   }
}

/*****************************************************************************/
void hex_replace(char *line)
{
   char *num_start_ptr, *num_end_ptr, *before_ptr, *after_ptr;
   char new_line[DESC_LENGTH];
   int num;
   char num_str[200];

   /* find beginning of hex number */
   num_start_ptr = strstr(line,"0x");
   if(num_start_ptr == NULL)
      return;

   /* find end of number */  
   num_end_ptr = num_start_ptr  + 2;
   while( ((*num_end_ptr >= '0') && (*num_end_ptr <= '9')) ||
	  ((*num_end_ptr >= 'a') && (*num_end_ptr <= 'f')) ||
	  ((*num_end_ptr >= 'A') && (*num_end_ptr <= 'F')))
      num_end_ptr++;

   /* backup to last digit */
   num_end_ptr--;

   /* now save pieces... before_piece, num_piece, after_piece */
   before_ptr = line;
   after_ptr = num_end_ptr + 1;

   /* extract number into string */
   strcpy(num_str, num_start_ptr);
   num_str[num_end_ptr - num_start_ptr + 1] = '\0';

   /* convert number */
   num = strtol(num_str,0,16);
   
   /* terminate before_string */

   *num_start_ptr = '\0';

   sprintf(new_line,"%s%d%s",before_ptr,num,after_ptr);

   strcpy(line, new_line);

   hex_replace(line);
}

/*****************************************************************************/
void instance_replace(char *line, uint instance_value, char preserve_num_sign, char *match, char format)
{
   char *start_token;
   char *end_token;
   char new_line[DESC_LENGTH];

   start_token = strstr(line,match);
   if(start_token == NULL)
      return;
   *start_token = '\0';  /* terminate the old string at first token for copying */
   end_token = start_token + 3;

   /* insert correct instance # into the string */
   if(preserve_num_sign)
      if(format == HEX)
	 sprintf(new_line,"%s#0x%x%s",line,instance_value,end_token);
      else if(format == ALPHA)
	 sprintf(new_line,"%s%c%s",line,instance_value+0x41,end_token);
      else
	 sprintf(new_line,"%s#%d%s",line,instance_value,end_token);
   else
      if(format == HEX)
	 sprintf(new_line,"%s%x%s",line,instance_value,end_token);
      else if(format == ALPHA)
	 sprintf(new_line,"%s%c%s",line,instance_value+0x41,end_token);     
      else
	 sprintf(new_line,"%s%d%s",line,instance_value,end_token);

   strcpy(line, new_line);

   instance_replace(line, instance_value, preserve_num_sign, match, format);
}
/*****************************************************************************/
char * eval_ini(struct reg_struct *reg_ptr, char *hini, int block_val)
{
   char ini_str[200];
   char sys_str[200];
   FILE *fi_result;
   char err_msg[200];

   res_str[0] = '\0';
   /* now eval the hini expressions */
   strcpy(ini_str, hini);
   instance_replace(ini_str, block_val, 0, "#b#", DECIMAL);
   instance_replace(ini_str, reg_ptr->rept_value, 0, "#r#", DECIMAL);
   hex_replace(ini_str);

   if( strstr(ini_str,"+") ||
       strstr(ini_str,"%") ||
       strstr(ini_str,"-") ||
       strstr(ini_str,"*") ||
       strstr(ini_str,"/"))
   {

      /* now eval any mathematical expressions */
      sprintf(sys_str,"/bin/rm -f rdf.tmp.bc rdf.tmp.bc.result\n",ini_str);
      system(sys_str);
      sprintf(sys_str,"echo \"%s\nquit\n\" > rdf.tmp.bc\n",ini_str);
      system(sys_str);
      sprintf(sys_str,"/usr/bin/bc rdf.tmp.bc > rdf.tmp.bc.result\n");
      system(sys_str);
      
      if((fi_result = fopen("rdf.tmp.bc.result","r")) == NULL)
      {   perror("rdf.tmp.bc");     exit(-1);  };
      
      fgets(res_str, MAX_INP, fi_result);
      fclose(fi_result);
/*    fprintf(stderr,"Result str = %s : bit 0 = %d\n",res_str,(int)res_str[0]); */
      if(res_str[0] == 0)
      {
	 sprintf(err_msg,"\nBad :*ini* line: %s, does not work with /usr/bin/bc\n",hini);
	 error(stderr,err_msg);
      }
   }
   else
      strcpy(res_str, ini_str);
   return(res_str);
}
/*****************************************************************************

first replace any ## in reg name or abbrev
then check unique on the register
*****************************************************************************/
void replace_instance_placeholders_and_ini()
{
   struct block_struct *block_ptr;
   struct reg_struct *reg_ptr;

   block_ptr = top.block_head;
   while(block_ptr != NULL)  /* walk each block */
   {
      reg_ptr = block_ptr->head_reg;
      while(reg_ptr != NULL)  /* walk each reg in the block */
      {
	 /* Look for #B# in reg name */
	 instance_replace(reg_ptr->reg_name, block_ptr->default_instance_value, 1, "#b#", reg_ptr->format);
	 /* Look for #B# in abbrev */
	 instance_replace(reg_ptr->reg_nick_name, block_ptr->default_instance_value, 0, "#b#", reg_ptr->format);
	 if( strstr(reg_ptr->reg_name, "#r#") || strstr(reg_ptr->reg_nick_name, "#r#"))
	    if(reg_ptr->rept_value == -2)
	    error(stderr,"Bad repeat value.\n");
	 /* Look for #R# in reg name */
	 instance_replace(reg_ptr->reg_name, reg_ptr->rept_value, 1, "#r#", reg_ptr->format);
	 /* Look for #R# in abbrev */
	 instance_replace(reg_ptr->reg_nick_name, reg_ptr->rept_value, 0, "#r#", reg_ptr->format);

	 parse_copy(reg_ptr->reg_nick_nameU, reg_ptr->reg_nick_name, REG_ABBV_LENGTH,"Reg Abbrv",
		    KILL_BADCHARS,UPPER); 
	 check_unique(reg_ptr,NAME);
	 check_unique(reg_ptr,NICK);
	 parse_copy(reg_ptr->reg_nick_name, reg_ptr->reg_nick_name,20,"", KILL_BADCHARS,LOWER);  
	 reg_ptr = reg_ptr->next_reg;
      }
      block_ptr = block_ptr->next_block;
   }
}


/*****************************************************************************
** This routine parses a keyword line.  It takes in the the
** keyword (so it can be parsed off) and then parses by spaces and/or
** tabs based upon the tab_only field.  It sets up an array of
** pointers (parsed_data_ptrs) in which each entry points to a
** delimted field in the parsed string.
*****************************************************************************/
void parse_keyword_line(char *line, char *parsed_data_ptrs[], char *keyword, char tab_only)
{

   int field, i;
   char *head, *tail, *weed_CR;

   field = 0;

   /* NULL out the aray of pointers */
   for(i=0; i<MAXFIELDS; i++)
      parsed_data_ptrs[i] = NULL;

   head = tail = line;
   
   if(keyword != NULL)
   {
      /* skip ahead of the keyword */
      head = strstr(head,":");
      head++;
      head = strstr(head,":");
      head++;
      tail = head;
   }
   do
   {      
      head = tail;
      /* skip all leading white space on keyword lines */
      if(keyword != NULL)
	 while((*head == '\t') || (*head == ' '))
	    head++;
      tail = head;
      /* if the parsing requires tabs on the line */
      if(tab_only)
	 while( (*tail != '\t') && (*tail != '\0') )
	    tail++;
      else
	 /* or if spaces are allowed as delimters */
	 /* this can't be the case on description lines since there */
	 /* may be spaces in the description strings */
	 while((*tail != '\t') && (*tail != ' ') && (*tail != '\0'))
	    tail++;
      if(head[0] == '\n')
	 break;
      parsed_data_ptrs[field] = head;
      if(parsed_data_ptrs[field][0] == '\t')
	 parsed_data_ptrs[field] = NULL;
      else
      {
	 weed_CR = strstr(head,"\n"); /* remove embedded linefeeds */
	 if(weed_CR != NULL)
	    *weed_CR = '\0';
	 weed_CR = strstr(head,"//"); /* terminate at a comment delimiter */
	 if(weed_CR != NULL)
	    *weed_CR = '\0';
	 weed_CR = strstr(head,"\r"); /* remove linefeeds */
	 if(weed_CR != NULL)
	    *weed_CR = '\0';
      }
      if((tail != NULL) && (*tail != '\0'))
      {
	 *tail = '\0';
	 tail++;
      }
      /* if we have reached a comment, end of line */
      if(parsed_data_ptrs[field] != NULL)
	 if((parsed_data_ptrs[field][0] == '/') && (parsed_data_ptrs[field][1] == '/'))
	 {
	    parsed_data_ptrs[field] = NULL;
	    *tail = '\0';
	 } 
      field++;
      parsed_data_ptrs[field] = NULL;  /* init to end */
   }while(*tail != (char)NULL);
}


/*****************************************************************************
** auto generate nicknames from descriptions
*****************************************************************************/
void autogen_nickname(char *src, char *nickname)
{
   char src_ptr;
   char dst_ptr;

   src_ptr = dst_ptr = 0;

   /* skip BADCHARS */
   while((src[src_ptr] == ' ') || (src[src_ptr] == '"'))
      src_ptr++;

   /* get first char */
   nickname[dst_ptr++] = toupper(src[src_ptr++]);

   while((src[src_ptr] != '\0') && (dst_ptr < NICK_NAME_LIM))
   {
      /* keep any numbers */
      if( (src[src_ptr] >= '0') && (src[src_ptr] <= '9') )
         nickname[dst_ptr++] = (src[src_ptr]);
      else
	 /* keep all letters after whitespace ... first leter of a word */
	 if( (src[src_ptr-1] == ' ') || (src[src_ptr-1] == '\t'))
	 {
	    if( (src[src_ptr] != '\0') && (toupper(src[src_ptr]) >= 'A') &&
		(toupper(src[src_ptr]) <= 'Z') && (toupper(src[src_ptr]) >= '*'))
	       nickname[dst_ptr++] = toupper(src[src_ptr]);
	 }
      src_ptr++;
   }
   nickname[dst_ptr] = '\0';
}

/*****************************************************************************
** This parses the access type (read, write, interrupt, clear-on-read)
** of the register
*****************************************************************************/
void parse_access_type(struct reg_struct *asic_reg, char *pdata[] )
{
   if(strstr(pdata[0],"cor") != NULL)
      error(stderr,"Please use 'c' not 'cor' in the RWCI field.");
   asic_reg->reg_read_write = 0;
   if(strstr(pdata[0],"R") || strstr(pdata[0],"r") )
      asic_reg->reg_read_write |= READ_ACCESS;
   if(strstr(pdata[0],"W")|| strstr(pdata[0],"w") )
      asic_reg->reg_read_write |= WRITE_ACCESS;
   if(strstr(pdata[0],"C")|| strstr(pdata[0],"c") )
      asic_reg->reg_read_write |= CLEAR_ON_READ_ACCESS;
   if(strstr(pdata[0],"I")|| strstr(pdata[0],"i") )
      asic_reg->reg_read_write |= INTERRUPT_ACCESS;
}


/*****************************************************************************
** find a block in the structure by matching its ABBREV name 
*****************************************************************************/
struct block_struct * find_block(char *block_name)
{
   struct block_struct *block_ptr;
   char block_err_msg[100];

   block_ptr = NULL;
   if(top.block_head == NULL)
      error(stderr,"No block table to search .... ");
   block_ptr = top.block_head;
   while(strcmp(block_name, block_ptr->block_abbrev) != 0)
   {
      block_ptr = block_ptr->next_block;
      if(block_ptr == NULL)
	 break;
   }
   if(block_ptr != NULL)
   {
      if(strcmp(block_name, block_ptr->block_abbrev) == 0)
	 return(block_ptr);
   }
   else 
   {
      sprintf(block_err_msg,"No block match made with supplied block nickname:%s , using block 0 ",block_name);
      error(stderr,block_err_msg);
      return(top.block_head);
   }
}
/*****************************************************************************
** find a block in the structure by matching its name and return the offset
*****************************************************************************/
int find_block_offset(char *block_name, char err_flag)
{
   struct block_struct *block_ptr;

   block_ptr = find_block(block_name);
   return(block_ptr->block_offset);
}
/*****************************************************************************
this routine searches a Bit Value Description for ::offset (block)::
and replaces it with the actual offset within the ASIC
This will piece together a new bvd or comment
-old bvd up to ::
-offset number computed
-:: old string after second ::

if BLOCK = SELF then use block ptr to know what block we are in
this is crucial to doing block copies.  Only one copy of the
lower level data need exist, since the references will only
be parsed out as they are printed, and they must be copied to
tmp strings before being parsed so the original is not modified
so the references are still there for the duplicate blocks to use.
*****************************************************************************/
void reference_search_and_replace(char *ref, uint block_offset)
{
   char *start_token;
   char *end_token;
   char new_ref[DESC_LENGTH];
   char *ref_data[MAXFIELDS];
   int addr;

   start_token = strstr(ref,"::");
   if(start_token == NULL)
      return;
   *start_token = '\0';  /* terminate the old string at first token for copying */
   start_token += 2;    /* advance token to start reading in offset data */
   end_token = strstr(start_token,"::");
   if(end_token == NULL)
      error(stderr,"Starting :: found, but no closing ::");
   *end_token = '\0';  /* terminate the old string at first token for copying */

   /* now compute the addr from the numeric data */
   parse_keyword_line(start_token, ref_data, NULL, 0);
   if(ref_data[0][1] != 'x') 
      error(stderr,"Error parsing assumed offset data in bit value description. No 0x on number.\nIf this is not an offset reference, please remove the ::, it is reserved for special use.");

   addr = strtol(ref_data[0],0,16); 
   if(ref_data[1] != NULL)
   {
      if(strncmp(ref_data[1],"SELF",4) == 0)   /* SELF block for duplicate */
	 addr = strtol(ref_data[0],0,16) + block_offset;
      else
	 addr = strtol(ref_data[0],0,16) + find_block_offset(ref_data[1], ERR_NO_FIND);
   }
   /* insert this offset into the string */
   sprintf(new_ref,"%s0x%04x",ref,addr);

   /* complete end of string */
   end_token += 2; /* advance end token past closing :: */
   strcat(new_ref, end_token);
   strcpy(ref, new_ref);
   reference_search_and_replace(ref, block_offset);
}

/*****************************************************************************
** 
*****************************************************************************/
void add_nib_desc(char * desc, struct nib_struct *nib_ptr)
{
   struct nib_desc_struct *desc_ptr;
   struct nib_desc_struct *old_desc_ptr;
   char ref[MAX_INP];

   desc_ptr = malloc(sizeof(struct nib_desc_struct)); /* need a new desc field */
   desc_ptr->next_desc = NULL;

   /* now search down nibble to end of existing descriptions and add there */
  
   old_desc_ptr = nib_ptr->head_desc;
   if(old_desc_ptr == NULL)
      nib_ptr->head_desc = desc_ptr;
   else
   {
      while(old_desc_ptr->next_desc != NULL)
	 old_desc_ptr = old_desc_ptr->next_desc;
      old_desc_ptr->next_desc = desc_ptr;  
   }
   if(desc != NULL)
   {
      strcpy(ref, desc);
      parse_copy(desc_ptr->bit_val_desc, ref, DESC_LENGTH, "Bit Value Description", 
		 NO_KILL_BADCHARS, COPY);
   }
   else
   {
      fprintf(stderr,"+++++++++----->>>>>Warning: Empty bit description\n");
      empty_bit_warning++;
      strcpy(desc_ptr->bit_val_desc, "_");
   }
}
/*****************************************************************************
** 
*****************************************************************************/
void add_nib_to_reg(struct nib_struct ** new_nib, struct reg_struct *reg, int start_bit)
{
   struct nib_struct * nib_ptr;
   int last_bit;
  
   /* alloc new nibble */
   nib_ptr = malloc(sizeof(struct nib_struct));
   nib_ptr->next_nib = NULL;
   nib_ptr->head_desc = NULL;
   /* need to tie on to last nibble */
   if(reg->head_nib == NULL)
   {
      reg->head_nib = nib_ptr;
      last_bit = top.reg_width;
      (*new_nib) = nib_ptr;  
   }
   else
   {
      last_bit = (*new_nib)->end_bit;
      (*new_nib)->next_nib = nib_ptr; /* get new nib */
      (*new_nib) = nib_ptr;  
   }
   nib_ptr->up_down = 0;
   if(last_bit - 1 > start_bit) /* the bits were skipped, insert UNUSED */
   {
      nib_ptr->start_bit = last_bit - 1;
      nib_ptr->end_bit = start_bit + 1;
      nib_ptr->start_char = nib_ptr->start_bit - nib_ptr->end_bit;
      add_nib_desc( UNUSED_token, nib_ptr);
      strcpy(nib_ptr->nick_name,"");
      nib_ptr->next_nib = *new_nib = malloc(sizeof(struct nib_struct)); /* get new nib */
      nib_ptr = nib_ptr->next_nib; /* and advance to it */
      nib_ptr->head_desc = NULL;
      nib_ptr->next_nib = NULL;
   }
}
/*****************************************************************************
** 
*****************************************************************************/
void do_nib_nickname(char *nick, char *rawdatnick, char *rawdatdesc, struct nib_struct *nib_ptr)
{
  char * field_ptr1;
  char * field_ptr2;
  char * field_ptr3;
  char errstr[80];

   if(rawdatnick == NULL)
   {
      if(rawdatdesc != NULL)
	 autogen_nickname(rawdatdesc, nick);
      else
	 error(stderr,"No Nibble name OR description given");
   }
   else if(strcmp(rawdatnick, UNUSED_token) != 0) /* no nick for UNUSED */
      strncpy(nick, rawdatnick, NICK_NAME_LIM);

   /* check for a [a:b] on end of nn */
   field_ptr1 = strstr(nick,"<");
   if(field_ptr1 != NULL)
   {
      field_ptr2 = field_ptr1 + 1;
      *field_ptr1 = '\0';  /* terminate nickname */
      field_ptr1 = strstr(field_ptr2,":");
      sprintf(errstr,"Badly formed <a:b> notation  : %s ",rawdatnick);
      if(field_ptr1 == NULL)
	 error(stderr,errstr);
      field_ptr3 = field_ptr1 + 1; /* point at second number */
      *field_ptr1 = '\0';  /* terminate start count number */
      /* now extract starting count */
      nib_ptr->start_char = (char)(int)strtol(field_ptr2,0,10);
      field_ptr1 = strstr(field_ptr3,">");
      *field_ptr1 = '\0';  /* terminate nickname */
      if((char)(int)strtol(field_ptr3,0,10) > nib_ptr->start_char)
	 nib_ptr->up_down = 1;
   }
}
/*****************************************************************************
** 
*****************************************************************************/
void parse_nib_line(struct reg_struct *reg, char *pdata[])
{
   char continuation = 0;
   struct nib_struct *nib_ptr;
   int start_bit;
   int bit;
   int mask_bit;
   int field;
   int tab_count;
   char new_desc[255];
   int i;

   if(pdata[0] == NULL)
      continuation = 1;
   else 
      {
	 start_bit = (int)strtol(pdata[0],0,10);
	 if(start_bit > top.reg_width)
	    error(stderr,"Bit number is higher than declared width of ASIC");
      }
	
   nib_ptr = NULL;
   if(reg->head_nib != NULL)    /* first nibble, first entry */
   {
      nib_ptr = reg->head_nib;
      while(nib_ptr->next_nib != NULL)
      {
	 if((nib_ptr->start_bit == start_bit) && (continuation != 1))
	 {
	    continuation = 1;
	    break;
	 }
	 nib_ptr = nib_ptr->next_nib;
      }
   }

   /* adjust pdata[3] for Donna */
   /* any tabs that were in the description line would have been parsed
      out as empty fields, so for each emtpy field add a tab when desc is found */
   /* also cat together all ending fields since tabs would have split them */
   /* bugfix 4/15/99 : but only if there is non-NULL data somewhere beyond.... */
   field = 3;
   tab_count = 0;
   new_desc[0] = '\0';

   while((pdata[field] == NULL) && (field < MAXFIELDS))
   {
      tab_count++;
      strcat(new_desc,"\t");
      field++;
   }
   if(field != MAXFIELDS)  /* desc found */
   {
      strcat(new_desc,pdata[field++]);
      while((field < MAXFIELDS) && (pdata[field] != NULL))
      {
	 strcat(new_desc,"\t");
	 strcat(new_desc,pdata[field++]);
      }
   }
   else
      new_desc[0] = '\0';

   /* skip other stuff when just a continuation */
   if(continuation != 1)
   {
      /* now search nibble tree.  If match is found then
	 a continuation, else malloc a new one */
 
      add_nib_to_reg(&nib_ptr, reg, start_bit);
      /* start bit established */
      nib_ptr->start_bit = start_bit;
      /* do end bit */
      if(pdata[1] == NULL)
	 nib_ptr->end_bit = start_bit;
      else if(!isdigit(pdata[1][0]))
	 error(stderr,"Bad nibble digit");
      else
	 nib_ptr->end_bit = (int)strtol(pdata[1],0,10);
      nib_ptr->start_char = nib_ptr->start_bit - nib_ptr->end_bit;

      /* now fill in nickname */
      do_nib_nickname(nib_ptr->nick_name, pdata[2], new_desc, nib_ptr);
   }
   else
      if(reg->head_nib == NULL)    /* first nibble, first entry */
	 error(stderr,"Cannot have a Nibble continuation here");

   /* either way this gets done */
   add_nib_desc(new_desc, nib_ptr);

   /* fill out reg bitmask value */
   for(bit = nib_ptr->end_bit; bit <= nib_ptr->start_bit; bit++)
   {
      mask_bit = 0x00000001 << bit;
      if(new_desc == NULL)
	 reg->reg_mask |= mask_bit;
      else
	 if(strcmp(new_desc, UNUSED_token) != 0)
	    reg->reg_mask |= mask_bit;
   }
}

/*****************************************************************************
** add a comment string to the structure 
*****************************************************************************/
void add_comment(struct reg_struct *reg, char *comment)
{
   struct com_struct *new_com, *com_ptr;
   char ref_com[COM_LENGTH];

   /* re-use nibble description structure for comments */
   new_com = malloc(sizeof(struct com_struct));
   strcpy(ref_com,comment);
   parse_copy(new_com->com, ref_com, COM_LENGTH, "Comment", NO_KILL_BADCHARS, COPY);

   new_com->next_com = NULL;
   if(reg->head_com == NULL)
      reg->head_com = new_com;
   else
   {
      com_ptr = reg->head_com;
      while(com_ptr->next_com != NULL)
	 com_ptr = com_ptr->next_com;
      com_ptr->next_com = new_com;
   }
}


/*****************************************************************************
** add a block to the top structure, initialize the entries 
** if the sort flag is set, add block in sorted order.
*****************************************************************************/
void add_new_block(lint start_offset, lint end_offset, char *name, char *abbrev, char int_ctrl_flag, 
		   uint default_instance_value)
{
   struct block_struct *new_block, *block_ptr;

   if(name == NULL)
      error(stderr, "Empty block name, could be missing a TAB");
   fprintf(stdout,"Adding block %s\n",name);
   new_block = malloc(sizeof(struct block_struct));
   new_block->next_block = NULL;
   new_block->longest_reg_name_in_block = 0;
   new_block->num_sub_blocks = 1;
   new_block->block_offset = start_offset;
   new_block->block_end = end_offset;
   new_block->default_instance_value = default_instance_value;
   new_block->num_block_regs = 0;
   new_block->head_reg = NULL;
   new_block->head_sub = NULL;
   new_block->int_ctrl_flag = int_ctrl_flag;
   parse_copy(new_block->block_name,name, BLOCK_NAME_LENGTH, "Block name",NO_KILL_BADCHARS,COPY);
   parse_copy(new_block->block_abbrev,abbrev, BLOCK_ABBRV_LENGTH, "Block Abbrv",KILL_BADCHARS,COPY);
   parse_copy(new_block->block_abbrevL,abbrev, BLOCK_ABBRV_LENGTH, "Block Abbrv",KILL_BADCHARS,LOWER);

   /* if no blocks yet, install as head block */
   if(top.block_head == NULL)
      top.block_head = new_block;
   else
   {
      /* search for block overlap */
      block_ptr = top.block_head;
      while(block_ptr != NULL) 
      {
	 if( ((new_block->block_offset > block_ptr->block_offset) && 
	      (new_block->block_offset < block_ptr->block_end)  )  || 
	     ((new_block->block_end    > block_ptr->block_offset) && 
	      (new_block->block_end    < block_ptr->block_end   )  )  ) 
	 {
	    fprintf(stderr,"+++++++++----->>>>>Warning: Blocks overlap: %s, %s.\n",
		    block_ptr->block_name, new_block->block_name);
	    offset_warning++;
	 }
	 block_ptr = block_ptr->next_block;
      }

      if(strncmp(abbrev,"INTCTRL",7) == 0)
	 /* tag on end of list */
      {
	 block_ptr = top.block_head;
	 /* search until offset exceeds other offset */
	 while(block_ptr->next_block != NULL)
	    block_ptr = block_ptr->next_block;
	 /* save link to rest of list */
	 new_block->next_block = block_ptr->next_block;
	 block_ptr->next_block = new_block;
      }
      else
      {
	 /* else insert into the block list */
	 block_ptr = top.block_head;
	 /* search until offset exceeds other offset */
	 while((block_ptr->next_block != NULL) && 
	       !(sort_data && (new_block->block_offset < block_ptr->next_block->block_offset)))
	    block_ptr = block_ptr->next_block;
	 /* save link to rest of list */
	 new_block->next_block = block_ptr->next_block;
	 block_ptr->next_block = new_block;
      }
   }
}
/*****************************************************************************
** alloc a new register and initialize
** values of -1 are flagged initializes to know if they are ever changed
*****************************************************************************/
struct reg_struct *get_new_register_struct()
{
   struct reg_struct *new_one;

   new_one = malloc(sizeof(struct reg_struct));
   if(new_one == NULL)
      error(stderr,"Could not alloc new reg struct");  /* init reg */
   new_one->reg_size = top.reg_width;  
   new_one->reg_offset = -1;
   new_one->reg_range = 0;
   strcpy(new_one->reg_nick_name,"");
   new_one->reg_read_write = 0;
   new_one->top_hini = NULL;
   new_one->top_aini = NULL;
   new_one->top_sini = NULL;
   new_one->format = DECIMAL;
   new_one->rept_value = -1;
   new_one->sub_block = 1;
   new_one->reg_mask = 0x00000000;
   new_one->print_flag = PRINT_NIBS;
   new_one->head_nib = NULL;
   new_one->head_com = NULL;
   new_one->next_reg = NULL;              /* terminate this list */
   new_one->masked_by_offset = -1;        /* terminate this list */
   new_one->masked_by_block = NULL;          /* terminate this list */
   new_one->end_index = -1;
   new_one->disp_length = 4;
   new_one->disp_base = 'H';
   return(new_one);
}

/*****************************************************************************
** add a filled-in register to its block
*****************************************************************************/
void add_reg_to_block(struct block_struct * block_ptr, struct reg_struct *new_reg, char unique_check)
{
   struct reg_struct *reg_ptr;
   struct sub_block_struct *sub_ptr;
   unsigned long i;
   uchr dummy_line[MAX_INP];
   char *dummy_data[MAXFIELDS];
   int reglen;
   char errstr[80];
   char * test;

   new_reg->hi_bit = -1;
   i=new_reg->reg_mask;
   while(i)
   {
      i >>= 1;
      new_reg->hi_bit++;
   }
   /* check to see if block table declared already */
   if(top.block_head == NULL) /* no table was declared */
      add_new_block(0x00000000, 0xffffffff , "ASIC BLOCK", "AB", 0, 1);

   /* check to be sure mask register specified if an interrupt register */
   if((new_reg->masked_by_offset == -1) && (new_reg->reg_read_write & INTERRUPT_ACCESS))
   {
      fprintf(stderr,"+++++++++----->>>>>Warning: no mask specified for Interrupt Register: %s.\n",
	      new_reg->reg_name);
      mask_warning++;
   }
   /* check to see if block is specified for mask register */
   if( (new_reg->masked_by_offset != -1) && (new_reg->masked_by_block == NULL) 
      && (new_reg->reg_read_write & INTERRUPT_ACCESS))
   {
      fprintf(stderr,"+++++++++----->>>>>Warning: Assuming 0. No block specified for mask of Interrupt Register: %s.\n",
	      new_reg->reg_name);
      mask_warning++;
      new_reg->masked_by_block = top.block_head ;  /* mz fix me */
   }
   /* check to see if mask declared when NOT an interrupt reg */
   if((new_reg->masked_by_offset != -1) && !(new_reg->reg_read_write & INTERRUPT_ACCESS))
   {
      fprintf(stderr,"+++++++++----->>>>>Warning: mask specified for a non Interrupt Register: %s.\n",
	      new_reg->reg_name);
      mask_warning++;
   }
   /* if no nickname specified, make one */
   if(strcmp(new_reg->reg_nick_name,"") == 0)
      autogen_nickname(new_reg->reg_name, new_reg->reg_nick_name);

   /* if a repeat register be sure to add in #r# */
   if((new_reg->rept_value != -1) && (strstr(new_reg->reg_nick_name,"#r#") == NULL))
      strcat(new_reg->reg_nick_name,"#r#");
   test = strstr(new_reg->reg_name,"#r#");
   if((new_reg->rept_value != -1) && (strstr(new_reg->reg_name,"#r#") == NULL))
      strcat(new_reg->reg_name,"#r#");

   parse_copy(new_reg->reg_nick_name, new_reg->reg_nick_name, REG_ABBV_LENGTH,"Reg Abbrv",
	      NO_KILL_BADCHARS,LOWER);

   /* check to see if bits are declared */
   if(new_reg->head_nib == NULL)
   {
      fprintf(stderr,"+++++++++----->>>>>Warning: no bit fields specified for %s.\n",new_reg->reg_name);
      bitfield_warning++;

      /* add full reg_width..0 unused */
      sprintf(dummy_line,"%d\t%d\t\t%s\n",top.reg_width-1,0,UNUSED_token);
      parse_keyword_line(dummy_line, dummy_data, NULL, 1);
      parse_nib_line(new_reg,dummy_data);
   }

   /* insert/append new register into the struct and initialize */ 
   if(block_ptr->head_reg == NULL)                /* if first reg place in head position*/
      block_ptr->head_reg = new_reg;
   else
   {
      if((new_reg->reg_offset+block_ptr->block_offset) > block_ptr->block_end)
      {
	 fprintf(stderr,"+++++++++----->>>>>Warning: Register offset 0x%x outside offset limits for its block.\n",
		 new_reg->reg_offset);
	 offset_warning++;
      }

      reg_ptr = block_ptr->head_reg;
      while(reg_ptr->next_reg != NULL)
      {
	 /* make sure another register with the same offset doesn't exist */
	 if(unique_check)
	    if(reg_ptr->reg_offset == new_reg->reg_offset)
	 {
	    sprintf(errstr,"+++++++++----->>>>>Error: Register offset 0x%x repeated in this block.\n", new_reg->reg_offset);
	    error(stderr,errstr);
	 }
	 if(strlen(new_reg->reg_nick_name) > 20)
	    error(stderr,"The :abvr: name is too long... abbreviations by nature should be short (limit 15 chars)\n");
	 reg_ptr = reg_ptr->next_reg;   /* go to end of reg branch */
      }
      if(unique_check)
	 if(reg_ptr->reg_offset == new_reg->reg_offset)
      {
	 sprintf(errstr,"+++++++++----->>>>>Error: Register offset 0x%x repeated in this block.\n", new_reg->reg_offset);
	 error(stderr,errstr);
      }

      /* insert register into sorted list */
      reg_ptr = block_ptr->head_reg;
      if(sort_data)
      {
	 /* start case */
	 if(new_reg->reg_offset < reg_ptr->reg_offset)

	 {
	    new_reg->next_reg = block_ptr->head_reg;
	    block_ptr->head_reg = new_reg;
	 }
	 else
	 {
	    while((reg_ptr->next_reg != NULL) && 
		  !(sort_data && (new_reg->reg_offset < reg_ptr->next_reg->reg_offset)))
	       reg_ptr = reg_ptr->next_reg;
	    new_reg->next_reg = reg_ptr->next_reg;
	    reg_ptr->next_reg = new_reg;
	 }
      }
      else
      {
	 new_reg->next_reg = reg_ptr->next_reg;
	 reg_ptr->next_reg = new_reg;
      }
   }      
   block_ptr->num_block_regs = block_ptr->num_block_regs + 1;
   top.total_num_regs++;
   if((text_out == HIT) && (new_reg->print_flag & SKIP_HIT) ||
      (text_out == NEW_HIT) && (new_reg->print_flag & SKIP_HIT) ||
      (text_out == VLOG) && (new_reg->print_flag & SKIP_VLOG))
   {
      block_ptr->num_block_regs = block_ptr->num_block_regs - 1;
      top.total_num_regs--;
   }
   /* now check to see if the length of this reg is longer than others */
   reglen = strlen(new_reg->reg_name);
   if(reglen > block_ptr->longest_reg_name_in_block)
      block_ptr->longest_reg_name_in_block = reglen;
	 
   /* determine register sub-block */
   if(block_ptr->num_sub_blocks > 1)
   {
      /* search for fit */
      sub_ptr = block_ptr->head_sub;
      while(sub_ptr != NULL)
      {
	 if((new_reg->reg_offset >= sub_ptr->sub_start) && (new_reg->reg_offset <= sub_ptr->sub_end))
	 {
	    new_reg->sub_block = sub_ptr->sub_num;
	    sub_ptr->num_sub_regs = sub_ptr->num_sub_regs + 1;
	    break;
	 }
	 sub_ptr = sub_ptr->next_sub;
      }
   }
   fprintf(stderr,"REG : %s\n",new_reg->reg_name);
}
/* */

/*****************************************************************************
** get a line from the file, ignore comment lines and blank lines 
**
*****************************************************************************/
void get_line(char *inprec, char **fetch, int *state, uchr mode)
{
   char * in_ptr;
   char good_line;
   struct file_struct *prev_file;
   char include_file[200];
   char *dummy_data[MAXFIELDS];
   struct reg_struct *new_reg;

   do
   {
      good_line = 1;
      *fetch = fgets(inprec, MAX_INP, &(*current_file->fi));
      if(*fetch == NULL)
      {
	 /* end of one file, pop stack and see if parent file is done */
	 if(current_file->parent == NULL)
	    return;
	 else do
	 {
	    current_file = current_file->parent;
	    *fetch = fgets(inprec, MAX_INP, &(*current_file->fi));
	 } while ((*fetch == NULL ) && (current_file->parent != NULL));
      }
      if(*fetch == NULL)
	 return;
      /* need to search for first white space being // comments and */
      /* retrieve another line in its place.... */
      in_ptr = *fetch;
      while((*in_ptr != '\0') && (*in_ptr != '\n') && ((*in_ptr == ' ') || (*in_ptr == '\t')))
	 in_ptr++;
      if((*in_ptr == '\0') || (*in_ptr == '\n')) /* line of white space */
	 good_line = 0;
      else if((*in_ptr != '\0') && (*in_ptr != '\n') && (*in_ptr == COMMENT_CHAR))
      {
	 in_ptr++;
	 if(*in_ptr == COMMENT_CHAR)
	    good_line = 0;
      }
      if(strncmp(inprec,INCLUDE_KEYWORD,7) == 0)
      {
	 /* build link to new file, backward linked list */
	 /* need to extract new file name from quotes */
	 parse_keyword_line(inprec, dummy_data, INCLUDE_KEYWORD, 0);
	 strcpy(include_file,dummy_data[0]);
	 
	 prev_file = current_file;
	 current_file = malloc(sizeof(struct file_struct));
	 if((current_file->fi = fopen(include_file,"r")) == NULL)
	 {   perror(include_file);     exit(-1);  };
	 current_file->parent = prev_file;
	 good_line = 0;
      }
   }while(good_line == 0);
}
/*****************************************************************************/
/* This routine creates a virtual set of registers that are only used by 
   the HIT code.  These registers are for the interrupt regs in the asic.  
   These allow those regs to have the reporting of interrupts masked.  It
   was just easier to build them here and have the parser create the structure 

02/24/98 MZ Changing to generate blocks less than INT_REG_LIM  registers, restriction from
 packet size.
*/

#define INT_REG_LIM 90

void add_hit_rptctrl_regs()
{
   struct reg_struct *reg_ptr;  
   struct reg_struct *rptctrl_reg;  
   struct reg_struct *mask_int_reg;  
   struct block_struct *block_ptr; 
   struct nib_struct *nib_ptr; 
   struct nib_struct *nib2_ptr; 
   struct nib_desc_struct *desc_ptr; 
   struct nib_desc_struct *desc2_ptr; 
   int index_num;
   int num_in_this_int_block;
   int int_block_num;
   char int_block_name[80];
   char int_block_nickname[20];

   index_num = 0;
   int_block_num = 0;
   num_in_this_int_block = 0;

   block_ptr = top.block_head;

   strcpy(int_block_name,"Interrupt Behavior Control Registers, Block X");
   strcpy(int_block_nickname,"INTCTRLX");

   while(block_ptr != NULL)
   {
      reg_ptr = block_ptr->head_reg;
      while(reg_ptr != NULL)
      {
	 if(reg_ptr->reg_read_write & INTERRUPT_ACCESS)
	 {
	    /* check to see if a new block is needed, if so add it */
	    if( (num_in_this_int_block % INT_REG_LIM) == 0)
	    {
	       int_block_num++;
	       int_block_name[44] = (char)('0' + int_block_num);
	       int_block_nickname[7]  = (char)('0' + int_block_num);
	       add_new_block(0x0000000, 0x01000000, int_block_name, int_block_nickname, 1, 1 );
	    }
	    num_in_this_int_block += 2;
	    rptctrl_reg = get_new_register_struct();
	    mask_int_reg = get_new_register_struct();
	    strcpy(rptctrl_reg->reg_name, reg_ptr->reg_name);  
	    strcpy(mask_int_reg->reg_name, reg_ptr->reg_name);  
	    strcat(rptctrl_reg->reg_name, " Report control");
	    strcat(mask_int_reg->reg_name, " Masking Option");
	    rptctrl_reg->rept_value = reg_ptr->rept_value;
	    mask_int_reg->rept_value = reg_ptr->rept_value;
	    strcpy(rptctrl_reg->reg_nick_name, reg_ptr->reg_nick_name);  
	    strcpy(mask_int_reg->reg_nick_name, reg_ptr->reg_nick_name);  
	    strcat(rptctrl_reg->reg_nick_name, "rc");
	    strcat(mask_int_reg->reg_nick_name, "mi");
	    rptctrl_reg->reg_size = top.reg_width;  
	    mask_int_reg->reg_size = top.reg_width;  
	    /* use the offset of these registers to tell us what index
               to use to access them in the aasic_interrupt structure where
               the value will actually be stored */
	    rptctrl_reg->reg_offset = index_num;
	    mask_int_reg->reg_offset = index_num++;
	    rptctrl_reg->reg_range = 0;
	    mask_int_reg->reg_range = 0;
	    rptctrl_reg->reg_read_write = READ_ACCESS | WRITE_ACCESS;
	    mask_int_reg->reg_read_write = READ_ACCESS | WRITE_ACCESS;

	    rptctrl_reg->top_hini = malloc(sizeof(struct ini_struct)); 
	    strcpy(rptctrl_reg->top_hini->ini,"0x0000");
	    /* reg_ptr->reg_mask;  defaults to rept no ints */
	    rptctrl_reg->top_hini->label[0] = '\0'; 
	    rptctrl_reg->top_hini->next_ini = NULL; 
	    mask_int_reg->top_hini = malloc(sizeof(struct ini_struct)); 
	    sprintf( mask_int_reg->top_hini->ini, "%d", reg_ptr->reg_mask); 
            /* reg_ptr->reg_mask;  defaults to mask all ints */
	    mask_int_reg->top_hini->label[0] = '\0';
	    mask_int_reg->top_hini->next_ini = NULL;

	    rptctrl_reg->reg_mask = reg_ptr->reg_mask; /* only need to worry about same bits */
	    mask_int_reg->reg_mask = reg_ptr->reg_mask; /* only need to worry about same bits */

	    /* Now need to put in some text for help */

	    desc_ptr = malloc(sizeof(struct nib_desc_struct)); /* need a new desc field */
	    desc_ptr->next_desc = NULL;

	    rptctrl_reg->head_nib = nib_ptr = malloc(sizeof(struct nib_struct));
	    nib_ptr->head_desc = desc_ptr;
	    nib_ptr->start_bit = top.reg_width - 1;
	    nib_ptr->end_bit = 0;
	    sprintf(desc_ptr->bit_val_desc, "Bits control interrupt register: %s",reg_ptr->reg_name);
	    rptctrl_reg->head_nib = nib_ptr;
	    desc_ptr->next_desc = malloc(sizeof(struct nib_desc_struct)); /* need a new desc field */
	    desc_ptr = desc_ptr->next_desc;
	    sprintf(desc_ptr->bit_val_desc, "a '1' will cause the interrupt to be reported, '0' to not report.");
	    desc_ptr->next_desc = malloc(sizeof(struct nib_desc_struct)); /* need a new desc field */
	    desc_ptr = desc_ptr->next_desc;
	    sprintf(desc_ptr->bit_val_desc, "This does not affect the interrupt, just whether");
	    desc_ptr->next_desc = malloc(sizeof(struct nib_desc_struct)); /* need a new desc field */
	    desc_ptr = desc_ptr->next_desc;
	    sprintf(desc_ptr->bit_val_desc, "they are REPORTED.  Not a true ASIC register.");
	    desc_ptr->next_desc = NULL;
	    desc2_ptr = malloc(sizeof(struct nib_desc_struct)); /* need a new desc field */
	    desc2_ptr->next_desc = NULL;
	    mask_int_reg->head_nib = nib2_ptr = malloc(sizeof(struct nib_struct));
	    nib2_ptr->head_desc = desc2_ptr;
	    nib2_ptr->start_bit = top.reg_width - 1;
	    nib2_ptr->end_bit = 0;
	    sprintf(desc2_ptr->bit_val_desc, "Bits control masking of interrupt register: %s",reg_ptr->reg_name);
	    mask_int_reg->head_nib = nib2_ptr;
	    desc2_ptr->next_desc = malloc(sizeof(struct nib_desc_struct)); /* need a new desc field */
	    desc2_ptr = desc2_ptr->next_desc;
	    sprintf(desc2_ptr->bit_val_desc, "A '1' will mask the interrupt(in the ASIC) when it");
	    desc2_ptr->next_desc = malloc(sizeof(struct nib_desc_struct)); /* need a new desc field */
	    desc2_ptr = desc2_ptr->next_desc;
	    sprintf(desc2_ptr->bit_val_desc, "occurs.  This does not affect the reporting of the interrupt");
	    desc2_ptr->next_desc = malloc(sizeof(struct nib_desc_struct)); /* need a new desc field */
	    desc2_ptr = desc2_ptr->next_desc;
	    sprintf(desc2_ptr->bit_val_desc, "that is the RPTCTRL register.  A '0' will allow the interrupt");
	    desc2_ptr->next_desc = malloc(sizeof(struct nib_desc_struct)); /* need a new desc field */
	    desc2_ptr = desc2_ptr->next_desc;
	    sprintf(desc2_ptr->bit_val_desc, "to occur and clear without being masked. This will be most useful");
	    desc2_ptr->next_desc = malloc(sizeof(struct nib_desc_struct)); /* need a new desc field */
	    desc2_ptr = desc2_ptr->next_desc;
	    sprintf(desc2_ptr->bit_val_desc, "for interrupts that cause endless loops as they continuously");
	    desc2_ptr->next_desc = malloc(sizeof(struct nib_desc_struct)); /* need a new desc field */
	    desc2_ptr = desc2_ptr->next_desc;
	    sprintf(desc2_ptr->bit_val_desc, "set and reset, hanging up HIT.  Not a true ASIC register.");
	    desc2_ptr->next_desc = NULL;

	    add_reg_to_block( find_block(int_block_nickname), rptctrl_reg, 0);
	    add_reg_to_block( find_block(int_block_nickname), mask_int_reg, 0);

	 }
	 reg_ptr = reg_ptr->next_reg;
      }
      block_ptr = block_ptr->next_block;
   } 
}


/*****************************************************************************/
struct block_struct *find_block_for_reg_offset(int *reg_offset)
{
   struct block_struct *block_ptr;

   if(top.block_head == NULL)
      error(stderr,"No block table to search .... ");
   block_ptr = top.block_head;
   while( (*reg_offset < block_ptr->block_offset ) || (*reg_offset > block_ptr->block_end ))
   {
      block_ptr = block_ptr->next_block;
      if(block_ptr == NULL)
	 break;
   }
   if(block_ptr == NULL)
      block_ptr = top.block_head;
   fprintf(stderr,"+++++++++----->>>>>Warning: No Block specified for this register, using block %s\n",block_ptr->block_name);

   *reg_offset = *reg_offset - block_ptr->block_offset;
   return(block_ptr);
}
/*****************************************************************************/
struct reg_struct * copy_reg(struct reg_struct *src_reg)
{
   struct reg_struct *new_reg;
   struct com_struct *com_ptr, new_com;

   /* allocate new register and copy src register to it */
   new_reg = get_new_register_struct();
   /* brute force copy of top level stuff */
   memcpy(new_reg, src_reg, sizeof(struct reg_struct));
   new_reg->reg_range = 0;
   /* now the lower level stuff */
   /* this is not needed.  It can be shared as it is common information */
   /* or should be anyway.  Any block references should use SELF as     */
   /* much as applicable */
   return(new_reg);
}

/*****************************************************************************/
void copy_block(char *src_block, char *dest_block)
{
   struct block_struct *src_block_ptr, *dest_block_ptr;
   struct reg_struct *src_reg_ptr, *dest_reg_ptr;

   fprintf(stderr,"Copying block %s to block %s\n",src_block, dest_block);
   src_block_ptr = find_block(src_block);
   dest_block_ptr = find_block(dest_block);

   /* start at the top of the block and copy all registers */
   src_reg_ptr = src_block_ptr->head_reg;

   while(src_reg_ptr != NULL)
   {
      if(strstr(src_reg_ptr->reg_name,"#b#") == NULL)
      {
	 block_warning++;
	 fprintf(stderr,"Warning: no #b# in register (%s) name used in block copy, block #b# appended to name\n",src_reg_ptr->reg_name);
	 strcat(src_reg_ptr->reg_name,", block #b#");
      }
      if(strstr(src_reg_ptr->reg_nick_name,"#b#") == NULL)
      {
	 block_warning++;
	 fprintf(stderr,"Warning: no #b# in register nick name (%s) used in block copy, block #b# appended to nickname\n",src_reg_ptr->reg_nick_name);
	 strcat(src_reg_ptr->reg_nick_name,"#b#");
      }
      dest_reg_ptr = copy_reg(src_reg_ptr);

      /* now adjust all block references */
      dest_reg_ptr->masked_by_block = dest_block_ptr;

      /* change prnt to SKIP_NIBS 
      dest_reg_ptr->print_flag |= SKIP_NIBS; */

      /* terminate as end of chain */
      dest_reg_ptr->next_reg = NULL;

      /* now add it in with unique checks */
      add_reg_to_block(dest_block_ptr,dest_reg_ptr, 0);

      /* move to next src reg */
      src_reg_ptr = src_reg_ptr->next_reg;
   }
}
/*****************************************************************************/
void expand_rept_reg(struct reg_struct *rept_reg_ptr, struct rept_struct *rept_info)
{

   struct reg_struct *new_reg;
   char cat_str[20];

   rept_info->count = 0;
   rept_info->start_index += rept_info->increment; /* first reg already added */

   while(rept_info->start_index <= rept_info->end_index)
   {
      /* make new register */
      new_reg = copy_reg(rept_reg_ptr);
      
      rept_info->count += rept_info->increment;

      /*adjust its offset */
      new_reg->reg_offset = rept_reg_ptr->reg_offset + (rept_info->count * rept_info->addr_increment);

      /* set REPV value */
      new_reg->rept_value = rept_info->start_index;

      rept_info->start_index += rept_info->increment;
      if((rept_info->skip_interval == -1) || (rept_info->count % rept_info->skip_interval != 0))
	 add_reg_to_block(rept_info->block_ptr, new_reg, 1);
   }
   
}

/*****************************************************************************/

void read_files_into_data_structure(uchr mode) 
/* read the input file into the asic structure */
{
   uchr inprec[MAX_INP];
   uchr dummy_line[MAX_INP];
   struct reg_struct *asic_reg;  /* points to a struct from the alloc routine */
   struct reg_struct *sameas;  
   struct block_struct *sameas_block_ptr; 
   struct nib_struct *dummy_nib;
   char *parsed_data[MAXFIELDS];
   char *dummy_data[MAXFIELDS];
   struct block_struct * block_ptr;
   int state;
   char save_line;
   char *fetch, *com_line;
   char err_str[80];
   lint block_offset;
   char entry;
   char repeat_flag;
   struct ini_struct *ini_ptr;
   struct rept_struct rept_info;
   int field;

   state = IN_FILE_NO_REG_YET;
   save_line = 0;
   asic_reg = NULL;
   repeat_flag = 0;
   get_line(inprec, &fetch, &state, mode);

   fprintf(stderr,"\nProcessing data file...\n");
   fprintf(stderr,"Registers processed....\n");

   /* while in the file */
   /* look for new reg, or top level key word */
   while( (fetch != NULL) && (state != END_OF_FILE))
   {
      if(state == IN_FILE_NO_REG_YET)  /* search for a REGister or BLOCK */
      {
	 asic_reg = NULL;
	 while(( strncmp (inprec,NAME_KEYWORD,5) !=0 ) && ( strncmp (inprec,":BLOCK:",7) !=0 ))
	 {
	    if(strncmp (inprec,ASIC_KEYWORD,KEY_LEN) == 0 )
	    {
	       parse_keyword_line(inprec, parsed_data, ASIC_KEYWORD,1);
	       parse_copy(top.asic_name, parsed_data[0], ASIC_NAME_LENGTH, "ASIC name",
			  KILL_BADCHARS,COPY);
	       parse_copy(top.asic_nameL, parsed_data[0], ASIC_NAME_LENGTH, "ASIC name",
			  KILL_BADCHARS,LOWER);

	       if((mode == HIT) || (mode == NEW_HIT))
	       {
		  sprintf(err_str,"%s_hit.log",top.asic_nameL);
		  if((fo_hit_log = fopen(err_str,"w")) == NULL)
		  {   perror(err_str);     exit(-1);  };
	       }    
	    }
	    if(strncmp (inprec,REGWIDTH_KEYWORD,11) == 0 )
	    {
	       parse_keyword_line(inprec, parsed_data, REGWIDTH_KEYWORD,0);
	       top.reg_width = (int)strtol(parsed_data[0],0,10);
	       
	       if(top.reg_width > 16)
	       {
		  if (mode != NEW_HIT)
		     error(stderr,"\nREG_WIDTH > 16 only supported for HIT.  Interleaf format not defined.\n");
		  else
		     fprintf(stderr,"\nWarning reg width over 16 bits, may not be fully tested yet\n");
	       }
	    }
	    if(strncmp (inprec,UNUSED_KEYWORD,8) == 0 )
	    {
	       parse_keyword_line(inprec, parsed_data, UNUSED_KEYWORD,0);
	       top.unused = parsed_data[0][0];  
	    }	
	    /* block copy */
	    if(strncmp (inprec,BLOCK_COPY_KEYWORD,5) == 0 )
	    {
	       block_copy_flag = 1;
	       parse_keyword_line(inprec, parsed_data,BLOCK_COPY_KEYWORD , 1);
	       if((parsed_data[0] == NULL) || (parsed_data[1] == NULL))
		  error(stderr,"Null field found in block copy statement.");
	       copy_block(parsed_data[0], parsed_data[1]);
	       state = IN_FILE_NO_REG_YET;
	    }	
	    get_line(inprec, &fetch, &state, mode);
	
	    if(fetch == NULL)      /* end of FILE reached */
	    {
	       state = END_OF_FILE;
	       /* need to jump to data output section */

	       if((mode == HIT) || (mode == NEW_HIT))
	       {
		  /* at the end of the file for HIT code we want to
		     add in a false set of registers, the interrupt
		     report masking registers.  The easiest way is
		     to let the code generate them as if they were real. */
		  add_hit_rptctrl_regs();
	       }
	       break;
	    }
	 }
      } /* end if IN_FILE_NO_REG_YET */

      if(strncmp (inprec,":REG_END:",8) == 0 )
      {
	 if (asic_reg != NULL)  /* so we have been filling a register, no block found */
	 {
	    if(asic_reg->reg_offset == -1)
	       error(stderr,"No offset recorded for this register");
	    add_reg_to_block(block_ptr, asic_reg, 1);
	    if(repeat_flag == 1)
	       expand_rept_reg(asic_reg, &rept_info);
	    repeat_flag = 0;
	 }
	    state = IN_FILE_NO_REG_YET;
      }
      if(strncmp (inprec,":BLOCK_END:",10) == 0 ) 
	 state = IN_FILE_NO_REG_YET;

      switch(state)
      {
	 case IN_FILE_NO_REG_YET:
	    while( (strncmp (inprec,NAME_KEYWORD,5) !=0 ) && ( strncmp (inprec,BLOCK_COPY_KEYWORD,5) !=0 ) &&
		   ( strncmp (inprec,":BLOCK:",7) !=0 ) )
	    {
	       get_line(inprec, &fetch, &state, mode);
	       if((fetch == NULL))      /* end of FILE reached */
	       {
		  state = END_OF_FILE;
		  if((mode == HIT) || (mode == NEW_HIT))
		  {
		     /* at the end of the file for HIT code we want to
			add in a false set of registers, the interrupt
			report masking registers.  The easiest way is
			to let the code generate them as if they were real. */
		     add_hit_rptctrl_regs();
		  }		  /* need to jump to data output section */
		  break;
	       }
	    }
	    if(state == IN_FILE_NO_REG_YET) /* register must have been found */
	    {
	       if(strncmp (inprec,NAME_KEYWORD,5) == 0 )
	       {
		  /* alloc the new register */
		  asic_reg = get_new_register_struct();
		  state = IN_FILE_WITH_REG;
		  if(top.reg_width == -1)
		     error(stderr,"No REG_WIDTH found before first register.");
	       }
	       else if(strncmp (inprec,":BLOCK:",7) == 0 )
	       {
		  state = BLOCK_TABLE;
	       }

               /* block copy */
	       else if(strncmp (inprec,BLOCK_COPY_KEYWORD,5) == 0 )
	       {
		  block_copy_flag = 1;
		  parse_keyword_line(inprec, parsed_data,BLOCK_COPY_KEYWORD , 1);
		  if((parsed_data[0] == NULL) || (parsed_data[1] == NULL))
		     error(stderr,"Null field found in block copy statement.");
		  copy_block(parsed_data[0], parsed_data[1]);
		  state = IN_FILE_NO_REG_YET;
	       }

	    }
	    break;
	 case BLOCK_TABLE:
	    parse_keyword_line(inprec, parsed_data, NULL, 1);
	    if((parsed_data[1][1] != 'x') || (parsed_data[2][1] != 'x'))
	       error(stderr,"All block addrs should be in hex.  Preceed all hex #'s with 0x.");
	    add_new_block(strtol(parsed_data[1],0,16), strtol(parsed_data[2],0,16),
			  parsed_data[4], parsed_data[3], 0, strtol(parsed_data[0],0,10) );
	    break;
	 case NIB_STATE:
	    if(inprec[0] == ':') /* leaving the nibble state, make sure we have descrips */
	    {
	       /* need to check for unused bits... either a totally */
	       /* unspecified reg, or with the lower end bits unused */
	       if(asic_reg->head_nib == NULL)
	       {
		  /* add full reg_width..0 unused */
		  sprintf(dummy_line,"%d\t%d\t\t%s\n",top.reg_width-1,0,UNUSED_token);
		  parse_keyword_line(dummy_line, dummy_data, NULL, 1);
		  parse_nib_line(asic_reg,dummy_data);
	       }
	       else /* shift down to last nib */
	       { 
		  dummy_nib = asic_reg->head_nib;
		  while(dummy_nib->next_nib != NULL)
		     dummy_nib = dummy_nib->next_nib;
		  if(dummy_nib->end_bit != 0 )/* if end bit != 0 */
		  { 
		     /* add end bit..0 unused */
		     sprintf(dummy_line,"%d\t%d\t\t%s\n",dummy_nib->end_bit-1,0,UNUSED_token);
		     parse_keyword_line(dummy_line, dummy_data, NULL, 1);
		     parse_nib_line(asic_reg,dummy_data);
		  }
	       }
	       state = IN_FILE_WITH_REG;
	       break;
	    }
	    parse_keyword_line(inprec, parsed_data, NULL, 1);
	    /* field 0 start bit
                     1 end bit
                     2 nickname
                     3 text description
		     */
	    parse_nib_line(asic_reg,parsed_data);
	    break;
	 case COMMENT_STATE:
	    if((inprec[0] == ':') && (strncmp(inprec,CMNT_KEYWORD,KEY_LEN) != 0))
	    {
	       state = IN_FILE_WITH_REG;
	       break;
	    }
	    if(save_line == 1)
	    {
	       save_line = 0;
	       com_line = strstr(inprec,":");
	       com_line++;
	       com_line = strstr(com_line,":");
	       com_line++;
	       com_line++; /* remove the tab */
	       if((com_line[0] != '\n') && (com_line != NULL) )
		  add_comment(asic_reg, com_line);
	       inprec[0] = ' '; /* skip past cmnt keyword */
	    }
	    else
	       add_comment(asic_reg, inprec);
	    save_line = 0;
	    break;
	 case IN_FILE_WITH_REG:
	    break;
	 case END_OF_FILE:
	    break;
	
	 default: 
	    error(stderr, "Unknown state reached");
	    break;
      }

      if(strncmp (inprec,ADDR_KEYWORD,KEY_LEN) == 0 )
      {
	    parse_keyword_line(inprec, parsed_data, ADDR_KEYWORD,0);
	    asic_reg->reg_offset = (int)strtol(parsed_data[0],0,16);
	    if(parsed_data[1] != NULL)
	       block_ptr = find_block(parsed_data[1]);
	    else
	    {
	       warning++;
	       block_ptr = find_block_for_reg_offset(&(asic_reg->reg_offset));
	    }
      }
      if(strncmp (inprec,NAME_KEYWORD,KEY_LEN) == 0 )
      {
	 if(block_copy_flag != 0)
	    error(stderr,"CANNOT have a new register after block copy statements");
	 parse_keyword_line(inprec, parsed_data, NAME_KEYWORD,1);
	 if(parsed_data[0] == NULL)
	    error(stderr,"No name specified for next REG");
	 parse_copy(asic_reg->reg_name, parsed_data[0], REG_NAME_LENGTH, 
		    "Reg name",NO_KILL_BADCHARS,COPY);
      }
      if(strncmp (inprec,RANGE_KEYWORD,KEY_LEN) == 0 )
      {
	    parse_keyword_line(inprec, parsed_data, RANGE_KEYWORD,0);
	    asic_reg->reg_range = (int)strtol(parsed_data[0],0,16);
      }
      if(strncmp (inprec,ABRV_KEYWORD,KEY_LEN) == 0 )
      {
	 parse_keyword_line(inprec, parsed_data, ABRV_KEYWORD,1);
	 if(parsed_data[0] != NULL)
	    parse_copy(asic_reg->reg_nick_name, parsed_data[0], 
		       REG_ABBV_LENGTH,"Reg Abbrv",NO_KILL_BADCHARS,LOWER);
      }
      if(strncmp (inprec,HINI_KEYWORD,KEY_LEN) == 0 )
      {
	 parse_keyword_line(inprec, parsed_data, HINI_KEYWORD,1);
	 if(parsed_data[0] == NULL)
	 {
	    if(((text_out == HIT) || (text_out == NEW_HIT)) && (asic_reg->reg_read_write & WRITE_ACCESS) )
	       fprintf(fo_hit_log, "Register %s has no HIT initial value\n", asic_reg->reg_name);
	    hini_warning++;
	 }
	 else /* handle multiple defaults */
	 {
	    ini_ptr =  asic_reg->top_hini;
	    if(ini_ptr != NULL)
	    {
	       while(  ini_ptr->next_ini != NULL )
		  ini_ptr = ini_ptr->next_ini ;
	       ini_ptr->next_ini = malloc(sizeof(struct ini_struct)); 
	       ini_ptr = ini_ptr->next_ini;
	    }
	    else
	       asic_reg->top_hini = ini_ptr = malloc(sizeof(struct ini_struct)); 
	    strcpy(ini_ptr->ini,parsed_data[0]);
	    if(parsed_data[1] != NULL)
	       strcpy(ini_ptr->label,parsed_data[1]);
	    else
	       ini_ptr->label[0] = '\0';
	    ini_ptr->next_ini = NULL;
	 }
      }
      if(strncmp (inprec,AINI_KEYWORD,KEY_LEN) == 0 )
      {
	 parse_keyword_line(inprec, parsed_data, AINI_KEYWORD,1);
	 if(parsed_data[0] != NULL)
	 {
	    ini_ptr =  asic_reg->top_aini;
	    if(ini_ptr != NULL)
	    {
	       while(  ini_ptr->next_ini != NULL )
		  ini_ptr = ini_ptr->next_ini ;
	       ini_ptr->next_ini = malloc(sizeof(struct ini_struct)); 
	       ini_ptr = ini_ptr->next_ini;
	    }
	    else
	       asic_reg->top_hini = ini_ptr = malloc(sizeof(struct ini_struct)); 
	    strcpy(ini_ptr->ini,parsed_data[0]);
	    if(parsed_data[1] != NULL)
	       strcpy(ini_ptr->label,parsed_data[1]);
	    else
	       ini_ptr->label[0] = '\0';
	    ini_ptr->next_ini = NULL;
	 }
      }
      
      
      if(strncmp (inprec,SINI_KEYWORD,KEY_LEN) == 0 )
      {
	 parse_keyword_line(inprec, parsed_data, SINI_KEYWORD,1);
	 if(parsed_data[0] != NULL)
	 {
	    ini_ptr =  asic_reg->top_sini;
	    if(ini_ptr != NULL)
	    {
	       while(  ini_ptr->next_ini != NULL )
		  ini_ptr = ini_ptr->next_ini ;
	       ini_ptr->next_ini = malloc(sizeof(struct ini_struct)); 
	       ini_ptr = ini_ptr->next_ini;
	    }
	    else
	       asic_reg->top_sini = ini_ptr = malloc(sizeof(struct ini_struct)); 
	    strcpy(ini_ptr->ini,parsed_data[0]);
	    if(parsed_data[1] != NULL)
	       strcpy(ini_ptr->label,parsed_data[1]);
	    else
	       ini_ptr->label[0] = '\0';
	    ini_ptr->next_ini = NULL;
	 }
      }
      

      if(strncmp (inprec,RWCI_KEYWORD,KEY_LEN) == 0 )
      {
	 parse_keyword_line(inprec, parsed_data, RWCI_KEYWORD,0);
	 if(parsed_data[0] == NULL)
	    error(stderr,"No access type specified");
	 parse_access_type(asic_reg,parsed_data);
      }
      if(strncmp (inprec,NIB_KEYWORD,4) == 0 )
	    state = NIB_STATE;


      if(strncmp (inprec,ALFA_KEYWORD,KEY_LEN) == 0 )
	 error(stderr,"Please change :rept: line to include ALPHA string there\n");

      /* note REPV is an INTERNALLY generated value for repeat registers */
      if(strncmp (inprec,REPV_KEYWORD,KEY_LEN) == 0 )
      {
	 parse_keyword_line(inprec, parsed_data, REPV_KEYWORD,0);
	 if(parsed_data[0] != NULL)
	    asic_reg->rept_value = (int)strtol(parsed_data[0],0,10);
      }

      if(strncmp (inprec,DISP_LENGTH_KEYWORD,KEY_LEN) == 0 )
      {
	 parse_keyword_line(inprec, parsed_data, DISP_LENGTH_KEYWORD,0);
	 if(parsed_data[0] != NULL)
	    asic_reg->disp_length = (int)strtol(parsed_data[0],0,16);
	 if ( asic_reg->disp_length > 16)
	    error(stderr,"Display length too long");
	 if ( asic_reg->disp_length < 1)
	    error(stderr,"Display length too short");
      }

      if(strncmp (inprec,DISP_BASE_KEYWORD,KEY_LEN) == 0 )
      {
	 parse_keyword_line(inprec, parsed_data, DISP_BASE_KEYWORD,0);
	 if(parsed_data[0] != NULL)
	    asic_reg->disp_base = toupper(parsed_data[0][0]);
	 if (( asic_reg->disp_base != 'H' ) && ( asic_reg->disp_base != 'D' ) && 
	     ( asic_reg->disp_base != 'O' ) && ( asic_reg->disp_base != 'B' )) 
	    error(stderr,"Display base invalid");
      }


      if(strncmp (inprec,MASK_KEYWORD,KEY_LEN) == 0 )
      {
	 parse_keyword_line(inprec, parsed_data, MASK_KEYWORD,0);

	 if(parsed_data[0] != NULL)
	 {
	    if((strncmp(parsed_data[0],"0x",2) != 0) && (parsed_data[0][0] != (char)NULL))
	       error(stderr,"MASK offset number not in hex format (0xabcd)");
	    else
	    {
	       if(parsed_data[0][0] != (char)NULL)
		  asic_reg->masked_by_offset = (int)strtol(parsed_data[0],0,16);
	       else
	       {      
		  asic_reg->masked_by_offset = 0;
		  fprintf(stderr,"+++++++++----->>>>>Warning: mask offset not specified: %s", asic_reg->reg_name);
		  mask_warning++;
	       }
	       if(parsed_data[1] != NULL)
	       {
		  if(strncmp("SELF",parsed_data[1],4) == 0)
		     asic_reg->masked_by_block = block_ptr;
		  else
		     asic_reg->masked_by_block = find_block(parsed_data[1]);
		  asic_reg->masked_by_offset += asic_reg->masked_by_block->block_offset;
	       }
	       else
	       {
		  asic_reg->masked_by_block = top.block_head;;
		  fprintf(stderr,"+++++++++----->>>>>Warning: mask offset does not include block: %s", asic_reg->reg_name);
		  mask_warning++;
	       }
	    }
	 }
      }
      if(strncmp (inprec,SAME_KEYWORD,KEY_LEN) == 0 )
      {
	 parse_keyword_line(inprec, parsed_data, SAME_KEYWORD,0);

	 /* need to parse out the block for SAMEAS */
	 if(strncmp(parsed_data[0],"0x",2) != 0)
	    error(stderr,"SAMEAS offset number not in hex format (0xabcd)");

	 if(parsed_data[1] != NULL)  /* find the block where the source register lives */
	    sameas_block_ptr = find_block(parsed_data[1]);

	 /* now search the block for the source reg */
	 sameas = sameas_block_ptr->head_reg;
	 if(sameas == NULL)
	 {
	    sprintf(err_str,"Could not find SAMEAS for 0x%x",asic_reg->reg_offset);
	    error(stderr,err_str);
	 }
	 while(sameas->reg_offset != strtol(parsed_data[0],0,16)) 
         /* now match up the offset in the block */
	 {
	    if(sameas->next_reg == NULL)
	    {
	       sprintf(err_str,"Could not find SAMEAS %s for 0x%x",parsed_data[0],
		       asic_reg->reg_offset);
	       error(stderr,err_str);
	    }
	    else
	       sameas = sameas->next_reg;
	 }
	 /* now copy over information from the source register */
	 asic_reg->top_hini = sameas->top_hini;
	 asic_reg->reg_read_write = sameas->reg_read_write;
	 asic_reg->reg_mask = sameas->reg_mask;

	 if(asic_reg->reg_read_write & INTERRUPT_ACCESS)
	 {
	    asic_reg->masked_by_offset = sameas->masked_by_offset + (asic_reg->reg_offset - sameas->reg_offset);
	    asic_reg->masked_by_block = sameas->masked_by_block;
	 }
	 asic_reg->head_nib = sameas->head_nib;
	 asic_reg->head_com = sameas->head_com;
	 /* copy print flag over.  preserve all skips, but turn of nibble printing */
	 asic_reg->print_flag = sameas->print_flag & 0xfe;  /* fix me */
	 if(asic_reg->print_flag & PRINT_NIBS_IN_SAMEAS)
	    asic_reg->print_flag |= PRINT_NIBS;  
      }
      /* if the prnt keyword is found then the nibble descriptions for a
         sameas register will be printed, otherwise they will not */
      if(strncmp (inprec,PRNT_KEYWORD,KEY_LEN) == 0 )
      {
	 parse_keyword_line(inprec, parsed_data, PRNT_KEYWORD,0);
	 if(parsed_data[0] == NULL)
	    asic_reg->print_flag = PRINT_NIBS_IN_SAMEAS;
	 else
	 {
	    entry = 0;
	    do
	    {
	       if(strncmp(parsed_data[entry], "SKIP_HIT",7) == 0)
		  asic_reg->print_flag |= SKIP_HIT;
	       else if(strncmp(parsed_data[entry], "SKIP_VLOG",7) == 0)
		  asic_reg->print_flag |= SKIP_VLOG;
	       else if(strncmp(parsed_data[entry], "SKIP_ILEAF",7) == 0)
		  asic_reg->print_flag |= SKIP_ILEAF;
	       else if(strncmp(parsed_data[entry], "SKIP_NIBS",6) == 0)
		  asic_reg->print_flag = 0;
	       else error(stderr,"Unknown entry on :prnt: line ");
	       entry++;
	    }while (parsed_data[entry] != NULL);
	 }

	 if((asic_reg->print_flag & SKIP_HIT) && ((mode == HIT) || (mode == NEW_HIT)) )
	    fprintf(stderr,"Ignoring REPT since registers, will be skipped anyway\n");

      }

      if(strncmp (inprec,GSKP_KEYWORD,KEY_LEN) == 0 )
      {
	 if(repeat_flag != 1)
	    error(stderr,"Cannot have GSKP without a REPT.");
	 fprintf(stderr,"--------------------->GSKP encountered\n");
	 asic_reg->print_flag |= SKIP_ILEAF;
	 asic_reg->reg_range = asic_reg->reg_offset + ( rept_info.addr_increment *
			(abs(rept_info.start_index-rept_info.end_index)/rept_info.increment ));
	 asic_reg->end_index = rept_info.end_index;
      }


      if(strncmp (inprec,CMNT_KEYWORD,KEY_LEN) == 0 )
      {
	 save_line = 1;
	 state = COMMENT_STATE;
      }
      if(strncmp (inprec,REPT_KEYWORD,KEY_LEN) == 0 )
      {
	 if((asic_reg->print_flag & SKIP_HIT) && ((mode == HIT) || (mode == NEW_HIT)) )
	    fprintf(stderr,"Ignoring REPT since registers, will be skipped anyway\n");
	 else
	 {
	    repeat_flag = 1;
	    rept_info.addr_increment = 2; 
	    rept_info.skip_interval = -1;

	    parse_keyword_line(inprec, parsed_data, REPT_KEYWORD,0);

	    if(parsed_data[0] == NULL)
	       error(stderr, "Badly formed REPT line, no start index\n");
	    else
	       rept_info.start_index = (int)strtol(parsed_data[0],0,0);
	    asic_reg->rept_value = rept_info.start_index;
	    if(parsed_data[1] == NULL)
	       error(stderr, "Badly formed REPT line, no end index\n");
	    else
	       rept_info.end_index = (int)strtol(parsed_data[1],0,0);

	    if(parsed_data[2] != NULL) 
	       if((strncmp(parsed_data[2],"ALPHA",5) !=0) && (strncmp(parsed_data[2],"HEX",3) !=0))
		  rept_info.increment = (int)strtol(parsed_data[2],0,0);
	       else rept_info.increment = 1;
	    else rept_info.increment = 1;

	    if(rept_info.increment == 0)
	       error(stderr,"Repeat increment is zero.");
	    if((rept_info.increment > 0) && (rept_info.start_index > rept_info.end_index) ||
	       (rept_info.increment < 0) && (rept_info.start_index < rept_info.end_index))
	       error(stderr,"Repeat indices will diverge with this given increment.");
	    if((rept_info.start_index-rept_info.end_index)%rept_info.increment != 0)
	       error(stderr,"End index will not be hit given the start index and the increment.");
	    
	    if(parsed_data[3] != NULL)
	       if((strncmp(parsed_data[3],"ALPHA",5) !=0) && (strncmp(parsed_data[3],"HEX",3) !=0))
		  rept_info.addr_increment = (int)strtol(parsed_data[3],0,16);
	    if(parsed_data[4] != NULL)
	       if((strncmp(parsed_data[4],"ALPHA",5) !=0) && (strncmp(parsed_data[4],"HEX",3) !=0))
		  rept_info.skip_interval = (int)strtol(parsed_data[4],0,16);
	    
	    rept_info.block_ptr = block_ptr;

	    for(field = 2; field < MAXFIELDS; field++)
	       if(parsed_data [field] != NULL)
	       {
		  if(strncmp(parsed_data[field],"ALPHA",5) == 0)
		     asic_reg->format = ALPHA;
		  if(strncmp(parsed_data[field],"HEX",3) == 0)
		     asic_reg->format = HEX;
	       }
	    /* if #r# repeat keyword, set value in register*/
            if(strstr(asic_reg->reg_name, "#r#"))
	       asic_reg->rept_value = rept_info.start_index;
	    else
	    {
	       rept_warning++;
	       fprintf(stderr,"Please update your :rept: registers to use #r# syntax.\n");
	       strcat(asic_reg->reg_name,"#r#");
	    }
	 }
      } 
     
      if(save_line == 0)
	 get_line(inprec, &fetch, &state, mode);	
   }
   if((mode == HIT) || (mode == NEW_HIT))
      fclose(fo_hit_log);
} /* end of FILE reached */

  

/*****************************************************************************
** open the files with the appropriate names for the MODE we are in
*****************************************************************************/
void open_files(FILE **fo1, uchr text_out_method, char *arg)
{
   char out_file[80];

   fprintf(stderr,"\nOutput file(s): ");
   switch(text_out_method)
   {
      case PROBE: 
	 probe_open_files(arg);
	 break;    
      case NEW_HIT:
	 new_hit_open_files();
	 break;    
      case INTERLEAFP: 
      case INTERLEAF: 
	 sprintf(out_file,"%s.tbl",arg);
	 fprintf(stderr,"%s\n",out_file);
	 if((*fo1 = fopen(out_file,"w")) == NULL)
	 {    	   perror(out_file);     exit(-1);  };
	 i_stub_copy(*fo1);
	 break;
      case TEXT:
	 break;
      case TEXT_FILE:	
	 sprintf(out_file,"%s.txt",arg);
	 fprintf(stderr,"%s\n",out_file);
	   if((*fo1 = fopen(out_file,"w")) == NULL)
	   {   perror(out_file);     exit(-1);  };
	 break;
      case VLOG:	
	 verilog_open_files();
	 break;
     default: 
     {   perror(out_file);     exit(-1);  };
     break;
   }

}
/****************************************************************************/
void init_asic_struct()
{
   top.block_head = NULL;
   top.reg_width = -1;
   top.unused = '-';
}

/*****************************************************************************/
/* this has poorly? evolved into a many pass output routine, but somethings
   had to come after others, and I suppose you could do it in one pass into
   many files, but... oh well.
   */
void data_out(uchr mode, char *argv1)
{
   FILE *fo1, *farb;
   struct reg_struct *reg_ptr;
   struct block_struct *block_ptr;

   /* now open the output files.. */
   if((mode != TEXT))
      open_files(&fo1, mode, argv1);

   block_ptr = top.block_head;

   switch(mode)  /* Preface to Pass 1 */
   {
      case INTERLEAFP:
      case INTERLEAF:   ileaf_pre1(fo1);	 break;
      case VLOG:	 verilog_clear_count();	 break;
      case TEXT:
      case TEXT_FILE:	 break;
      case NEW_HIT:	 break;
      default:	    break;     
   }
   
   top.highest_offset = 0x00000000;

   while(block_ptr != NULL)
   {
      if(block_ptr->num_block_regs > 64)
      {
	 fprintf(stderr,"More than 64 regsiters in block %s.  This will cause problems in HIT\n",block_ptr->block_name);
	 block_length_warning++;
      }
      reg_ptr = block_ptr->head_reg;
      while(reg_ptr != NULL)
      {
	 /* print out with absolute addr */
	 reg_ptr->reg_offset += block_ptr->block_offset;
	 if(reg_ptr->reg_offset > top.highest_offset)
	    top.highest_offset = reg_ptr->reg_offset;
	 if(reg_ptr->reg_range != 0)
	    reg_ptr->reg_range += block_ptr->block_offset;
	 if(mode == TEXT)
	    print_out_text_data_from_structure(stderr, reg_ptr, 0, block_ptr );
	 if(mode == TEXT_FILE)
	    print_out_text_data_from_structure(fo1, reg_ptr, 0, block_ptr );
	 reg_ptr = reg_ptr->next_reg;
      }
      switch(mode)     {
	 case INTERLEAFP:
	 case INTERLEAF: ileaf_pass1(block_ptr, mode, fo1);   break;
	 default:	    break;     }
      block_ptr = block_ptr->next_block;
   }
   switch(mode)     {
      case INTERLEAFP:
      case INTERLEAF:	 ileaf_pre2(fo1, argv1);  break;
      case VLOG:	 
	 verilog_vc();
	 verilog_vt();
	 break;
      case PROBE:	 
	 probe();
	 break;
      case NEW_HIT:
	do_new_hi_output_files();
	 break;
      default:	    break;  
   }
   if(mode == NEW_HIT)
      new_hit_close_files();
   else if(mode == VLOG)
      verilog_close_files();
   else if((mode != TEXT))
   {
      fclose(fo1);
   }
}
/*****************************************************************************
** sort asic data
** do a single linked-list sort
** sort block data then registers under each block using offsets
** as the primary sort criterion, and the order in the rdf file as the
** tiebreaking criterion
*****************************************************************************/
void sort_asic_data()
{
   struct reg_struct *reg_ptr;
   struct block_struct *block_ptr, *block_top;

   block_top = top.block_head;
   block_ptr = block_top;
   /* incomplete */
}
/*****************************************************************************
** main
*****************************************************************************/
int main(int argc, char *argv[])
{
   FILE *fi;
   uchr inp_file[64];
   uchr last_nib;

   non_unique_warning = 0;
   bitfield_warning = 0;
   mask_warning = 0;
   offset_warning = 0;
   empty_bit_warning = 0;
   warning = 0;
   block_warning = 0;
   rept_warning = 0;
   hini_warning = 0;
   errors = 0;
   block_length_warning = 0;
   label[0] = '\0';
   block_copy_flag = 0;
   top_file.parent = NULL;
   current_file = &top_file;

   /* check command line arguments */
   if ((argc < 2))
   {
      fprintf(stderr,"\nVersion: %s",version);
      fprintf(stderr,"\nUsage: %s <input_file> [-aiIptv] [-nS] [-h] [-l LABEL]",argv[0]);
      fprintf(stderr,"\nNote that files with a suffix of .rdf may be colorised in Xemacs.");
      fprintf(stderr,"\n       -a is for HIT output");
      fprintf(stderr,"\n       -i is for Interleaf table output");
      fprintf(stderr,"\n       -I is for Interleaf table output with page breaks on block boundaries");
      fprintf(stderr,"\n       -p is for pROBE default value output");
      fprintf(stderr,"\n       -t is for text file output");
      fprintf(stderr,"\n       -v is for Verilog code output(under development)");
      fprintf(stderr,"\n       default is just text to screen");
      fprintf(stderr,"\n  -nS will prevent blocks and registers from being sorted by offset");
      fprintf(stderr,"\n  -l LABEL will output defaults associated with LABEL\n\n");
      exit(-1);
   }
   text_out = TEXT;
   sprintf(inp_file,"%s",argv[1]);
   if((current_file->fi = fopen(inp_file,"r")) == NULL)
   {   perror(inp_file);     exit(-1);  };
   

   strcpy(filename, inp_file);
   if(argc > 2)
   {
      switch(argv[2][1])
      {
         case 'I': text_out = INTERLEAFP;
	    break;
         case 'i': text_out = INTERLEAF;
	    break;
         case 'c': 
	 case 'C': error(stderr,"This option is disabled\n");
	    break;
         case 'a': text_out = NEW_HIT;
	    break; 
	 case 'p': text_out = PROBE;
	    break; 
	 case 'z': text_out = NEW_HIT;
	    lif_flag = 1;
	    break; 
         case 't': text_out = TEXT_FILE;
	    break;
         case 'v': text_out = VLOG;
	    break;
         case 'n': 
	    if(argv[2][2] == 'S')
	       sort_data = 0;
	    fprintf(stderr,"\nNo sorting will be done \n");
	    break;
	 case 'h':
	    error(stderr,"This option is now part of the :rept: line format\n");
	    break;
         default:  break;
      }
      if(argc > 3)
	 switch(argv[3][1])
	 { 
	    case 'n':
	       if(argv[3][2] == 'S')
	       {
		  sort_data = 0;
		  fprintf(stderr,"\nNo sorting will be done \n");
	       }
	    case 'h':
	       error(stderr,"This option is now part of the :rept: line format\n");
	       break;
	    case 'l':
	       if(argv[4] == NULL)
		  error(stderr,"-l used, but No LABEL specified\n");
	       strcpy(label,argv[4]);
	       break;
	    default:  break;
	 }
   }
   init_asic_struct();
   read_files_into_data_structure(text_out);  /* read the input file into the structure */
   replace_instance_placeholders_and_ini();
   data_out(text_out, argv[1]);
   print_results();

}
