/************************************************************************
**
**	Dinero III Cache Simulator 
**	$Header: /var/home/markhill/DistributeDineroIII/RCS/fetch.c,v 3.3 89/05/04 09:57:29 markhill Exp $
**	Similar to Version 3.1, Released 8/7/85
**
**	Mark D. Hill
**	Computer Sciences Dept.
**	Univ. of Wisconsin
**	Madison, WI 53706
**	markhill@cs.wisc.edu
**
**	Developed DineroIII While Affiliated With:
**
**	Computer Science Division
**	University of California
**	Berkeley, California 94720
**
**	Source File:	fetch.c
**
************************************************************************/

/* 
**  Copyright 1985, 1989 Mark D. Hill
**
**  Permission to use, copy, modify, and distribute this
**  software and its documentation for any purpose and without
**  fee is hereby granted, provided that the above copyright
**  notice appear in all copies.  Mark D. Hill makes no
**  representations about the suitability of this software
**  for any purpose.  It is provided "as is" without expressed
**  or implied warranty.
*/  

#include "global.h"

fetch(					/* Get addr from input stream & decode*/
	cachep,ctrlp,metricp,dap)	
CACHETYPE *cachep;			/* <  */
CTRLTYPE *ctrlp;			/* <  */
METRICTYPE *metricp;			/* <> */
register DECODEDADDRTYPE *dap;		/*  > */

/* affects: many things indirectly
** returns: EOF or ~EOF
*/
{
long int addr;   /* @ changed from int to long int */
int thelabel;
static int flushcount = -2;	/* No of addr since last cache flush,
					-1=>no flushing */

if (flushcount==-2) {		/* Initialize */
	flushcount = ((ctrlp->Q<1) ? -1 : 0); /* flush? */
 }	/* end init */


 if ((flushcount>=0) && (flushcount++ >= ctrlp->Q)) { 
	flushcount=1;
	flushcache(cachep,ctrlp,metricp);
 }



/*  for 370 port:
370
370	Must do file I/O on 3081.
370
*/
#ifdef IBM370
if (readfrominputstream370(&thelabel,&addr,ctrlp,
			   ctrlp->infilep)==EOF) return(EOF);
#else
if (readfrominputstream(&thelabel,&addr,ctrlp)==EOF) return(EOF);
#endif

else {

#if test
           printf ("\nfetch.c:(fetch) addr %lx\n", addr);
#endif

    	if (thelabel == XFLUSH) flushcache(cachep,ctrlp,metricp);
	breakupaddr(addr,thelabel,dap,cachep);

	/* ******* debug -D ***** */
	/* dumpaddr(0, dap);	*/
	return(~EOF);
 }

} /* ********************************************************************* */


prefetch(				
	cachep,policyp,dap,miss,stackptr)
CACHETYPE *cachep;			/* <  */
POLICYTYPE *policyp;			/* <  */
register DECODEDADDRTYPE *dap;		/* < */
int miss;
STACKNODETYPE *stackptr;


/* 
**  This routine decides whether to start a prefetch access.
**  If so the prefetch is pushed on the addrstack to be read
**  instead of the next address trace address.
*/
{
long int prefetchaddr; /* @@ changed from int to long int */
extern int random();   /* @@ changed from long to int. MAXINTPERCENT in the global.h file was changed too. */


/*
**  See if prefetch needs to be aborted (because of data reference 
**  interference.  Note: 0 <= random() <= 2^31-1 (on processors that employ 32 bit integers) and
**  0 <= random()/MAXINTPERCENT < 100. 
**  The definition of MAXINTPERCENT in global.h is (int)(INT_MAX/100).
*/
if (policyp->abortprefetchpercent > 0 ) {
	if (random()/MAXINTPERCENT < policyp->abortprefetchpercent) {
		return;
	}
 }

/*
** Calculate address to prefetch
*/
prefetchaddr = dap->address + cachep->prefetchdisplacement;

/*
** Switch on prefetch policy
*/
switch (policyp->fetch) {

case ALWAYSPREFETCH:
	push_addrstack((PREFETCH+dap->accesstype),prefetchaddr);
	break;

case LOADFORWARDPREFETCH:
	/*
	** Don't prefetch into next block.
	*/
	if (
	  (dap->address/cachep->blocksize)==(prefetchaddr/cachep->blocksize)
		) {
		push_addrstack((PREFETCH+dap->accesstype),prefetchaddr);
	}
	break;

case SUBBLOCKPREFETCH:
	/*
	** Don't prefetch into next block; wrap around within block instead.
	*/
	if (
	   (dap->address/cachep->blocksize)!=(prefetchaddr/cachep->blocksize)
		) {
		prefetchaddr = prefetchaddr - cachep->blocksize;
	}
	push_addrstack((PREFETCH+dap->accesstype),prefetchaddr);
	break;

case MISSPREFETCH:
	if (miss) {
		push_addrstack((PREFETCH+dap->accesstype),prefetchaddr);
	}
	break;

/* 
**      Tagged prefetch (see Smith, "cache Memories," ~p.20) initiates 
**      a prefetch on the first demand reference to a (sub)-block.  Thus, 
**      a prefetch is initiated on a demand miss or the first demand 
**      reference to a (sub)-block that was brought into the cache by a 
**      prefetch. 
** 
**      Tagged prefetching is implemented using demand reference bits 
**      that are active only when tagged prefetching is selected.  A 
**      prefetch is started on a demand miss and on a refernce to a 
**      (sub)-block whose reference bit was not previously set.
*/
case TAGGEDPREFETCH:
	if ((miss) || ((dap->validbit & stackptr->reference)==0)) {
		push_addrstack((PREFETCH+dap->accesstype),prefetchaddr);
	}
	break;

case DEMAND:
default :
	printf("\n---Error in prefetch policy:%c\n",policyp->fetch);
	exit(1);
 }

} /* ********************************************************************* */





/*  for 370 port:
370
370	Must do file I/O on 3081.
370
*/
#ifdef IBM370

readfrominputstream370(labelp,addrp,ctrlp,theinfilep)	/* get next addr */
int *labelp;
int *addrp;
CTRLTYPE *ctrlp;
FILE *theinfilep;
{
register int readflag;
char linebuffer[256];
char *fgets();	/* fgets(s,n,stream) gets n-1 chars from stream 
		** or until a newline; changes newline to nullchar; 
		** returns ptr to the sting.
		*/

/* 
**	Read in ASCII from standard input 
**	Expect ONE label and addr in hex per line.
**	Rest of data input line is ignored so it may
**	be used for comments.
**
**	WARNING: If more than one tuple is put on a line,
**	all but the first tuple will be ignored.
*/

/*
**	Get tuple from addrstack if any are there.
*/
if (pop_addrstack(labelp,addrp)>=0) return(~EOF);

linebuffer[0] = NULL;
fgets(linebuffer,255,theinfilep);

#ifdef FAST_BUT_DANGEROUS_INPUT
readflag = sscanxx(linebuffer,labelp,addrp);
#else
readflag = sscanf(linebuffer,"%x %x",labelp,addrp);
#endif

if (readflag==2) {
	ctrlp->tracecount++;
	if (ctrlp->tracecount > ctrlp->maxcount) {
		return(EOF);
	}
	else {
		return(~EOF);
	}
 }
else {
	if (readflag!=EOF) {
		printf("\n **** Error in standard input.\n");
	}
	return(EOF);
 }

} /* ********************************************************************* */

#else

readfrominputstream(labelp,addrp,ctrlp)	/* get next addr */
int *labelp;
long int *addrp;   /* @ changed from int to long int */
CTRLTYPE *ctrlp;
{
register int readflag;
char linebuffer[255];
char *gets();	/* Gets chars from stdin until a newline;
		** changes newline to nullchar; returns ptr
		** to the sting.
		*/

/* 
**	Read in ASCII from standard input 
**	Expect ONE label and addr in hex per line.
**	Rest of data input line is ignored so it may
**	be used for comments.
**
**	WARNING: If more than one tuple is put on a line,
**	all but the first tuple will be ignored.
*/

/*
**	Get tuple from addrstack if any are there.
*/
if (pop_addrstack(labelp,addrp)>=0) return(~EOF);

linebuffer[0] = NULL;
gets(linebuffer);

#ifdef FAST_BUT_DANGEROUS_INPUT
readflag = sscanxx(linebuffer,labelp,addrp);
#else
readflag = sscanf(linebuffer,"%x %x",labelp,addrp);
#endif

if (readflag==2) {
	ctrlp->tracecount++;  /* count how many addresses were processed by now */
	if (ctrlp->tracecount > ctrlp->maxcount) {

if(sizeof(long) > sizeof(int))
	        printf ("\n*** Dinero did NOT process the entire input trace! ***\nDinero has processed %ld addresses from the input trace which is the current limit.\nIf you wish to process more addresses than this limit allows, next time please use the -z flag to change this limit.\n\n", ctrlp->maxcount);
else
	       printf ("\n*** Dinero did NOT process the entire input trace! ***\nDinero has processed %d addresses from the input trace which is the current limit.\nIf you wish to process more addresses than this limit allows, next time please use the -z flag to change this limit.\n\n", ctrlp->maxcount);

		return(EOF);
	}
	else {
		return(~EOF);
	}
 }
else {
	if (readflag!=EOF) {
		printf("\n **** Error in standard input.\n");
	}
	return(EOF);
 }

} /* ********************************************************************* */

#endif





breakupaddr(			/* Decode address */
	    addr,labl,dap,cachep)
register long int addr;		/* <  address  @@ changed from int to long int */
register int labl;		/* <  type of reference */
register DECODEDADDRTYPE *dap;	/* <> fields of this ptr are altered */
CACHETYPE *cachep;			/* <  */
/*
** affects: none
** returns: OK
*/
{

/*
**	These unsigned integers are so that a long int address with
**	an MSB of 1 will be shifted correctly with a divide.
*/

unsigned long int theaddr;        /* @@ changed from int to long int */
unsigned long int theblocksize;   /* @@ changed from int to long int */

unsigned int thenumUorDsets, thenumIsets;


#if 0
   printf ("fetch.c: (breakupaddr) addr = %lx\n", addr);
#endif

theaddr = addr;

theblocksize = cachep->blocksize;
thenumUorDsets = cachep->numUorDsets;
thenumIsets = cachep->numIsets;

#if test
   printf ("block size: %d      #of U/D sets: %d     #of I sets: %d\n", theblocksize, thenumUorDsets, thenumIsets); 
#endif

dap->address = theaddr;
dap->accesstype = labl;
dap->block = theaddr % theblocksize;
dap->blockaddr = theaddr - dap->block;

#if test
   printf ("dap->address: %lx     dap->accesstype: %d     dap->block: %d    dap->blockaddr: %lx\n", dap->address, dap->accesstype, dap->block, dap->blockaddr);
#endif

if (cachep->subblocksize==0) {
	dap->validbit = VALID;
 }
else {
	dap->subblocknum = dap->block / cachep->subblocksize;
	dap->validbit = VALID<<dap->subblocknum;
#if test
           printf ("dap->subblocknum: %d   dap->validbit=%d\n", dap->subblocknum, dap->validbit);
#endif
 }

switch ( labl ) {

	case XREAD :
	case XWRITE : 
	case PREFETCH+XREAD :
	case PREFETCH+XWRITE : 
		dap->set = (theaddr / theblocksize) % thenumUorDsets;
		dap->tag = (theaddr / theblocksize) / thenumUorDsets;
		break;

	case XINSTRN : 
	case PREFETCH+XINSTRN : 
		if (thenumIsets != 0) { /* I-Cache */
			dap->set = ((theaddr / theblocksize) % thenumIsets)
				+ thenumUorDsets;
			dap->tag = (theaddr / theblocksize) / thenumIsets;
		}
		else {	/*  Mixed Cache; put in data cache set */
		dap->set = (theaddr / theblocksize) % thenumUorDsets;
		dap->tag = (theaddr / theblocksize) / thenumUorDsets;
		}
		break;

	case PREFETCH+XMISC : 
		dap->set = (theaddr / theblocksize) % thenumUorDsets;
		dap->tag = (theaddr / theblocksize) / thenumUorDsets;
		break;

	default : 
		dap->accesstype = XMISC;
		dap->set = (theaddr / theblocksize) % thenumUorDsets;
		dap->tag = (theaddr / theblocksize) / thenumUorDsets;
		break;
 }

#if test
   printf ("dap->tag: %lx    dap->set: %d\n", dap->tag, dap->set);
#endif

} /* ********************************************************************* */






flushcache(			/* Flush cache */
	cachep,ctrlp,metricp)	
CACHETYPE *cachep;			/* <  */
CTRLTYPE *ctrlp;			/* <  */
METRICTYPE *metricp;			/* <> */

/* affects: 
** returns:
*/
{
extern int copybackstack();
extern int putonfreelist();

int stacknum;

for (stacknum=0; stacknum<(cachep->numsets); stacknum++) {
	copybackstack(cachep,ctrlp,metricp,stacknum);
	putonfreelist(stacknum,&(stack[stacknum]));
 }

} /* *************************************************** */




init_addrstack ()
/*
**	NOTE: size_addrstack points beyond valid data.  A 3-address
**	stack would have array entries 0, 1, and 2 filled and
**	size_addrstack equal to 3.
*/
{
int i;

for (i=0; i<MAXNUMADDRSTACK; i++) {
	addrstack[i].label = XMISC;
	addrstack[i].address = 0;
 }

size_addrstack = 0;
}



push_addrstack (labl, addr)
int labl;
long int addr;  /* @@ changed from int to long int */

{
if (size_addrstack == MAXNUMADDRSTACK) {
	/* error: addr stack overflow */
	printf("Error: addr stack overflow\n");
	return(-2);
 }
else {
	addrstack[size_addrstack].label = labl;
	addrstack[size_addrstack].address = addr;
	size_addrstack++;
	return(size_addrstack);
 }
}


pop_addrstack (lablp, addrp)
int *lablp;
long int *addrp;  /* @@ changed from int to long int */

{
if (size_addrstack == 0) {
	/* addr stack empty */
	return(-1);
 }
else {
	size_addrstack--;
	*lablp = addrstack[size_addrstack].label;
	*addrp = addrstack[size_addrstack].address;
	return(size_addrstack);
 }
}


print_addrstack ()
{
int i;

printf("addrstack(%d of %d): ", size_addrstack, MAXNUMADDRSTACK);

for (i=size_addrstack-1; i>=0; i--) {
	printf("%d@%ld, ", addrstack[i].label, addrstack[i].address);
 }

printf("end.\n");
}







#ifdef FAST_BUT_DANGEROUS_INPUT
/*
**	The above "ifdef" enables a fast C-function called "sscanxx" 
**	to interpret input characters instead of the library function 
**	"sscanf" because a profile showed that dineroIII was spending 
**	35% to 50% of this time in sscanf.  The function "sscanxx" runs 
**	about 6 times faster than "sscanf."  The function "sscanxx,"
**	selected by the compile-time flag FAST_BUT_DANGEROUS_INPUT in 
**	global.h, make dineroIII run in 60 to 70% of the time with the
**	option diabled.
*/

#define CHAR_ZERO		'0'
#define CHAR_NINE		'9'
#define CHAR_LOWERCASE_A	'a'
#define CHAR_LOWERCASE_F	'f'

#define HEX	4

#define CHAR_DIGIT_OFFSET		'0'
#define CHAR_LOWERCASE_ABCDEF_OFFSET	'a'-10

#define WHITE_SPACE	((*ptr==' ')||(*ptr=='\t'))

#define CHAR_HEX_DIGIT		\
	((((digit = *ptr)>=CHAR_ZERO) && (digit<=CHAR_NINE)) \
	|| ((digit>=CHAR_LOWERCASE_A) && (digit<=CHAR_LOWERCASE_F)))

#define HEX_CHAR_OFFSET		\
	((digit>=CHAR_LOWERCASE_A) && (digit<=CHAR_LOWERCASE_F) \
	? /* a-f */		\
	CHAR_LOWERCASE_ABCDEF_OFFSET	\
	: /* 0-9 */		\
	CHAR_DIGIT_OFFSET	\
	)


sscanxx(linebuffer,num1p,num2p)
char linebuffer[];
int *num1p;
long int *num2p;   /* @@ changed from int to long int */
/*
**	This routine is designed to do the same function as
**	sscanf(linebuffer,"%x %x",labelp,addrp).  It was added
**	because gprof said that 35 to 50% of dineroIII's run time
**	was being spent in sscanf.   This routine runs eight
**	times faster.
**
**	The routine is not exactly the same as sscanf:
**	
**	(1)	It is implementation dependent.
**	(2)	Hex numbers cannot be preceded by "0x."
**	(3)	Legal digits are "0-9" and "a-f;" "A-F" will not 
**		be interpretted correctly.
**	(4)	The reponse to input errors is undefined.
*/
{
register char digit;
register char *ptr;
register long int num;   /* @ changed from int to long int */

#define MAX_ADDRESS_LENGTH  sizeof(long int)*2  /* The max number of litterals in a legal input Hex address */
    
register int input_addr_length;   /* counter that holds the number of literals in the input address, which should not exceed MAX_ADDRESS_LENGTH. */
char input_addr_string[MAX_ADDRESS_LENGTH+2] = ""; /* define a string to hold the literals of the current address */


/*
**	Exit with EOF if the buffer is empty.
*/
if (*(ptr = linebuffer)==NULL) return(EOF);

while (WHITE_SPACE) ptr++;

/*
**	Convert first hex number.
*/
num = 0;
while (CHAR_HEX_DIGIT) {
	num = (num<<HEX) + digit - HEX_CHAR_OFFSET;
	ptr++;
 }
*num1p = num;

/*	Skip white space.	*/
while (WHITE_SPACE) ptr++;

/*
**	Convert second hex number.
*/
num = 0;                /* accumulate the input address */
input_addr_length = 0;  /* count the input address literals as they are read */


while (CHAR_HEX_DIGIT) {
	num = (num<<HEX) + digit - HEX_CHAR_OFFSET;
	ptr++;
	input_addr_string[input_addr_length++] = digit;  /* gather the incoming address into a string */

	/*	printf ("%d  %s\n", input_addr_length, input_addr_string); */

	if (input_addr_length > MAX_ADDRESS_LENGTH)  /* check for address length violation */
	  {
	    printf ("\n\nAn address longer than %d Hex literals was found in the input trace.\nThis computer can only handle address length of up to %d bits.\nThe offensive address begins with (Hex) %s\n*** Program Terminated ***\n", MAX_ADDRESS_LENGTH, MAX_ADDRESS_LENGTH*4, input_addr_string);
	    exit (1);
	  }
}
*num2p = num;

#if 0
   printf ("fetch.c:(sscanxx) num2p = %ld  %lx\n", num, num);
#endif

return(2);
}

#endif


