/*
 Copyright (c) 1992-1993 The Regents of the University of California.
 All rights reserved.  See copyright.h for copyright notice and limitation 
 of liability and disclaimer of warranty provisions.
 */

#include "copyright.h"

#include <stdio.h>
#include "instr.h"
#include "encode.h"
#include "int.h"

#define FAST	0
#define true	1
#define false	0


extern char mem[];
extern int TRACE, Regtrace;

/* Machine registers */
int Reg[32];			/* GPR's */
int HI, LO;			/* mul/div machine registers */

/* statistics gathering places */
int numjmpls;
int arch1cycles;

/* Condition-code calculations */
#define  b31(z)		(((z) >>31 )&0x1)	/* extract bit 31 */

/* code looks funny but is fast thanx to MIPS! */
#define cc_add(rr, op1, op2)	\
	N = (rr < 0);	\
	Z = (rr == 0);	\
	C = ((unsigned) rr < (unsigned) op2);	\
	V = ((op1^op2) >= 0  &&  (op1^rr) < 0);

#define cc_sub(rr, op1, op2)	\
	N = (rr < 0);	\
	Z = (rr == 0);	\
	V = b31((op1 & ~op2 & ~rr) | (~op1 & op2 & rr));	\
	C = ((unsigned) op1 < (unsigned) op2);

	/* C = b31((~op1 & op2) | (rr & (~op1 | op2))); /* */

#define cc_logic(rr)	\
	N = (rr < 0);	\
	Z = (rr == 0);	\
	V = 0;	\
	C = 0;

#define cc_mulscc(rr, op1, op2)	\
	N = (rr < 0);	\
	Z = (rr == 0);	\
	V = b31((op1 & op2 & ~rr) | (~op1 & ~op2 & rr));	\
	C = b31((op1 & op2) | (~rr & (op1 | op2)));


runprogram(startpc, argc, argv)
int startpc, argc;
char *argv[];
{
    int aci, ai, j;
    register int instr, pc, xpc, npc;
    register int i;		/* temporary for local stuff */
    register int icount;
    extern char *strcpy();

    icount = 0;
    pc = startpc; npc = pc + 4;
    i = MEMSIZE - 1024 + memoffset;	/* Initial SP value */
    Reg[29] = i;			/* Initialize SP  */
    /* setup argc and argv stuff (icky!) */
    store(i, argc);
    aci = i + 4;
    ai = aci + 32;
    for  ( j=0; j<argc; ++j )
    {
	strcpy((mem-memoffset)+ai, argv[j]);
	store(aci, ai);
	aci += 4;
	ai += strlen(argv[j]) + 1;
    }


    for  ( ; ; )
    {
	++icount;
	xpc = pc; pc = npc; npc = pc + 4;
	instr = ifetch(xpc);
	Reg[0] = 0;	/* Force r0 = 0 */

	if  ( instr != 0 )	/* eliminate no-ops */
	{
	switch ( (instr>>26) & 0x0000003f)
	{
		case I_SPECIAL:
		{
		    switch ( instr & 0x0000003f )
		    {

			case I_SLL:
			    Reg[rd(instr)] = Reg[rt(instr)] << shamt(instr);
			    break;
			case I_SRL:
			    Reg[rd(instr)] = 
				(unsigned) Reg[rt(instr)] >> shamt(instr);
			    break;
			case I_SRA:
			    Reg[rd(instr)] = Reg[rt(instr)] >> shamt(instr);
			    break;
			case I_SLLV:
			    Reg[rd(instr)] = Reg[rt(instr)] << Reg[rs(instr)];
			    break;
			case I_SRLV:
			    Reg[rd(instr)] = 
				(unsigned) Reg[rt(instr)] >> Reg[rs(instr)];
			    break;
			case I_SRAV:
			    Reg[rd(instr)] = Reg[rt(instr)] >> Reg[rs(instr)];
			    break;
			case I_JR:
			    npc = Reg[rs(instr)];
			    break;
			case I_JALR:
			    npc = Reg[rs(instr)];
			    Reg[rd(instr)] = xpc + 8;
			    break;

			case I_SYSCALL: system_trap(); break;
			case I_BREAK: system_break(); break;

			case I_MFHI: Reg[rd(instr)] = HI; break;
			case I_MTHI: HI = Reg[rs(instr)]; break;
			case I_MFLO: Reg[rd(instr)] = LO; break;
			case I_MTLO: LO = Reg[rs(instr)]; break;

			case I_MULT:
			    {
				int t1, t2;
				int t1l, t1h, t2l, t2h;
				int neg;

				t1 = Reg[rs(instr)];
				t2 = Reg[rt(instr)];
				neg = 0;
				if ( t1 < 0 ) { t1 = -t1 ; neg = !neg; }
				if ( t2 < 0 ) { t2 = -t2 ; neg = !neg; }
				LO = t1 * t2;
				t1l = t1 & 0xffff;
				t1h = (t1 >> 16) & 0xffff;
				t2l = t2 & 0xffff;
				t2h = (t2 >> 16) & 0xffff;
				HI = t1h*t2h+((t1h*t2l)>>16)+((t2h*t1l)>>16);
				if ( neg )
				{
					LO = ~LO; HI = ~HI; LO = LO + 1;
					if  ( LO == 0 )  HI = HI + 1;
				}
			    }
			    break;
			case I_MULTU:
			    {
				int t1, t2;
				int t1l, t1h, t2l, t2h;

				t1 = Reg[rs(instr)];
				t2 = Reg[rt(instr)];
				t1l = t1 & 0xffff;
				t1h = (t1 >> 16) & 0xffff;
				t2l = t2 & 0xffff;
				t2h = (t2 >> 16) & 0xffff;
				LO = t1*t2;
				HI = t1h*t2h+((t1h*t2l)>>16)+((t2h*t1l)>>16);
			    }break;
			case I_DIV:
			    LO = Reg[rs(instr)] / Reg[rt(instr)];
			    HI = Reg[rs(instr)] % Reg[rt(instr)];
			    break;
			case I_DIVU:
			    LO =
			    (unsigned)Reg[rs(instr)] / (unsigned)Reg[rt(instr)];
			    HI =
			    (unsigned)Reg[rs(instr)] % (unsigned)Reg[rt(instr)];
			    break;

			case I_ADD:
			case I_ADDU:
			    Reg[rd(instr)] = Reg[rs(instr)] + Reg[rt(instr)];
			    break;
			case I_SUB:
			case I_SUBU:
			    Reg[rd(instr)] = Reg[rs(instr)] - Reg[rt(instr)];
			    break;
			case I_AND:
			    Reg[rd(instr)] = Reg[rs(instr)] & Reg[rt(instr)];
			    break;
			case I_OR:
			    Reg[rd(instr)] = Reg[rs(instr)] | Reg[rt(instr)];
			    break;
			case I_XOR:
			    Reg[rd(instr)] = Reg[rs(instr)] ^ Reg[rt(instr)];
			    break;
			case I_NOR:
			    Reg[rd(instr)] = ~(Reg[rs(instr)] | Reg[rt(instr)]);
			    break;

			case I_SLT:
			    Reg[rd(instr)] = (Reg[rs(instr)] < Reg[rt(instr)]);
			    break;
			case I_SLTU:
			    Reg[rd(instr)] =
				((unsigned) Reg[rs(instr)] 
					< (unsigned) Reg[rt(instr)]);
			    break;
			default: u(); break;
		    }
		} break;

		case I_BCOND:
		{
		    switch ( rt(instr) )	/* this field encodes the op */
		    {
			case I_BLTZ:
				if ( Reg[rs(instr)] < 0 )
					npc = xpc + 4 + (immed(instr)<<2);
				break;
			case I_BGEZ:
				if ( Reg[rs(instr)] >= 0 )
					npc = xpc + 4 + (immed(instr)<<2);
				break;

			case I_BLTZAL:
				Reg[31] = xpc + 8;
				if ( Reg[rs(instr)] < 0 )
					npc = xpc + 4 + (immed(instr)<<2);
				break;
			case I_BGEZAL:
				Reg[31] = xpc + 8;
				if ( Reg[rs(instr)] >= 0 )
					npc = xpc + 4 + (immed(instr)<<2);
				break;
			default: u(); break;
		    }
			
		} break;

		case I_J:
			npc = (xpc & 0xf0000000) | ((instr & 0x03ffffff) << 2);
			break;
		case I_JAL:
			Reg[31] = xpc + 8;
			npc = (xpc & 0xf0000000) | ((instr & 0x03ffffff) << 2);
			break;
		case I_BEQ:
			if  ( Reg[rs(instr)] == Reg[rt(instr)] )
				npc = xpc + 4 + (immed(instr) << 2);
			break;
		case I_BNE:
			if  ( Reg[rs(instr)] != Reg[rt(instr)] )
				npc = xpc + 4 + (immed(instr) << 2);
			break;
		case I_BLEZ:
			if  ( Reg[rs(instr)] <= 0 )
				npc = xpc + 4 + (immed(instr) << 2);
			break;
		case I_BGTZ:
			if  ( Reg[rs(instr)] > 0 )
				npc = xpc + 4 + (immed(instr) << 2);
			break;
		case I_ADDI:
			Reg[rt(instr)] = Reg[rs(instr)] + immed(instr);
			break;
		case I_ADDIU:
			Reg[rt(instr)] = Reg[rs(instr)] + immed(instr);
			break;
		case I_SLTI:
			Reg[rt(instr)] = (Reg[rs(instr)] < immed(instr));
			break;
		case I_SLTIU:
		        Reg[rt(instr)] =
			  ((unsigned) Reg[rs(instr)] < (unsigned) immed(instr));
			break;
		case I_ANDI:
			Reg[rt(instr)] = Reg[rs(instr)] & immed(instr);
			break;
		case I_ORI:
			Reg[rt(instr)] = Reg[rs(instr)] | immed(instr);
			break;
		case I_XORI:
			Reg[rt(instr)] = Reg[rs(instr)] ^ immed(instr);
			break;
		case I_LUI:
			Reg[rt(instr)] = instr << 16;
			break;

		case I_LB:
			Reg[rt(instr)] = cfetch(Reg[rs(instr)] + immed(instr));
			break;
		case I_LH:
			Reg[rt(instr)] = sfetch(Reg[rs(instr)] + immed(instr));
			break;
		case I_LWL:
		    i = Reg[rs(instr)] + immed(instr);
		    Reg[rt(instr)] &= (-1 >> 8*((-i) & 0x03));
		    Reg[rt(instr)] |= ((fetch(i & 0xfffffffc)) << 8*(i & 0x03));
		    break;
		case I_LW:
			Reg[rt(instr)] = fetch(Reg[rs(instr)] + immed(instr));
			break;
		case I_LBU:
			Reg[rt(instr)] = ucfetch(Reg[rs(instr)] + immed(instr));
			break;
		case I_LHU:
			Reg[rt(instr)] = usfetch(Reg[rs(instr)] + immed(instr));
			break;
		case I_LWR:
		    i = Reg[rs(instr)] + immed(instr);
		    Reg[rt(instr)] &= (-1 << 8*(i & 0x03));
		    if  ( (i & 0x03)== 0 )
			Reg[rt(instr)] = 0;
		    Reg[rt(instr)] |= 
			((fetch(i & 0xfffffffc)) >> 8*((-i) & 0x03));
		    break;

		case I_SB:
			cstore(Reg[rs(instr)] + immed(instr), Reg[rt(instr)]);
			break;
		case I_SH:
			sstore(Reg[rs(instr)] + immed(instr), Reg[rt(instr)]);
			break;
		case I_SWL:
			fprintf(stderr, "sorry, no SWL yet.\n");
			u();
			break;
		case I_SW:
			store(Reg[rs(instr)] + immed(instr), Reg[rt(instr)]);
			break;

		case I_SWR:
			fprintf(stderr, "sorry, no SWR yet.\n");
			u();
			break;

		case I_LWC0: case I_LWC1:
		case I_LWC2: case I_LWC3:
		case I_SWC0: case I_SWC1:
		case I_SWC2: case I_SWC3:
		case I_COP0: case I_COP1:
		case I_COP2: case I_COP3:
			fprintf(stderr, "Sorry, no coprocessors.\n");
			exit(2);
			break;

		default: u(); break;
	}
	}

#ifdef DEBUG
/*
printf(" %d(%x) = %d(%x) op  %d(%x)\n", Reg[rd], Reg[rd], op1, op1, op2, op2);
/* */
#endif
#if !FAST
	if  ( TRACE )
	{
	    dump_ascii(instr, xpc); printf("\n"); /* */
	    if  ( Regtrace )  dump_reg();
	}
#endif
    }
}



u()				/* unimplemented */
{
	printf("Unimplemented Instruction\n"); exit(2);
}

ny()
{
	printf("This opcode not implemeted yet.\n"); exit(2);
}


/* debug aids */

RS(i)
int i;
{
	return rs(i);
}

RT(i)
int i;
{
	return rt(i);
}

RD(i)
int i;
{
	return rd(i);
}

IM(i)
int i;
{
	return immed(i);
}



dump_reg()
{
	int j;

	printf(" 0:"); for  ( j=0; j<8; ++j ) printf(" %08x", Reg[j]);
		printf("\n");
	printf(" 8:"); for  ( ; j<16; ++j ) printf(" %08x", Reg[j]);
		printf("\n");
	printf("16:"); for  ( ; j<24; ++j ) printf(" %08x", Reg[j]);
		printf("\n");
	printf("24:"); for  ( ; j<32; ++j ) printf(" %08x", Reg[j]);
		printf("\n");

}



/*
	0 -> 0
	1 -> 1
	2 -> 1
	3 -> 2
	4 -> 2
	5 -> 2
	6 -> 2
	7 -> 3
	8 -> 3
	9 -> 3  ...
	Treats all ints as unsigned numbers.
*/
ilog2(i)
int i;
{
        int j, l;

	if  ( i == 0 )  return 0;
	j = 0;
	l = 1;
        if  ( (j=(i&0xffff0000)) != 0 ) { i = j; l += 16; }
        if  ( (j=(i&0xff00ff00)) != 0 ) { i = j; l += 8; }
        if  ( (j=(i&0xf0f0f0f0)) != 0 ) { i = j; l += 4; }
        if  ( (j=(i&0xcccccccc)) != 0 ) { i = j; l += 2; }
        if  ( (j=(i&0xaaaaaaaa)) != 0 ) { i = j; l += 1; }
        return l;
}



#define NH     32
#define NNN     33

static int hists[NH][NNN];
int hoflo[NH], htotal[NH];

void henters(n, hist)
int n, hist;
{
	if  ( 0 <= n  &&  n < NNN ) ++hists[hist][n]; else ++hoflo[hist];
	++htotal[hist];
}

hprint()
{
	int h, i;
	double I;

	for  ( h=0; h<=NH; ++h ) if  ( htotal[h] > 0 )
	{
		printf("\nhisto %d:\n", h);
		I = 0.0;
		for  ( i=0; i<NNN; ++i )
		{
			I += hists[h][i];
			printf("%d\t%d\t%5.2f%%\t%5.2f%%\n",
				i, hists[h][i],
				(double) 100*hists[h][i] / htotal[h],
				(double) 100*I/htotal[h]);
		}
		printf("oflo %d:\t%d/%d\t%5.2f%%\n", 
			h, hoflo[h], htotal[h],
			(double) 100*hoflo[h] / htotal[h]);
	}
}

int numadds=1, numsubs=1, numsuccesses, numcarries;
int addtable[33][33];
int subtable[33][33];

char fmt[] = "%6d";
char fmt2[] = "------";

patable(tab)
int tab[33][33];
{
	int i, j;

	printf("  |");
	for  ( j=0; j<33; ++j )
		printf(fmt, j);
	putchar('\n');
	printf("  |");
	for  ( j=0; j<33; ++j )
		printf(fmt2);
	putchar('\n');
	for  ( i=0; i<33; ++i )
	{
		printf("%2d|", i);
		for  ( j=0; j<33; ++j )
			printf(fmt, tab[i][j]);
		putchar('\n');
	}
}




printstatistics()
{
	/*
	printhist();
	/*
	printf("numjmpls = %d / %d = %5.2f%%\n",
		numjmpls, arch1cycles, 100.0*numjmpls/arch1cycles);
	printf("numadds = %d, numsubs = %d, numcycles = %d, frac = %5.2f%%\n",
		numadds, numsubs,
		arch1cycles, (double) 100 * (numadds+numsubs) / arch1cycles);
	printf("numsuccesses = %d (%5.2f%%) numcarries = %d\n", 
		numsuccesses, 100.0*numsuccesses/(numadds+numsubs), numcarries);

	/*
	hprint();
	printf("\nADD table:\n");patable(addtable);
	printf("\nSUB table:\n");patable(subtable);
	*/
}



#define NNNN	(64)

static int hist[NNNN];

henter(n)
int n;
{
	if  ( 0 <= n  &&  n < NNNN )
		++hist[n];
}

printhist()
{
	int i;

	for  ( i=0; i<NNNN; ++i )
		printf("%d %d\n", i, hist[i]);
}