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

# include "regi.h"
# include "proc.h"
# include "instruct.h"

RCSID("$Id: instruct.c 461 2000-12-04 17:03:43Z hbb $")

# define CN 	(proc.n = 0)
# define CZ 	(proc.z = 0)
# define CC 	(proc.c = 0)
# define CV 	(proc.v = 0)

# define SN 	(proc.n = 1)
# define SZ 	(proc.z = 1)
# define SC 	(proc.c = 1)
# define SV 	(proc.v = 1)


int	ill_trace;	/* trace illegal instructions on stdout */


void
ret(void)
{
}

extern void S46(void);
extern void F26(void);


void MOV(void) {
	dst = src;
	TestW(src); CV;
}
void MOVB(void) {
	dst = src;
	dst = SCHAR(dst);
	TestW(dst); CV;
}

void CMP(void) { CmpW(dst, src); }
void BIT(void) { dst &= src; TestW(dst); CV; }
void BIC(void) { dst &= ~src; TestW(dst); CV; }
void BIS(void) { dst |= src; TestW(dst); CV; }

void ADD(void) { AddWC(dst, src); }
void SUB(void) { SubWC(dst, src); }


void CMPB(void) { CmpB(dst, src); }
void BITB(void) { dst &= src; TestB(dst); CV; }
void BICB(void) { dst &= ~src; TestB(dst); CV; }
void BISB(void) { dst |= src; TestB(dst); CV; }


/*
 * call/return/jmp
 */
void JSR(void) {
	int reg = (proc.cmd >> 6) & 7;

	dst = proc.reg[reg]; S46();
	proc.reg[reg] = proc.reg[7];
	proc.reg[7] = src;
}

void RTS(void) {
	int reg = proc.cmd & 7;

	F26();
	proc.reg[7] = proc.reg[reg];
	proc.reg[reg] = src;
}
void JMP(void) {
	proc.reg[7] = src;
}

/*
 * single address
 */
void CLR(void) {
	dst = 0; CN; SZ; CV; CC;
}
void CLRB(void) {
	dst = 0; CN; SZ; CV; CC;
}

void COM(void) {
	dst = ~dst; TestW(dst); CV; SC;
}
void COMB(void) {
	dst = ~dst; TestB(dst); CV; SC;
}

void INC(void) { AddW(dst, 1); }
void INCB(void) { AddB(dst, 1); }
void DEC(void) { SubW(dst, 1); }
void DECB(void) { SubB(dst, 1); }

void NEG(void) {
	dst = -dst;
	TestW(dst);
	proc.c = (dst != 0);
	proc.v = (dst == 0100000);
}
void NEGB(void) {
	dst = -dst;
	TestB(dst);
	proc.c = (dst != 0);
	proc.v = (dst == 0200);
}

void ADC(void) { AddWC(dst, proc.c); }
void ADCB(void) { AddBC(dst, proc.c); }
void SBC(void) { SubWC(dst, proc.c); }
void SBCB(void) { SubBC(dst, proc.c); }
void TST(void) { TestW(src); CC; CV; }
void TSTB(void) { TestB(src); CC; CV; }

void ROR(void) {
	int tmp = (int)dst | ((int)proc.c << 16);
	proc.c = tmp & 1;
	dst = tmp >> 1;

	TestW(dst);
	proc.v = proc.c ^ proc.n;
}
void RORB(void) {
	int tmp = (int)dst | ((int)proc.c << 8);
	proc.c = tmp & 1;
	dst = tmp >> 1;

	TestB(dst);
	proc.v = proc.c ^ proc.n;
}

void ROL(void) {
	int tmp = ((int)dst << 1) | proc.c;
	dst = tmp;
	proc.c = (tmp & 0x10000) >> 16;
	TestW(dst);
	proc.v = proc.c ^ proc.n;
}
void ROLB(void) {
	int tmp = ((int)dst << 1) | proc.c;
	dst = tmp;
	proc.c = (tmp & 0x100) >> 8;
	TestB(dst);
	proc.v = proc.c ^ proc.n;
}

void ASR(void) {
	int tmp = (int)(signed short)dst;
	proc.c = tmp & 1;
	dst = tmp >> 1;
	TestW(dst);
	proc.v = proc.c ^ proc.n;
}
void ASRB(void) {
	int tmp = (int)SCHAR(dst);
	proc.c = tmp & 1;
	dst = tmp >> 1;
	TestB(dst);
	proc.v = proc.c ^ proc.n;
}

void ASL(void) {
	int tmp = (int)(signed short)dst;
	dst = (tmp <<= 1);
	proc.c = (tmp & 0x10000) >> 16;
	TestW(dst);
	proc.v = proc.c ^ proc.n;
}
void ASLB(void) {
	int tmp = (int)SCHAR(dst);
	dst = (tmp <<= 1);

	proc.c = (tmp & 0x100) >> 8;
	TestB(dst);
	proc.v = proc.c ^ proc.n;
}

/*
 * MARK loads 2*I<5:0> (not I<5:0>), thanks to Bob Supnik
 */
void MARK(void) {
	proc.reg[6] = proc.reg[7] + ((proc.cmd & 077) << 1);
	proc.reg[7] = proc.reg[5];
	F26();
	proc.reg[5] = src;
}

/*
 * the manual says nothing about wether to get the vector
 * 10 from I or from D-space, but my experience (in running rsx) suggest
 * it must be I-space
 */
void CSM(void) {
	int tmp, psw;

	if(!(proc.mmr3 & 010) || proc.curmode == 0)
		Trap10();
	proc.sp[1] = proc.reg[6];
	psw = iofetch(017777776);
	tmp = psw & ~017;
	psw = (psw & 07757) | ((psw & 0140000) >> 2) | 0040000;
	iostore(017777776, psw);
	dst = tmp; S46();
	dst = proc.reg[7]; S46();
	dst = src; S46();
	proc.reg[7] = mfetch(010, 0);
}

/* 
 * move from/to prevoius space, src has effective address 
 *
 * "M{T/F}P{I/D} do not trap on mode 0 references to non-sp;
 *  the normal register is referenced" (Bob Supnik)
 */
static void
domfp(int dspace)
{
	u_short a = src;

	if((proc.cmd & 070) == 0) {
		if((proc.cmd & 07) == 6)
			dst = proc.sp[proc.prevmode];
		else
			dst = proc.reg[proc.cmd & 07];
	} else {
		if(a & 1)
			Trap4(0100);
		dst = (dspace || (proc.curmode == 3 && proc.prevmode == 3)) ?
			mmfetch(a, 1, proc.prevmode)
		      : mmfetch(a, 0, proc.prevmode);
	}
	TestW(dst); CV;
	S46();
}
static void
domtp(int dspace)
{
	u_short a = src;

	F26();
	TestW(src); CV;

	if((proc.cmd & 070) == 0) {
		if((proc.cmd & 07) == 6)
			proc.sp[proc.prevmode] = src;
		else
			proc.reg[proc.cmd & 07] = src;
	} else {
		if(a & 1)
			Trap4(0100);
		mmstore(a, dspace, proc.prevmode, src);
	}
}

void MFPI(void) {
	domfp(0);
}
void MFPD(void) {
	domfp(1);
}
void MTPI(void) {
	domtp(0);
}
void MTPD(void) {
	domtp(1);
}

void WRTLCK(void) {
	if((proc.cmd & 070) == 0)
		Trap10();

	dst = proc.reg[0];
	TestW(dst); CV;
}
void TSTSET(void) {
	if((proc.cmd & 070) == 0)
		Trap10();

	proc.reg[0] = dst;
	proc.c = proc.reg[0] & 1;
	dst |= 1;
	TestW(dst); CV;
}

void MFPS(void) {
	dst = (signed short)(signed char)iofetch(017777776);
	TestB(dst); CV;
}
void MTPS(void) {
	dst = iofetch(017777776);
	if(proc.curmode == 0)
		dst = (dst & 020) | (src & 0357);
	else
		dst = (dst & 0340) | (src & 017);
	iostoreb(017777776, dst);
}

void SXT(void) {
	dst = proc.n ? -1 : 0;
	proc.z = !proc.n;
	CV;
}

void SWAB(void) {
	SwabB(dst);
	TestB(dst); CV; CC;
}

/*
 * EIS
 */
void MUL(void) {
	int reg = (proc.cmd >> 6) & 7;
	int s = (int)(signed short)src;
	int d = (int)(signed short)proc.reg[reg];
	int v = s * d;

	proc.n = (v < 0);
	proc.z = (v == 0);
	CV;
	proc.c = (v < -(1 << 15)) || (v >= (1 << 15)-1);
	proc.reg[reg] = (v >> 16);
	proc.reg[reg | 1] = v;
}

void DIV(void) {
	int reg = (proc.cmd >> 6) & 6;
	int v1 = (signed short)proc.reg[reg];		/* sign extend */
	int v2 = proc.reg[reg + 1];
	int v = (v1 << 16) | v2;		/* assume int has 32 bit */
	int d = (signed short)src;

	if(d == 0) {
		SV; SC; SZ; CN;
	} else if(abs(v1) >= abs(d)) {
		SV; CC; 
		proc.n = ((v < 0) ^ (d < 0));
	} else {
		CV; CC;
		Div(v1, v2, v, d);
		proc.n = (v1 < 0);
		proc.z = (v1 == 0);
		proc.reg[reg] = v1;
		proc.reg[reg + 1] = v2;
	}
}

/*
 * This instruction is another sources of questions (see ASHC later down).
 * It interpretes it's source the following way:
 * 	Values of the last 6 bits from 00 to 36 are interpreted as 
 *	positive values from 0 to +30. an mean left shift.
 *	Values of the last 6 bits higher than 36 are interpreted as
 *	negative numbers from -1. to -33. and give a right shift.
 */
void ASH(void) {
	int reg = (proc.cmd >> 6) & 7;
	int v = (int)(signed short)proc.reg[reg];
	int shift = src & 077;

	if(shift == 0) {	/* no shift */
		CC; CV;
	} else if(shift < 037) {	/* positive - left shift */
		/*
		 * make 32 bit shift so we know all shifted out
		 * bits. There was no overflow, if all shifted out bits
		 * are the same as the results sign.
		 */
		v <<= shift;
		proc.c = (v >> 16) & 1;
		proc.v = (u_short)(v >> 16) != (u_short)((signed short)v >> 15);
	} else if(shift == 037) {	/* right shift 33. bits */
		proc.c = (v < 0);
		v = (v < 0) ? -1 : 0;
		CV;
	} else {			/* negative - right shift */
		shift = 64 - shift;
		v >>= shift - 1;
		proc.c = v & 1;
		v >>= 1;
		CV;
	}
	proc.reg[reg] = v;
	TestW(v);
}

/*
 * The documentation to ASHC let's more questions open than it gives
 * answers. To get the answers I wrote a program that makes a large number
 * of tests and run it on my KDJ11A. Here a some points about the result:
 *
 * 1. Interpretation of the source operand.
 *	A source operand with the last 6 bits lesser than 037 is interpreted
 *	as a number from 0 to +30. and yields a left shift.
 *	A source operand with the last 6 bits greater than 037 is interpreted
 *	as 64.-SRC, i.e. a number from -1 to -32. and gives a right shift.
 *	The source operand 000037 is interpreted as +31. (left shift). 
 *	All other variants ending in 37 are taken as -33. meaning a right shift.
 * 2. "Rotation" if register is odd.
 *	This operation gives you kind of rotation on right shifts if
 *	the shift count is not to large. On left shifts there is no rotate.
 *	It appears to me that the processor builds a 32-bit word with
 *	equal high and low words, shifts them the normal way, computes
 *	condition codes from the 32-bit value and stores the lower half
 *	in the destination register.
 * 3. Right shifts can not set V (thanks to Bob Supnik).
 *
 * the code assumes v is a 32-bit signed value
 */
void ASHC(void) {
	int reg = (proc.cmd >> 6) & 7;
	int v;
	int d = src & 077;

	v = (proc.reg[reg] << 16) | proc.reg[reg | 1];

	CV;

	if(d == 0) {					/* no shift */
		CC;
	} else if(d < 037 || src == 037) {		/* left shift */
		/*
		 * use 64 bit shift, so carry is simply the last bit
		 * in the high word and there is no overflow if all bits
		 * shifted out are the same as the resulting sign bit
		 */
		xquad_t q = v;
		v = (int)(q <<= d);
		proc.c = (int)(q >> 32) & 1;
		proc.v = ((int)(q >> 32) != (v >> 31));
	} else if(d == 037) {				/* right shift 33 */
		/*
		 * result is 0 or -1 depending on sign of operand
		 */
		proc.c = (v < 0);
		v = (v < 0) ? -1 : 0;
		CV;
	} else {					/* right shift */
		v >>= (64 - d) - 1;
		proc.c = v & 1;
		v >>= 1;
	}

	/*
	 * condition codes are computed even on no shifts
	 */
	proc.n = (v < 0);
	proc.z = (v == 0);

	proc.reg[reg] = v >> 16;	/* upper half first for odd case */
	proc.reg[reg | 1] = v;
}

void XOR(void) {
	dst ^= proc.reg[(proc.cmd >> 6) & 7]; TestW(dst); CV;
}

void SOB(void) {
	int reg = (proc.cmd >> 6) & 7;

	if(--proc.reg[reg] != 0)
		proc.reg[7] -= (proc.cmd & 077) << 1;
}


/*
 * Branches
 */
static inline void dobranch(void) {
	proc.reg[7] += (signed char)(proc.cmd & 0377) << 1;
}
# define BRANCH(N,C) void N(void) { if(C) dobranch(); }

BRANCH(BR,  1)
BRANCH(BNE, !proc.z)
BRANCH(BEQ, proc.z)
BRANCH(BGE, !(proc.n ^ proc.v))
BRANCH(BLT, (proc.n ^ proc.v)) 
BRANCH(BGT, !(proc.z || (proc.n ^ proc.v)))
BRANCH(BLE, (proc.z || (proc.n ^ proc.v)))
BRANCH(BPL, !proc.n)
BRANCH(BMI, proc.n)
BRANCH(BHI, (!proc.z && !proc.c))
BRANCH(BLOS,(proc.c || proc.z))
BRANCH(BVC, !proc.v)
BRANCH(BVS, proc.v)
BRANCH(BCC, !proc.c)
BRANCH(BCS, proc.c)


/*
 * traps
 */
# define TP(N,W) void N(void) { SetupTrap(W); }

TP(BPT,  014)
TP(IOT,  020)
TP(EMT,  030)
TP(TRAP, 034)


/*
 * misc
 */
void HALT(void) {
	if(proc.curmode != 0 || (proc.maint & CPU_HALT))
		Trap4(0200);
	else
		proc.halt = 1;
}

void WAIT(void) {
	if(proc.curmode == 0)
		while(proc.reqpri <= proc.pri && !proc.halt)
			pause();
}

void RESET(void) {
	if(proc.curmode == 0)
		Reset();
}

void
dorti(void)
{
	u_short	pc, psw; 
	u_short	cpsw = iofetch(017777776);

	F26(); pc = src;
	F26(); psw = src;

	if(proc.curmode != 0) {
		/*
		 * mode bits and register set can not be cleared
		 * priority can not be changed
		 */
		psw &= ~0340;
		psw |= (cpsw & 0174340);
	}

	iostore(017777776, psw);
	proc.t = (psw >> 4) & 1;
	proc.reg[7] = pc;
}

void RTI(void) {
	dorti();
}

/*
 * what does rtt with T-bit on to an rtt? This is hard to test.
 * I suppose it traps after the second one.
 * So inhibit only if the T-bit toggles on.
 */
void RTT(void) {
	int oldT = proc.t;
	dorti();
	if(!oldT)
		proc.tinhibit = 1;
}
void SPL(void) {
	if(proc.curmode == 0)
		SETPRI(proc.cmd & 07);
}
void MFPT(void) {
	proc.reg[0] = 5;
}

void SCC(void) {
	if(proc.cmd &   1) 	SC;
	if(proc.cmd &   2) 	SV;
	if(proc.cmd &   4) 	SZ;
	if(proc.cmd & 010) 	SN;
}
void CCC(void) {
	if(proc.cmd &   1) 	CC;
	if(proc.cmd &   2) 	CV;
	if(proc.cmd &   4) 	CZ;
	if(proc.cmd & 010) 	CN;
}
void ILL(void) {
	if(ill_trace) {
		printf("ill inst: pc=%06o curmode=%d inst=%06o\n", proc.reg[7],
				proc.curmode, proc.cmd);
	}
	Trap10();
}
