/* Copyright (c)1994-2000 Begemot Computer Associates. All rights reserved.
 * See the file COPYRIGHT for details of redistribution and use. */

/*
 * main interpreter loop
 */
# include "regi.h"
# include "proc.h"

RCSID("$Id: interp.c 481 2002-03-07 12:43:08Z hbb $")


/*
 * debugging support
 *
 * you can define one of the following flags to get some interesting
 * output, that real hardware doesn't give. These flags are compile-time
 * rather than run-time flags to minimize the imposed overhead if they are
 * switched off.
 *
 * PROFIL:
 *	if switched on p11 counts how many times each instruction is executed
 *	and writes the result to 'profile.p11' at exit. This file contains
 *	64k unsigned ints.
 * TRACE:
 *	ouput processer registers in ascii after each instruction.
 *	This and PTRACE is done only if the environment variable P11TRACE
 *	exists.
 * PCTRACE:
 *	ouput a pc-trace as a file of unsigned integers. TRACE and PCTRACE
 *	are mutually exclusive.
 * UTRACE:
 *	trace user code to console (can be switched on/off trough the 
 *	monitor)
 * CPROF:
 *	profile microcode execution times using a microsecond counter
 *	(only on SPARC platforms), as a backup solution gettimeofday
 *	may be used instead (with high calling overhead!)
 * COUNT:
 *	count instructions
 */
/* # define PROFIL */
/* # define TRACE */
/* # define PCTRACE */
/* # define UTRACE */
/* # define CPROF */
/* # define CMPROF */
/* # define GTOD */
# define COUNT

# ifdef COUNT
u_xquad_t cycle;
# endif

# ifdef PROFIL
u_int	profile[0x10000];
#  define Profil()	profile[proc.cmd]++;
#  define StartProfil()	start_profile();
# else
#  define Profil()
#  define StartProfil()
# endif

# if defined(TRACE) || defined(PCTRACE)
#  define Trace()	trace();
#  define OpenTrace()	opentrace();
# else
#  define Trace()
#  define OpenTrace()
# endif

# ifdef UTRACE
#  define UTrace() if(proc.curmode == 3) utrace();
# else
#  define UTrace()
# endif

# ifdef	CPROF
#  ifdef CMPROF
#   define	CPROF_TABSIZE	4
#  else  /* CMPROF */
#   define	CPROF_TABSIZE	1
#  endif /* CMPROF */
u_int cprof_instab[0200000][CPROF_TABSIZE];
u_int cprof_calls[0200000]; 
#  define StartCprof()	start_cprof();
#  ifdef GTOD
/*
 * gettimeofday as a cheap replacement on platforms, where a
 * microsecond counter is not simply accessable
 */
static struct timeval cprof_v0, cprof_v1;
#  define CPROF_START()	(void)gettimeofday(&cprof_v0, NULL)
#  define CPROF_STOP(C)	{						\
				u_int long	v;			\
				(void)gettimeofday(&cprof_v1, NULL);	\
				v = (cprof_v1.tv_sec - cprof_v0.tv_sec)	\
				  * 1000000				\
				  + (cprof_v1.tv_usec - cprof_v0.tv_usec);\
				cprof_calls[proc.cmd]++;		\
				cprof_instab[proc.cmd][C] += v;		\
			}
#  else  /* GTOD */
/*
 * low overhead microsecond profile on SPARC's
 */
# ifdef __GNUC__
/* Sch*** fixincludes */
# include "/usr/include/machine/clock.h"
# else  /* __GNUC__ */
# include <machine/clock.h>
# endif /* __GNUC__ */

static u_int *counterp;
static u_int *map_counter(void);
static u_int v0, v1;
#  define	usec(x)	(((x)&CTR_USEC_MASK)>>CTR_USEC_SHIFT)
#  define CPROF_WRAP	((1<<21)-1)
#  define CPROF_START()	(void)(v0 = *counterp)
#  define CPROF_STOP(C)	{						\
				v1 = *counterp;				\
				v0 = usec(v0);				\
				v1 = usec(v1);				\
				if( v1 < v0 )				\
					v1 += CPROF_WRAP;		\
				cprof_calls[proc.cmd]++;		\
				cprof_instab[proc.cmd][C] += (v1 - v0);	\
			}
#  endif /* GTOD */
# else
#  define StartCprof()
#  define CPROF_START()
#  define CPROF_STOP(C)
# endif	/* CPROF */


/*************************************************************
 *
 * Start of real code
 */
 
void 	start_profile(void);
void 	start_cprof(void);
void	trace(void);
void	opentrace(void);


/*
 * fetch instruction, set appropriate processor registers
 */
static inline void
ifetch(void)
{
	u_short a = proc.reg[7];

	proc.tinhibit = 0;
	if(a & 1)
		Trap4(0100);
	proc.mmr1reg[0] = proc.mmr1reg[1] = 0;
	proc.mmr1chg[0] = proc.mmr1chg[1] = 0;
	proc.mmr2 = a;
	proc.push = 0;
	proc.reg[7] += 2;
	proc.cmd = mfetch(a, 0);
}

/*
 * if instruction did a push and current mode is kernal than
 * we must check if stack dropped below 400
 */
static inline void
ChkStack(void)
{
	if(proc.push && proc.curmode == 0 && proc.reg[6] < 0400)
		YellowTrap();
}


extern VPF	instab[0200000][4]; 

/*
 * call microcode
 */
static inline void
Do(void)
{
	VPF	*vpf = &instab[proc.cmd][0];

# ifdef COUNT
	cycle++;
# endif
# ifdef CMPROF
	CPROF_START(); (*vpf[0])(); CPROF_STOP(0);
	CPROF_START(); (*vpf[1])(); CPROF_STOP(1);
	CPROF_START(); (*vpf[2])(); CPROF_STOP(2);
	CPROF_START(); (*vpf[3])(); CPROF_STOP(3);
# else  /* CMPROF */
	CPROF_START(); (*vpf[0])();
		       (*vpf[1])();
		       (*vpf[2])();
		       (*vpf[3])(); CPROF_STOP(0);
# endif /* CMPROF */
}


/*
 * interpreter loop
 *
 * 1. handle halt comands and hardware breaks.
 * 2. handle DMA and interrupt requests
 * 3. fetch instruction and interprete it
 * 4. handle the RTT/RTI case
 */
void
interp(void)
{
	OpenTrace();
	StartProfil();
	StartCprof();

	Reset();
	if((proc.maint & CPU_PWUP_MASK) == CPU_PWUP_T24)
		SetupTrap(024);
	for(setjmp(trap);;) {
		if(proc.halt || proc.reg[7] == proc.bp)
			monitor();
		if(proc.reqpri > proc.pri)
			request();
		else {
			UTrace();
			ifetch();
			Trace();
			Profil();
			Do();
			ChkStack();
			if(proc.t && !proc.tinhibit) SetupTrap(014);
		}
	}
}

/*************************************************************
 *
 * Tracing support
 */
# if defined(TRACE) || defined(PCTRACE)

FILE	*tf = NULL;

void
opentrace(void)
{
	char	*tfname = getenv("P11TRACE");

	if(!tfname)
		return;
	if((tf = fopen(tfname, "w")) == 0)
		panic("cannot create trace file");
}

void
trace(void)
{
# ifdef TRACE
	int r;

	if(!tf)
		return;
	for(r = 0; r < 8; r++)
		fprintf(tf, "%06ho ", proc.reg[r]);
	fprintf(tf, "%06ho\n", proc.cmd);
# else
	u_short w;

	if(!tf)
		return;
	w = htons(proc.reg[7]);
	fwrite(&w, sizeof(w), 1, tf);
	w = htons(proc.cmd);
	fwrite(&w, sizeof(w), 1, tf);
# endif
}
# endif

/*************************************************************
 *
 * Profiling
 */
# ifdef PROFIL
FILE	*pf;

void
write_profile(void)
{
	fwrite(profile, sizeof(profile), 1, pf);
	fclose(pf);
}

void
start_profile(void)
{
	if((pf = fopen("profile.p11", "w")) == 0)
		panic("cannot create profile file");
	atexit(write_profile);
}
# endif

/*************************************************************
 *
 * Cprofiling
 */
# ifdef CPROF
FILE	*cpf;

void
write_cprof(void)
{
	fwrite(cprof_instab, sizeof(cprof_instab), 1, cpf);
	fwrite(cprof_calls, sizeof(cprof_calls), 1, cpf);
	fclose(cpf);
}

void
start_cprof(void)
{
# ifdef	CMPROF
	static char *fname = "mprof.p11";
# else
	static char *fname = "cprof.p11";
# endif /* CMPROF */
	if((cpf = fopen(fname, "w")) == 0)
		panic("cannot create cprof file");
# ifndef GTOD
	counterp = map_counter();
# endif  /* GTOD */
	atexit(write_cprof);
}

# ifndef GTOD
#  ifdef sun4m
#   define	counter14	cpu[0].timer_lsw
#  endif
static u_int *
map_counter(void)
{
	int	fd;
	caddr_t	addr = NULL;
	char	*devname = "/dev/clock";
	struct counterregs *cregs;

	if( ( fd = open( devname, O_RDONLY ) ) < 0 )
		panic( "Cannot open %s:%s", devname, strerror(errno) );

	addr = mmap( NULL, sizeof(struct counterregs), PROT_READ,
						MAP_SHARED, fd, 0 ) ;

	if( ((int)addr) == -1 ) {
		return NULL;
	} else {
		cregs = (struct counterregs*)addr;
		return &(cregs->counter14);
	}
}
# endif /* GTOD */
# endif	/* CPROF */
