#ifndef _fastsim_h
#define _fastsim_h
/******************************************************************************
** FILE: fastsim.h -- Part of the FastSim simulation system.
** Interface to the fastsim runtime system.
*/

#include <sys/types.h>
#include <libelf.h>
#include <math.h>

#include <assert.h>

void xmain();		/* Main function defined in Facile */

/******************************************************************************
 * Value of simulator command line options.
 */

extern int verbose;
extern int debug_level;
extern char* execfile;
extern char* logfile;

/******************************************************************************
 * External functions called by FastSim that can be overridden.
 * Default versions of these functions are provided in libfs.a.
 */

void xexit();		/* called on simulator exit */
void xno_instruction();	/* may be called by Facile ?exec attribute */
void xno_default();	/* may be called by Facile switch statements */

/******************************************************************************
 * External functions called by FastSim that cannot be overridden.
 */

/* Interface to detect SIGSEGV and SIGBUS signals */
extern void* xmem_test_address;		/* address to test */
extern unsigned char xmem_test_flag;	/* set to true surrounding test */
extern unsigned char xmem_test_failed;	/* result of test; true if failed */
unsigned char xmem_load8_test(void *va);
unsigned short xmem_load16_test(void *va);
unsigned long xmem_load32_test(void *va);
unsigned long long xmem_load64_test(void *va);
/* There should probably be some corresponding store tests */

/* write message to a log file */
void xlog(int lvl, const char*, ...);

/* dump simulator statistics to the logfile */
extern size_t nstat;
void xstats();

/******************************************************************************
 * Other definitions used in the Facile/C interface.
 */

#define TARGET_VADDR_MASK 0x80000000	/* Or'ed with all target addresses to
					   generate translated target addr */

/* Types used by generated simulators */

typedef char* string_t;
typedef unsigned char cc_t;
typedef unsigned long token_stream_t;

typedef struct queue {
    unsigned char size;		/* max # of elements in allocated array */
    unsigned char length;	/* # of elemets in use (ie, queue length) */
    unsigned char begin;	/* index of first element in the queue */
    unsigned char end;		/* just past the last element in the queue */
    void *data;			/* dynamically allocated array */
} queue_t;

/* Allocate a new queue data array to fix queue after assignment */
void queue_realloc(queue_t*, unsigned long width);

/* Reallocate a queue data array when more space is needed */
void queue_grow(queue_t*, unsigned long width);

/* Global variables defined by the simulation framework */

extern Elf *elf;
extern caddr_t break_value;

/* Interface functions to query & manipulate simulation data */

token_stream_t start_pc();
unsigned long start_sp();

static inline token_stream_t token_stream(unsigned long addr) { return addr; }

static inline unsigned char stream_lt(token_stream_t s1, token_stream_t s2)
{ return s1 < s2; }

static inline unsigned char stream_le(token_stream_t s1, token_stream_t s2)
{ return s1 <= s2; }

static inline unsigned char stream_eq(token_stream_t s1, token_stream_t s2)
{ return s1 == s2; }

static inline unsigned char stream_ne(token_stream_t s1, token_stream_t s2)
{ return s1 != s2; }

static inline unsigned char stream_ge(token_stream_t s1, token_stream_t s2)
{ return s1 >= s2; }

static inline unsigned char stream_gt(token_stream_t s1, token_stream_t s2)
{ return s1 > s2; }

static inline token_stream_t stream_add(token_stream_t stream,
					unsigned long delta)
{ return stream + delta; }

static inline token_stream_t stream_sub(token_stream_t stream,
					unsigned long delta)
{ return stream - delta; }

unsigned long stream_bits32(token_stream_t,
			    unsigned long offset, unsigned long width,
			    unsigned long lo, unsigned long hi);

unsigned long long stream_bits64(token_stream_t,
				 unsigned long offset, unsigned long width,
				 unsigned long lo, unsigned long hi);

unsigned long sign_extend32(unsigned long src, unsigned long width);
unsigned long long sign_extend64(unsigned long long src, unsigned long width);

union Cast32 {
    float f;
    signed long s;
    unsigned long u;
};
extern union Cast32 cast32;

union Cast64 {
    double f;
    signed long long s;
    unsigned long long u;
};
extern union Cast64 cast64;

/* floating point sqrt */
float fsqrt32(float);
double fsqrt64(double);

/* operators on signed/unsigned long values that set condition codes */
ulong_t uadd32_cc(cc_t*, ulong_t, ulong_t);
long sadd32_cc(cc_t*, long, long);
ulong_t usub32_cc(cc_t*, ulong_t, ulong_t);
long ssub32_cc(cc_t*, long, long);
ulong_t uand32_cc(cc_t*, ulong_t, ulong_t);
ulong_t uor32_cc(cc_t*, ulong_t, ulong_t);
ulong_t uxor32_cc(cc_t*, ulong_t, ulong_t);

/* operators on signed/unsigned long long values that set condition codes */
u_longlong_t uadd64_cc(cc_t*, u_longlong_t, u_longlong_t);
longlong_t sadd64_cc(cc_t*, longlong_t, longlong_t);
u_longlong_t usub64_cc(cc_t*, u_longlong_t, u_longlong_t);
longlong_t ssub64_cc(cc_t*, longlong_t, longlong_t);
u_longlong_t uand64_cc(cc_t*, u_longlong_t, u_longlong_t);
u_longlong_t uor64_cc(cc_t*, u_longlong_t, u_longlong_t);
u_longlong_t uxor64_cc(cc_t*, u_longlong_t, u_longlong_t);

/* floating point operators that set condition codes */
float fsub32_cc(cc_t*, float, float);
double fsub64_cc(cc_t*, double, double);

extern void* dummybuf;

/******************************************************************************
 * Types, variables, and functions to support fast-forwarding.
 *
 * The primary function is fast_forward(), which looks up the given RTS data
 * in the memoization cache and tries to switch to the fast-forwarded version
 * of the simulator. Upon return, the RTS data in the "init" variable will be
 * set to the correct value for whatever point the simulation has reached and
 * slow simulation can be (re)started.
 */

typedef struct {
    size_t bytes;
    long buf[0x100];
} pack_data_t;

typedef struct {
    size_t bytes;
    void* buf;
} packed_queue_t;

/* RTS data should be packed into pack_data before calling
 * either fast_forward() or write_result(). */
extern pack_data_t pack_data;

/* make sure last word of pack_data.buf han no random data */
static inline size_t
fill_pack_data()
{
    size_t words, align;

    words = (pack_data.bytes+3) / 4;
    assert(words < 0x100);

    align = pack_data.bytes & 0x3;
    if(align) pack_data.buf[words-1] &= 0xffffffff << (8*(4 - align));

    return words*4;
}

/* Try to switch to fast-forwarded simulation */
void fast_forward();

/* Insure static versions of global vars have the same structure as their
 * dynamic versions. Called at start of recovery from memoization miss. */
void ff_copy_to_stat();

/*
 * The following structures are used to store
 * control data in the memoization cache */

#define INDEX_ACTION 0
#define DLL32_ACTION 0x7fff
#define DLL64_ACTION 0x7ffe
#define RESULT1_ACTION -1
#define RESULT8_ACTION -2
#define RESULTN_ACTION -3

typedef struct Action {
    ushort_t action;	/* >=0 for actions; <0 for results */
    ushort_t bytes;	/* bytes to previous action or result */
} action_t;

typedef struct Result8 {
    char action;	/* -1 for 1 bit results; otherwise -2 */
    uchar_t result;	/* up to 8 bits of result data */
    ushort_t bytes;	/* bytes to previous action or result */
    void *next;
} result_8_t;

typedef struct ResultN {
    char action;	/* should always be -3 */
    uchar_t words;	/* # of words of result data */
    ushort_t bytes;	/* bytes to previous action or result */
    struct ResultN *next;
} result_N_t;

#define ACTION_BYTES(act) (((action_t*)(act))->bytes & 0xfffc)
#define ACTION_FLAGS(act) (((action_t*)(act))->bytes & 0x0003)
#define IS_RESULT(xx) (*((signed char*)(xx)) < 0)

/* Fast-forwarded version of main (generated by fs) */
void ff_main(action_t*);

/*
 * Flags providing info about or control of the fast-forwarding process */

/* True if a fast-forwarding simulator was generated */
extern int ff_memoize;

/* True if transitioning back to slow-sim */
extern int ff_recover;

/*
 * Functions called by automatically generated code */

/* Allocate unitialized bytes in the memoization cache */
void *ff_alloc(size_t);

/* Allocate and initialize a new action structure */
void ff_write_action(ushort_t);

/* Allocate and initialize a record to verify the RTS data in pack_data */
void ff_write_result_N();

/* Returns a pointer to packed data written by ff_write_result_N */
void *ff_recover_result_N();

/* Allocate and initialize a record to verify up to 8 bits of data */
uchar_t ff_write_result_8(uchar_t);

/* Allocate and initialize a record to verify 1 bit of data */
int ff_write_result_1(int);

/* Miss processing */
void ff_miss_result_N(result_N_t*,size_t);
void ff_miss_result_8(result_8_t*,uchar_t);
void ff_miss_result_1(result_8_t*,int);

/* Search the linked list of possible successors
 * to find the given result identifier. */
static inline void*
ff_find_result_N(void *data)
{
    result_N_t *rr, *rr0 = (result_N_t*)data;
    size_t bytes = fill_pack_data();
    size_t words = bytes / 4;

#ifdef STATS
    extern u_longlong_t count_resultN_fast;
    ++count_resultN_fast;
#endif

    for(rr=rr0; rr; rr=(result_N_t*)rr->next) {
	long* data = (long*)(rr+1);
	assert(rr->action == RESULTN_ACTION);
	if(rr->words == words && !memcmp(data,pack_data.buf,bytes))
	    return data + words;
    }

    ff_miss_result_N(rr0,bytes);	/* longjmp return */
    assert(!"Should not be here!");
    return NULL;
}

/* Search the linked list of possible successors
 * to find the given result identifier. */
static inline void*
ff_find_result_8(void *data, uchar_t result)
{
    result_8_t *rr, *rr0 = (result_8_t*)data;

#ifdef STATS
    extern u_longlong_t count_result8_fast;
    ++count_result8_fast;
#endif

    for(rr=rr0; rr; rr=(result_8_t*)rr->next) {
	assert(rr->action == RESULT8_ACTION);
	if(rr->result == result) return rr+1;
    }

    ff_miss_result_8(rr0,result);	/* longjmp return */
    assert(!"Should not be here!");
    return NULL;
}

/* Same as above, but optimized for the case where there
 * are only 2 possible results (ie, IF or WHILE). */
static inline void*
ff_find_result_1(void *data, int result)
{
    result_8_t *rr0 = (result_8_t*)data;

#ifdef STATS
    extern u_longlong_t count_result1_fast;
    ++count_result1_fast;
#endif

    assert(rr0->action == RESULT1_ACTION);
    if((int)rr0->result == result) return rr0+1;

    if(rr0->next) return rr0->next;

    ff_miss_result_1(rr0,result);	/* jongjmp return */
    assert(!"Should not be here!");
    return NULL;
}

/* Process the INDEX action */
void* ff_index_action(action_t*);

/* Process the DLL action */
void* ff_dll_action(action_t*);

#ifdef STATS
extern u_longlong_t count_action_fast;
#define COUNT_ACTIONS ++count_action_fast;
#else
#define COUNT_ACTIONS
#endif

#endif