/* $Id$
 * Interpreter library, internal API.
 *
 * Copyright (C) 2008-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 */

#ifndef __FAUHDLI_PRIVATE_H_INCLUDED
#define __FAUHDLI_PRIVATE_H_INCLUDED

#include "fauhdli.h"
#include <stdbool.h>
#include <stdlib.h> 	/* for size_t */
#include "list.h"
#include "slset.h"
#include "vhdl_sched.h"
#include "basetypes.h"

/* ********************* types  *********************** */

enum opcode_kind {
	OPCODE_MOV, 	/* 0 */
	OPCODE_ADD,
	OPCODE_SUB,
	OPCODE_IMUL,
	OPCODE_DIV,
	OPCODE_CALL,	/* 5 */
	OPCODE_PROC,
	OPCODE_RETURN,
	OPCODE_BEGINTRANS,
	OPCODE_ENDTRANS,
	OPCODE_SETPARAM, /* 10 */
	OPCODE_GETPARAM,
	OPCODE_UPDATE,
	OPCODE_GETSIG,
	OPCODE_JMP,
	OPCODE_JE,	/* 15 */
	OPCODE_JB,
	OPCODE_JNE,
	OPCODE_JBE,
	OPCODE_AOFFSET,
	OPCODE_ROFFSET,	/* 20 */
	OPCODE_LABEL,
	OPCODE_SUSPEND,
	OPCODE_CONNECT,
	OPCODE_WAKEAT,
	OPCODE_WAKEON,	/* 25 */
	OPCODE_LOG,
	OPCODE_ABORT,
	OPCODE_GETSIMTIME
};

/** kind of operand */
enum operand_kind {
	/** register operand */
	OPERAND_REGISTER,
	/** indirect memory access via register */
	OPERAND_INDIRECT,
	/** immediate operand */
	OPERAND_IMMEDIATE,
	/** reference to a data object. (= pointer to data) */
	OPERAND_REFERENCE,
	/** reference to a label */
	OPERAND_TARGET
};

/** basic type enumeration */
enum type_kind {
	/** integral type */
	TYPE_INT,
	/** float type */
	TYPE_FLOAT,
	/** pointer type */
	TYPE_POINTER,
};

/** storage kinds of data elements */
enum storage_kind {
	/** plain storage, only the value is stored. */
	STORAGE_VARIABLE,
	/** signal storage, defining a signal */
	STORAGE_SIGNAL,
	/** driver storage, a driver to a signal */
	STORAGE_DRIVER
};

/** code container */
struct code_container {
	/** name of the container */
	char *name;
	/** list of types */
	struct slist *type_definitions;
	/** architecture segment */
	struct slist *transfer_segment;
	/** stack segment */
	struct slist *stack_segment;
	/** nested code containers */
	struct slist *sub_containers;
	/** data size of stack segment */
	size_t stack_size;
	/** data size of transfer segment */
	size_t transfer_size;
	/** text segment */
	struct slist *text_segment;
	/** nesting level of container */
	unsigned int nesting_level;
	/** number of used virtual registers by code_container */
	unsigned int num_vregs;
};

/** one intermediate code operand */
struct operand {
	/** kind of operand */
	enum operand_kind kind;
	/** type of operand */
	enum type_kind type;
	/** union by kind of operand */
	union {
		/** register for register and indirect operands */
		unsigned int reg;
		/** immediate value */
		union fauhdli_value immediate;
		/** reference to a label */
		struct {
			/** name of the label */
			char *name;
			/** resolved reference or NULL if unresolved */
			const struct slist_entry *ref;
		} target;
		/** reference to a data object, or a code container */
		struct {
			/** name of the reference */
			char *name;
			/** resolved reference or NULL if unresolved */
			const struct data_definition *ref;
			/** for setparam, begintrans, endtrans: 
			 *  reference to the code container 
			 */
			const struct code_container *container;
		} data;
	} bytype;
};

struct opcode {
	/** kind of opcode */
	enum opcode_kind kind;
	/** first operand (if any) */
	struct operand *op1;
	/** second operand (if any) */
	struct operand *op2;
	/** third operand (if any) */
	struct operand *op3;
	/** label, in case it's no opcode but a label. */
	char *label;
	/** type element referring to the indexed type in caes of 
	 *  aoffset/roffset opcodes */
	struct type_element *indexed_type;
	/** line number in parsed file */
	int lineno;
	/** list of annotations (optional) */
	struct slist *annotations;
};

/** generic annotation specification */
struct annotation_spec {
	/** name of the annotation specification */
	char *name;
	/** string value */
	char *string_value;
	/** int value */
	int int_value;
};

/** Element of a type.
 *  
 *  A type_element represents one element of a possibly composite type.
 *  It can represent an array of either a basic type or another composite
 *  type.
 *  A basic type would be universal_integer or universal_real, or a driver
 *  of it, or a signal of it.
 *  
 *  elem_count represents the number of basic types that this type_element
 *  refers to.
 *  offset means the number of basic type elements in a composite type 
 *  that reside before this type.
 *
 *  Example:
 *  type t is { a: u[10], b: v }
 *  type u is { c: universal_integer[5] }
 *  type v is { d: universal_real, e: universal_integer }
 *
 *  type_element d has offset 0 and elem_count 1
 *  type_element e has offset 1 and elem_count 1
 *  type v has elem_count 2
 *
 *  type_element c has offset 0 and elem_count 5
 *  type u has elem_count 5
 *
 *  type_elment a has offset 0 and elem_count 10 * 5 = 50 (and elements = 10)
 *  type_element b has offset 50 and elem_count 2 (and elements = 1)
 *  type t has elem_count 52
 */
struct type_element {
	/** name of the referred to type */
	char *name;
	/** referred to type */
	const struct type_declaration *type;
	/** number of elements of an array */
	universal_integer elements;
	/** list of initial values */
	struct slist *initial_list;
	/** number of basic subelements. -1 means unset. */
	int elem_count;
	/** element offset from beginning of type (not a size!) */
	int offset;
	/** annotations (optional) */
	struct slist *annotations;
};

struct type_declaration {
	/** name of the type */
	char *name;
	/** subelements of the type */
	struct slist *elements;
	/** number of basic subelements. -1 means unset. */
	int elem_count;
};

enum segment_kind {
	SEGMENT_TRANSFER,
	SEGMENT_STACK
};

struct data_definition {
	/** name of the definition */
	char *name;
	/** type of the definition (local type_element) */
	struct type_element *type;
	/** storage kind of the data definition */
	enum storage_kind storage;
	/** location of the data. */
	enum segment_kind loc;
	/** nesting level, 0=current stack seg., 1=parent stack seg..) */
	unsigned int nesting_level;
	/** offset of data in segment (bytes) */
	size_t offset;
	/** storage size in byte of the resulting data */
	size_t storage_size;
	/** annotations (optional) */
	struct slist *annotations;
	/** reference to a resolution function */
	struct {
		/** name of resolution function */
		char *name;
		/** container of the resolution function */
		const struct code_container *container;

	} resolver;
};

/** value of a virtual register */
struct reg_value {
	/** actual value */
	union fauhdli_value value;
	/** type of register (mainly useful for debugging */
	enum type_kind kind;
	/** was the register written to already? 
	 *  (only useful to debug intermediate code) */
	bool initialized;
};

/** foreign mode for port/generic SetParams. */
enum foreign_mode_e {
	/** component instantiation mode */
	FOREIGN_MODE_COMPONENT,
	/** architecture create mode */
	FOREIGN_MODE_ARCHITECTURE
};

/** main instance of vhdl interpreter */
struct fauhdli {
	/** top code container. */
	struct code_container *container;
	/** set containing various allocated pointers that should get free'd
	 *  on exit. (order must not be relevant!)
	 */
	struct slset *cleanup_ptrs;

	/** scheduler instance */
	struct vhdl_sched *scheduler;

	/** set of created signals */
	struct slset *signals;
	/** set of created, but not free'd stack_frames */
	struct slset *pending_stack_frames;
	/** all vhdl processes */
	struct slset *processes;
	/** current simulation time */
	universal_integer sim_time;
	/** tracer instance (NULL if no tracing is desired) */
	struct trace_t *tracer;
	/** debugging enabled/disabled? */
	bool debug;
	/** opaque glue_vhdl pointer. */
	void *glue_vhdl;
	/** set containing all (non-foreign) drivers */
	struct slset *drivers;
	/** set of all "foreign" drivers. A foreign driver means that the
	 *  signal of a process is written to from VHDL but the signal itself
	 *  is foreign. */
	struct slset *foreign_drivers;

	/** helper structure for setting foreign generics of arrays */
	struct {
		/** component id */
		unsigned int comp_id;
		/** pointer to base (NULL means not yet set) */
		union fauhdli_value base;
		/** name of generic (formal name) */
		const char *formal_name;
		/** element type name */
		const char *element_type;
		/** left bound */
		universal_integer left;
		/** left bound set */
		bool left_set;
		/** right bound */
		universal_integer right;
		/** right bound set */
		bool right_set;
		/** direction */
		universal_integer direction;
		/** direction set? */
		bool direction_set;
	} foreign_g_array_params;

	/** mode for foreign generics/ports */
	enum foreign_mode_e foreign_mode;

	/** callbacks for foreign interface. */
	struct glue_vhdl_cb callbacks;

	/** wakeup event already scheduled? */
	bool wakeup_scheduled;

	/** list of entities to trace */
	struct slist *trace_list;
};

/* ********************* functions *********************** */

/** allocate memory for an opcode an initialize the members.
 *  @param kind kind of opcode.
 *  @param callbacks for malloc
 *  @return opcode with kind set, and pointers set to 0.
 */
extern 
__attribute__((__malloc__))
struct opcode *
fauhdli_alloc_opcode(
	enum opcode_kind kind,
	const struct glue_vhdl_cb *callbacks
);

/** find the annotation with name in the list of annotations.
 *  @param name name tag of the annotation
 *  @param annotations list of annotations
 *  @return the spec in question or NULL if not found.
 */
extern
const struct annotation_spec *
fauhdli_find_annotation(const char *name, const struct slist *annotations);

#endif /* __FAUHDLI_PRIVATE_H_INCLUDED */
