/*  This file is part of the program psim.

    Copyright (C) 1994-1997, Andrew Cagney <cagney@highland.com.au>

    This program 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 2 of the License, or
    (at your option) any later version.

    This program 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 this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
    */



#include "misc.h"
#include "lf.h"
#include "table.h"
#include "filter.h"
#include "igen.h"

#include "ld-insn.h"
#include "ld-decode.h"

#include "gen.h"

#include "gen-semantics.h"
#include "gen-icache.h"
#include "gen-idecode.h"


static void
print_semantic_function_header (lf *file,
				const char *basename,
				const char *format_name,
				opcode_bits *expanded_bits,
				int is_function_definition,
				int nr_prefetched_words)
{
  int indent;
  lf_printf(file, "\n");
  lf_print__function_type_function (file, print_semantic_function_type,
				    "EXTERN_SEMANTICS",
				    (is_function_definition ? "\n" : " "));
  indent = print_function_name (file,
				basename,
				format_name,
				NULL,
				expanded_bits,
				function_name_prefix_semantics);
  if (is_function_definition)
    {
      indent += lf_printf (file, " ");
      lf_indent (file, +indent);
    }
  else
    {
      lf_printf (file, "\n");
    }
  lf_printf (file, "(");
  lf_indent (file, +1);
  print_semantic_function_formal (file, nr_prefetched_words);
  lf_indent (file, -1);
  lf_printf (file, ")");
  if (is_function_definition)
    {
      lf_indent (file, -indent);
    }
  else
    {
      lf_printf (file, ";");
    }
  lf_printf (file, "\n");
}

void
print_semantic_declaration (lf *file,
			    insn_entry *insn,
			    opcode_bits *expanded_bits,
			    insn_opcodes *opcodes,
			    int nr_prefetched_words)
{
  print_semantic_function_header (file,
				  insn->name,
				  insn->format_name,
				  expanded_bits,
				  0/* is not function definition*/,
				  nr_prefetched_words);
}



/* generate the semantics.c file */


void
print_idecode_invalid (lf *file,
		       const char *result,
		       invalid_type type)
{
  const char *name;
  switch (type)
    {
    default: name = "unknown"; break;
    case invalid_illegal: name = "illegal"; break;
    case invalid_fp_unavailable: name = "fp_unavailable"; break;
    case invalid_wrong_slot: name = "wrong_slot"; break;
    }
  if (options.gen.code == generate_jumps)
    {
      lf_printf (file, "goto %s_%s;\n",
		 (options.gen.icache ? "icache" : "semantic"),
		 name);
    }
  else if (options.gen.icache)
    {
      lf_printf (file, "%s %sicache_%s (", result, options.prefix.global.name, name);
      print_icache_function_actual (file, 0);
      lf_printf (file, ");\n");
    }
  else
    {
      lf_printf (file, "%s %ssemantic_%s (", result, options.prefix.global.name, name);
      print_semantic_function_actual (file, 0);
      lf_printf (file, ");\n");
    }
}


void
print_semantic_body (lf *file,
		     insn_entry *instruction,
		     opcode_bits *expanded_bits,
		     insn_opcodes *opcodes)
{
  /* validate the instruction, if a cache this has already been done */
  if (!options.gen.icache)
    {
      print_idecode_validate (file, instruction, opcodes);
    }
  
  print_itrace (file, instruction, 0/*put_value_in_cache*/);
  
  /* generate the instruction profile call - this is delayed until
     after the instruction has been verified.  The count macro
     generated is prefixed by ITABLE_PREFIX */
  {
    lf_printf (file, "\n");
    lf_indent_suppress (file);
    lf_printf (file, "#if defined (%sPROFILE_COUNT_INSN)\n",
	       options.prefix.itable.uname);
    lf_printf (file, "%sPROFILE_COUNT_INSN (CPU, CIA, MY_INDEX);\n",
	       options.prefix.itable.uname);
    lf_indent_suppress (file);
    lf_printf (file, "#endif\n");
  }

  /* generate the model call - this is delayed until after the
     instruction has been verified */
  {
    lf_printf (file, "\n");
    lf_indent_suppress (file);
    lf_printf (file, "#if defined (WITH_MON)\n");
    lf_printf (file, "/* monitoring: */\n");
    lf_printf (file, "if (WITH_MON & MONITOR_INSTRUCTION_ISSUE)\n");
    lf_printf (file, "  mon_issue (");
    print_function_name (file,
			 instruction->name,
			 instruction->format_name,
			 NULL,
			 NULL,
			 function_name_prefix_itable);
    lf_printf (file, ", cpu, cia);\n");
    lf_indent_suppress (file);
    lf_printf (file, "#endif\n");
    lf_printf (file, "\n");
  }
  
  /* determine the new instruction address */
  {
    lf_printf(file, "/* keep the next instruction address handy */\n");
    if (options.gen.nia == nia_is_invalid)
      {
	lf_printf(file, "nia = %sINVALID_INSTRUCTION_ADDRESS;\n",
		  options.prefix.global.uname);
      }
    else
      {
	int nr_immeds = instruction->nr_words - 1;
	if (options.gen.delayed_branch)
	  {
	    if (nr_immeds > 0)
	      {
		lf_printf (file, "cia.dp += %d * %d; %s\n",
			   options.insn_bit_size / 8, nr_immeds,
			   "/* skip dp immeds */");
	      }
	    lf_printf (file, "nia.ip = cia.dp; %s\n",
		       "/* instruction pointer */");
	    lf_printf (file, "nia.dp = cia.dp + %d; %s\n",
		       options.insn_bit_size / 8,
		       "/* delayed-slot pointer */");
	  }
	else
	  {
	    if (nr_immeds > 0)
	      {
		lf_printf (file, "nia = cia + %d * (%d + 1); %s\n",
			   options.insn_bit_size / 8, nr_immeds,
			   "/* skip immeds as well */");
		
	      }
	    else
	      {
		lf_printf (file, "nia = cia + %d;\n",
			   options.insn_bit_size / 8);
	      }
	  }
      }
  }
  
  /* if conditional, generate code to verify that the instruction
     should be issued */
  if (filter_is_member (instruction->options, "c")
      || options.gen.conditional_issue)
    {
      lf_printf (file, "\n");
      lf_printf (file, "/* execute only if conditional passes */\n");
      lf_printf (file, "if (IS_CONDITION_OK)\n");
      lf_printf (file, "  {\n");
      lf_indent (file, +4);
      /* FIXME - need to log a conditional failure */
    }
  
  /* Architecture expects a REG to be zero.  Instead of having to
     check every read to see if it is refering to that REG just zap it
     at the start of every instruction */
  if (options.gen.zero_reg)
    {
      lf_printf (file, "\n");
      lf_printf (file, "/* Architecture expects REG to be zero */\n");
      lf_printf (file, "GPR_SET(%d, 0);\n", options.gen.zero_reg_nr);
    }
  
  /* generate the code (or at least something */
  lf_printf (file, "\n");
  lf_printf (file, "/* semantics: */\n");
  if (instruction->code != NULL)
    {
      /* true code */
      lf_printf (file, "{\n");
      lf_indent (file, +2);
      lf_print__line_ref (file, instruction->code->line);
      table_print_code (file, instruction->code);
      lf_indent (file, -2);
      lf_printf (file, "}\n");
      lf_print__internal_ref (file);
    }
  else if (filter_is_member (instruction->options, "nop"))
    {
      lf_print__internal_ref (file);
    }
  else
    {
      const char *prefix = "sim_engine_abort (";
      int indent = strlen (prefix);
      /* abort so it is implemented now */
      lf_print__line_ref (file, instruction->line);
      lf_printf (file, "%sSD, CPU, cia, \\\n", prefix);
      lf_indent (file, +indent);
      lf_printf (file, "\"%s:%d:0x%%08lx:%%s unimplemented\\n\", \\\n",
		 filter_filename (instruction->line->file_name),
		 instruction->line->line_nr);
      lf_printf (file, "(long) CIA, \\\n");
      lf_printf (file, "%sitable[MY_INDEX].name);\n",
		 options.prefix.itable.name);
      lf_indent (file, -indent);
      lf_print__internal_ref (file);
    }
  
  /* Close off the conditional execution */
  if (filter_is_member (instruction->options, "c")
      || options.gen.conditional_issue)
    {
      lf_indent (file, -4);
      lf_printf (file, "  }\n");
    }  
}

static void
print_c_semantic (lf *file,
		  insn_entry *instruction,
		  opcode_bits *expanded_bits,
		  insn_opcodes *opcodes,
		  cache_entry *cache_rules,
		  int nr_prefetched_words)
{
  
  lf_printf (file, "{\n");
  lf_indent (file, +2);
  
  print_my_defines (file,
		    instruction->name,
		    instruction->format_name,
		    expanded_bits);
  lf_printf (file, "\n");
  print_icache_body (file,
		     instruction,
		     expanded_bits,
		     cache_rules,
		     (options.gen.direct_access
		      ? define_variables
		      : declare_variables),
		     (options.gen.icache
		      ? get_values_from_icache
		      : do_not_use_icache),
		     nr_prefetched_words);
  
  lf_printf (file, "%sinstruction_address nia;\n", options.prefix.global.name);
  print_semantic_body (file,
		       instruction,
		       expanded_bits,
		       opcodes);
  lf_printf (file, "return nia;\n");
  
  /* generate something to clean up any #defines created for the cache */
  if (options.gen.direct_access)
    {
      print_icache_body (file,
			 instruction,
			 expanded_bits,
			 cache_rules,
			 undef_variables,
			 (options.gen.icache
			  ? get_values_from_icache
			  : do_not_use_icache),
			 nr_prefetched_words);
    }
  
  lf_indent (file, -2);
  lf_printf (file, "}\n");
}

static void
print_c_semantic_function (lf *file,
			   insn_entry *instruction,
			   opcode_bits *expanded_bits,
			   insn_opcodes *opcodes,
			   cache_entry *cache_rules,
			   int nr_prefetched_words)
{
  /* build the semantic routine to execute the instruction */
  print_semantic_function_header (file,
				  instruction->name,
				  instruction->format_name,
				  expanded_bits,
				  1/*is-function-definition*/,
				  nr_prefetched_words);
  print_c_semantic (file,
		    instruction,
		    expanded_bits,
		    opcodes,
		    cache_rules,
		    nr_prefetched_words);
}

void
print_semantic_definition (lf *file,
			   insn_entry *insn,
			   opcode_bits *expanded_bits,
			   insn_opcodes *opcodes,
			   cache_entry *cache_rules,
			   int nr_prefetched_words)
{
  print_c_semantic_function (file,
			     insn,
			     expanded_bits,
			     opcodes,
			     cache_rules,
			     nr_prefetched_words);
}


