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

/*
 * fetch/store memory
 *
 * This file generates rather much code.
 * The macros below generate one function per addressing mode per
 * memory access mode. This way we don't need a sophisticated and slow
 * address decoding scheme.
 * The functions are identified by an uppercase letter (lowercase for
 * byte access) and two octal digits for the address mode.
 * Float value access is done 'by hand' trough a switch because performance
 * is less important here.
 * The functions for mode 46 and 56 set the proc.push flag to handle
 * the yellow/red stack traps.
 */

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

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

# define D(N,M,R) void N##M##R(void); void N##M##R(void) {
# define GENC(N,M) N##M(0) N##M(1) N##M(2) N##M(3) N##M(4) N##M(5) N##M(6) N##M(7) 
# define G(N) 	GENC(N,0) GENC(N,1) GENC(N,2) GENC(N,3) \
		GENC(N,4) GENC(N,5) GENC(N,6) GENC(N,7) 
# define RG(R) proc.reg[R]

/*
 * modify register R for operand X by I and record modification
 * in MMR1.
 * it appears that modifications of R7 are not recorded in MMR1
 */
# define MOD(R,I,X) {			\
	proc.reg[R] +=(I);		\
	if(R != 7) {			\
		proc.mmr1reg[X]=(R);	\
		proc.mmr1chg[X]+=(I);	\
	}				\
}

# define END }
# define WFETCH(A,S)	mfetch(CHKODD(A),(S))
# define WSTORE(A,S,V)	mstore(CHKODD(A),(S),(V))
# define ADDA(A,B)	(((A)+(B))&0177777U)

# define SETRGB(R,B)	((R) = ((R) & ~0377) | ((B) & 0377))

# define SPUSH(R)	if((R)==6) ++proc.push

/*
 * fetch a byte from virtual address a in space s
 */
# define BFETCH(A,S) ((((A) & 1) ?					\
			(mfetch((A) & ~1, (S)) >> 8)			\
		       :(mfetch((A), (S))))     & 0377)
# define BSTORE(A,S,V)	mstoreb(A,S,V)

/*
 * Check word address. trap to 4 if address is odd.
 * Inline functions are nice, but some compilers still don't support them.
 */
# define CHKODD(A) (((A) & 1) ? (Trap4(0100), 0U) : (A))

/*
 * if fetching in addressing mode 6/7 with following
 * store we must only temporary 
 * update the offset value by 2 if the register is R7
 * because it's original value is needed later when storing
 */
# define KK(R) (((R)==7)?2:0)

/*************************************************************
 *
 * Fetch source without following store
 * This is for TST and co.
 */
# define F0(R) D(F,0,R) src=RG(R); END
# define F1(R) D(F,1,R) src=WFETCH(RG(R),(R!=7)); END
# define F2(R) D(F,2,R) u_short a=RG(R); MOD(R,2,0); src=WFETCH(a,(R!=7)); END
# define F3(R) D(F,3,R) u_short a=RG(R); MOD(R,2,0); src=WFETCH(WFETCH(a,(R!=7)),1); END
# define F4(R) D(F,4,R) MOD(R,-2,0); SPUSH(R); src=WFETCH(RG(R),(R!=7)); END
# define F5(R) D(F,5,R) MOD(R,-2,0); SPUSH(R); src=WFETCH(WFETCH(RG(R),(R!=7)),1); END
# define F6(R) D(F,6,R) u_short a=mfetch(RG(7),0); RG(7)+=2; src=WFETCH(ADDA(RG(R),a),1); END
# define F7(R) D(F,7,R) u_short a=mfetch(RG(7),0); RG(7)+=2; src=WFETCH(WFETCH(ADDA(RG(R),a),1),1); END

/*************************************************************
 *
 * Fetch destination without following store
 * for CMP
 */
# define G0(R) D(G,0,R) dst=RG(R); END
# define G1(R) D(G,1,R) dst=WFETCH(RG(R),(R!=7)); END
# define G2(R) D(G,2,R) u_short a=RG(R); MOD(R,2,1); dst=WFETCH(a,(R!=7)); END
# define G3(R) D(G,3,R) u_short a=RG(R); MOD(R,2,1); dst=WFETCH(WFETCH(a,(R!=7)),1); END
# define G4(R) D(G,4,R) MOD(R,-2,1); SPUSH(R); dst=WFETCH(RG(R),(R!=7)); END
# define G5(R) D(G,5,R) MOD(R,-2,1); SPUSH(R); dst=WFETCH(WFETCH(RG(R),(R!=7)),1); END
# define G6(R) D(G,6,R) u_short a=mfetch(RG(7),0); RG(7)+=2; dst=WFETCH(ADDA(RG(R),a),1); END
# define G7(R) D(G,7,R) u_short a=mfetch(RG(7),0); RG(7)+=2; dst=WFETCH(WFETCH(ADDA(RG(R),a),1),1); END

/*************************************************************
 *
 * Fetch destination with following store
 * for INC, ...
 */
# define H0(R) D(H,0,R) dst=RG(R); END
# define H1(R) D(H,1,R) dst=WFETCH(RG(R),(R!=7)); END
# define H2(R) D(H,2,R) dst=WFETCH(RG(R),(R!=7)); END
# define H3(R) D(H,3,R) dst=WFETCH(WFETCH(RG(R),(R!=7)),1); END
# define H4(R) D(H,4,R) MOD(R,-2,1); SPUSH(R); dst=WFETCH(RG(R),(R!=7)); END
# define H5(R) D(H,5,R) MOD(R,-2,1); SPUSH(R); dst=WFETCH(WFETCH(RG(R),(R!=7)),1); END
# define H6(R) D(H,6,R) u_short a=mfetch(RG(7),0)+KK(R); dst=WFETCH(ADDA(RG(R),a),1); END
# define H7(R) D(H,7,R) u_short a=mfetch(RG(7),0)+KK(R); dst=WFETCH(WFETCH(ADDA(RG(R),a),1),1); END

/*************************************************************
 *
 * Fetch source address without following store
 * I0 is called for MF??, but value not used
 * for MFPI, ...
 */
# define I0(R) D(I,0,R) src=RG(R); END
# define I1(R) D(I,1,R) src=RG(R); END
# define I2(R) D(I,2,R) src=RG(R); MOD(R,2,0); END
# define I3(R) D(I,3,R) u_short a=RG(R); MOD(R,2,0); src=WFETCH(a,(R!=7)); END
# define I4(R) D(I,4,R) MOD(R,-2,0); SPUSH(R); src=RG(R); END
# define I5(R) D(I,5,R) MOD(R,-2,0); SPUSH(R); src=WFETCH(RG(R),(R!=7)); END
# define I6(R) D(I,6,R) u_short a=mfetch(RG(7),0); RG(7)+=2; src=RG(R)+a; END
# define I7(R) D(I,7,R) u_short a=mfetch(RG(7),0); RG(7)+=2; src=WFETCH(ADDA(RG(R),a),1); END


/*************************************************************
 *
 * Store destination without preceeding fetch
 * for CLR, ...
 */
# define S0(R) D(S,0,R) RG(R)=dst; END
# define S1(R) D(S,1,R) WSTORE(RG(R),(R!=7),dst); END
# define S2(R) D(S,2,R) u_short a=RG(R); MOD(R,2,1); WSTORE(a,(R!=7),dst); END
# define S3(R) D(S,3,R) u_short a=RG(R); MOD(R,2,1); WSTORE(WFETCH(a,(R!=7)),1,dst); END
# define S4(R) D(S,4,R) MOD(R,-2,1); SPUSH(R); WSTORE(RG(R),(R!=7),dst); END
# define S5(R) D(S,5,R) MOD(R,-2,1); SPUSH(R); WSTORE(WFETCH(RG(R),(R!=7)),1,dst); END
# define S6(R) D(S,6,R) u_short a=mfetch(RG(7),0); RG(7)+=2; WSTORE(ADDA(RG(R),a),1,dst); END
# define S7(R) D(S,7,R) u_short a=mfetch(RG(7),0); RG(7)+=2; WSTORE(WFETCH(ADDA(RG(R),a),1),1,dst); END


/*************************************************************
 *
 * Store destination with preceeding fetch
 * for INC, ...
 */
# define T0(R) D(T,0,R) RG(R)=dst; END
# define T1(R) D(T,1,R) WSTORE(RG(R),(R!=7),dst); END
# define T2(R) D(T,2,R) u_short a=RG(R); MOD(R,2,1); WSTORE(a,(R!=7),dst); END
# define T3(R) D(T,3,R) u_short a=RG(R); MOD(R,2,1); WSTORE(WFETCH(a,(R!=7)),1,dst); END
# define T4(R) D(T,4,R) SPUSH(R); WSTORE(RG(R),(R!=7),dst); END
# define T5(R) D(T,5,R) SPUSH(R); WSTORE(WFETCH(RG(R),(R!=7)),1,dst); END
# define T6(R) D(T,6,R) u_short a=mfetch(RG(7),0); RG(7)+=2; WSTORE(ADDA(RG(R),a),1,dst); END
# define T7(R) D(T,7,R) u_short a=mfetch(RG(7),0); RG(7)+=2; WSTORE(WFETCH(ADDA(RG(R),a),1),1,dst); END

/**********
 * BYTE
 * byte functions have the same naming scheme, but lowercase letters
 */

# define BM(R) (((R)>=6)?2:1)

/*************************************************************
 *
 * Fetch source without following store
 */
# define f0(R) D(f,0,R) src=RG(R)&0377; END
# define f1(R) D(f,1,R) src=BFETCH(RG(R),(R!=7)); END
# define f2(R) D(f,2,R) u_short a=RG(R); MOD(R,BM(R),0); src=BFETCH(a,(R!=7)); END
# define f3(R) D(f,3,R) u_short a=RG(R); MOD(R,2,    0); src=BFETCH(WFETCH(a,(R!=7)),1); END
# define f4(R) D(f,4,R) MOD(R,-BM(R),0); SPUSH(R); src=BFETCH(RG(R),(R!=7)); END
# define f5(R) D(f,5,R) MOD(R,-2,    0); SPUSH(R); src=BFETCH(WFETCH(RG(R),(R!=7)),1); END
# define f6(R) D(f,6,R) u_short a=mfetch(RG(7),0); RG(7)+=2; src=BFETCH(ADDA(RG(R),a),1); END
# define f7(R) D(f,7,R) u_short a=mfetch(RG(7),0); RG(7)+=2; src=BFETCH(WFETCH(ADDA(RG(R),a),1),1); END

/*************************************************************
 *
 * Fetch destination without following store
 */
# define g0(R) D(g,0,R) dst=RG(R); END
# define g1(R) D(g,1,R) dst=BFETCH(RG(R),(R!=7)); END
# define g2(R) D(g,2,R) u_short a=RG(R); MOD(R,BM(R),1); dst=BFETCH(a,(R!=7)); END
# define g3(R) D(g,3,R) u_short a=RG(R); MOD(R,2    ,1); dst=BFETCH(WFETCH(a,(R!=7)),1); END
# define g4(R) D(g,4,R) MOD(R,-BM(R),1); SPUSH(R); dst=BFETCH(RG(R),(R!=7)); END
# define g5(R) D(g,5,R) MOD(R,-2    ,1); SPUSH(R); dst=BFETCH(WFETCH(RG(R),(R!=7)),1); END
# define g6(R) D(g,6,R) u_short a=mfetch(RG(7),0); RG(7)+=2; dst=BFETCH(ADDA(RG(R),a),1); END
# define g7(R) D(g,7,R) u_short a=mfetch(RG(7),0); RG(7)+=2; dst=BFETCH(WFETCH(ADDA(RG(R),a),1),1); END

/*************************************************************
 *
 * Fetch destination with following store
 */
# define h0(R) D(h,0,R) dst=RG(R); END
# define h1(R) D(h,1,R) dst=BFETCH(RG(R),(R!=7)); END
# define h2(R) D(h,2,R) dst=BFETCH(RG(R),(R!=7)); END
# define h3(R) D(h,3,R) dst=BFETCH(WFETCH(RG(R),(R!=7)),1); END
# define h4(R) D(h,4,R) MOD(R,-BM(R),1); SPUSH(R); dst=BFETCH(RG(R),(R!=7)); END
# define h5(R) D(h,5,R) MOD(R,-2    ,1); SPUSH(R); dst=BFETCH(WFETCH(RG(R),(R!=7)),1); END
# define h6(R) D(h,6,R) u_short a=mfetch(RG(7),0)+KK(R); dst=BFETCH(ADDA(RG(R),a),1); END
# define h7(R) D(h,7,R) u_short a=mfetch(RG(7),0)+KK(R); dst=BFETCH(WFETCH(ADDA(RG(R),a),1),1); END

/*************************************************************
 *
 * Store destination without preceeding fetch
 */
# define s0(R) D(s,0,R) SETRGB(RG(R),dst); END
# define s1(R) D(s,1,R) BSTORE(RG(R),(R!=7),dst); END
# define s2(R) D(s,2,R) u_short a=RG(R); MOD(R,BM(R),1); BSTORE(a,(R!=7),dst); END
# define s3(R) D(s,3,R) u_short a=RG(R); MOD(R,2    ,1); BSTORE(WFETCH(a,(R!=7)),1,dst); END
# define s4(R) D(s,4,R) MOD(R,-BM(R),1); SPUSH(R); BSTORE(RG(R),(R!=7),dst); END
# define s5(R) D(s,5,R) MOD(R,-2    ,1); SPUSH(R); BSTORE(WFETCH(RG(R),(R!=7)),1,dst); END
# define s6(R) D(s,6,R) u_short a=mfetch(RG(7),0); RG(7)+=2; BSTORE(ADDA(RG(R),a),1,dst); END
# define s7(R) D(s,7,R) u_short a=mfetch(RG(7),0); RG(7)+=2; BSTORE(WFETCH(ADDA(RG(R),a),1),1,dst); END


/*************************************************************
 *
 * Store destination with preceeding fetch
 */
# define t0(R) D(t,0,R) SETRGB(RG(R),dst); END
# define t1(R) D(t,1,R) BSTORE(RG(R),(R!=7),dst); END
# define t2(R) D(t,2,R) u_short a=RG(R); MOD(R,BM(R),1); BSTORE(a,(R!=7),dst); END
# define t3(R) D(t,3,R) u_short a=RG(R); MOD(R,2    ,1); BSTORE(WFETCH(a,(R!=7)),1,dst); END
# define t4(R) D(t,4,R) SPUSH(R); BSTORE(RG(R),(R!=7),dst); END
# define t5(R) D(t,5,R) SPUSH(R); BSTORE(WFETCH(RG(R),(R!=7)),1,dst); END
# define t6(R) D(t,6,R) u_short a=mfetch(RG(7),0); RG(7)+=2; BSTORE(ADDA(RG(R),a),1,dst); END
# define t7(R) D(t,7,R) u_short a=mfetch(RG(7),0); RG(7)+=2; BSTORE(WFETCH(ADDA(RG(R),a),1),1,dst); END

/*************************************************************
 *
 * Store destination without preceeding fetch
 * same as s* but sign extend to word in mode 0
 */
# define u0(R) D(u,0,R) RG(R)=dst; END
# define u1(R) D(u,1,R) BSTORE(RG(R),(R!=7),dst); END
# define u2(R) D(u,2,R) u_short a=RG(R); MOD(R,BM(R),1); BSTORE(a,(R!=7),dst); END
# define u3(R) D(u,3,R) u_short a=RG(R); MOD(R,2    ,1); BSTORE(WFETCH(a,(R!=7)),1,dst); END
# define u4(R) D(u,4,R) SPUSH(R); MOD(R,-BM(R),1); BSTORE(RG(R),(R!=7),dst); END
# define u5(R) D(u,5,R) SPUSH(R); MOD(R,-2    ,1); BSTORE(WFETCH(RG(R),(R!=7)),1,dst); END
# define u6(R) D(u,6,R) u_short a=mfetch(RG(7),0); RG(7)+=2; BSTORE(ADDA(RG(R),a),1,dst); END
# define u7(R) D(u,7,R) u_short a=mfetch(RG(7),0); RG(7)+=2; BSTORE(WFETCH(ADDA(RG(R),a),1),1,dst); END

/*************************************************************
 *
 * Define functions for all registers
 */
G(F)
G(G)
G(H)
G(I)
G(S)
G(T)
G(f)
G(g)
G(h)
G(s)
G(t)
G(u)

/*
 * functions, used by float package
 */
void
store_long(int ll)
{
	int reg = proc.cmd & 07;
	u_short a = RG(reg);
	int dspace = (reg != 7);
	int off = (ll ? 4 : 2);

	switch(proc.cmd & 070) {

	case 000:
		RG(reg) = src;
		return;

	case 010:
		break;

	case 020:
		if(reg == 7) {
			ll = 2;
			off = 2;
		}
		MOD(reg,off,0);
		break;
	case 030:
		a = RG(reg); MOD(reg,2,0); a = WFETCH(a, (reg != 7)); dspace = 1;
		break;
	case 040:
		MOD(reg,-off,0); SPUSH(reg); a = RG(reg);
		break;
	case 050:
		MOD(reg,-2,0); SPUSH(reg); a = WFETCH(RG(reg), (reg != 7)); dspace = 1;
		break;
	case 060:
		a = mfetch(RG(7),0); RG(7)+=2; a += RG(reg); dspace = 1;
		break;
	case 070:
		a = mfetch(RG(7),0); RG(7)+=2; a = WFETCH(RG(reg) + a, 1); dspace = 1;
		break;
	}
	WSTORE(a + 0, dspace, src);
	if(ll)
		WSTORE(a + 2, dspace, dst);
}

/*
 * v - pointer to value
 * dbl - if true then double mode
 * mod - if true then need to modify registers in autodec modes
 */
void
store_float(u_short *v, int dbl, int mod)
{
	int reg = proc.cmd & 07;
	u_short a = RG(reg);
	int dspace = (reg != 7);
	int offs = (dbl ? 8 : 4);

	switch(proc.cmd & 070) {

	case 000:
		if(reg == 6 || reg == 7)
			abort();	/* may not happen */
		((u_short *)&proc.fpp.ac[reg])[0] = *v++;
		((u_short *)&proc.fpp.ac[reg])[1] = *v++;
		if(dbl) {
			((u_short *)&proc.fpp.ac[reg])[2] = *v++;
			((u_short *)&proc.fpp.ac[reg])[3] = *v++;
		} else {
			((u_short *)&proc.fpp.ac[reg])[2] = 0;
			((u_short *)&proc.fpp.ac[reg])[3] = 0;
		}
		return;

	case 010:
		break;

	case 020:
		if(reg == 7) {
			MOD(reg,2,0);
			WSTORE(a, dspace, v[0]);
			return;
		}
		MOD(reg,offs,0);
		break;
	case 030:
		MOD(reg,2,0); a = WFETCH(a, dspace); dspace = 1;
		break;
	case 040:
		if(mod) MOD(reg,-offs,0);
		SPUSH(reg);
		a = RG(reg);
		break;
	case 050:
		if(mod) MOD(reg,-2,0);
		SPUSH(reg);
		a = WFETCH(RG(reg), dspace); dspace = 1;
		break;
	case 060:
		a = mfetch(RG(7),0); RG(7)+=2; a += RG(reg); dspace = 1;
		break;
	case 070:
		a = mfetch(RG(7),0); RG(7)+=2; a = WFETCH(RG(reg) + a, 1); dspace = 1;
		break;
	}


	WSTORE(a + 0, dspace, v[0]);
	WSTORE(a + 2, dspace, v[1]);
	if(dbl) {
		WSTORE(a + 4, dspace, v[2]);
		WSTORE(a + 6, dspace, v[3]);
	}
}


/*
 * v - pointer to value
 * dbl - if true then double mode
 * mod - if true then need to modify registers in autoinc modes
 */
void
load_float(u_short *v, int dbl, int mod)
{
	int reg = proc.cmd & 07;
	u_short a = RG(reg);
	int dspace = (reg != 7);
	int offs = (dbl ? 8 : 4);

	switch(proc.cmd & 070) {

	case 000:
		if(reg == 6 || reg == 7)
			abort();	/* may not happen */
		*v++ = ((u_short *)&proc.fpp.ac[reg])[0];
		*v++ = ((u_short *)&proc.fpp.ac[reg])[1];
		if(dbl) {
			*v++ = ((u_short *)&proc.fpp.ac[reg])[2];
			*v++ = ((u_short *)&proc.fpp.ac[reg])[3];
		} else {
			*v++ = 0;
			*v++ = 0;
		}
		return;

	case 010:
		break;

	case 020:
		if(reg == 7) {
			if(mod) MOD(reg,2,0);
			*v++ = WFETCH(a, dspace);
			*v++ = 0;
			*v++ = 0;
			*v++ = 0;
			return;
		}
		if(mod) MOD(reg,offs,0);
		break;
	case 030:
		if(mod) MOD(reg,2,0); 
		a = WFETCH(a, dspace); dspace = 1;
		break;
	case 040:
		MOD(reg,-offs,0);
		SPUSH(reg);
		a = RG(reg);
		break;
	case 050:
		MOD(reg,-2,0); 
		SPUSH(reg);
		a = WFETCH(RG(reg), dspace); dspace = 1;
		break;
	case 060:
		a = mfetch(RG(7),0); RG(7)+=2; a += RG(reg); dspace = 1;
		break;
	case 070:
		a = mfetch(RG(7),0); RG(7)+=2; a = WFETCH(RG(reg) + a, 1); dspace = 1;
		break;
	}


	*v++ = WFETCH(a + 0, dspace);
	*v++ = WFETCH(a + 2, dspace);
	if(dbl) {
		*v++ = WFETCH(a + 4, dspace);
		*v++ = WFETCH(a + 6, dspace);
	} else {
		*v++ = 0;
		*v++ = 0;
	}
}

/*
 * load int or long
 * ll means long if true
 * src = int or high
 * dst = low
 */
void
load_long(int ll)
{
	int reg = proc.cmd & 07;
	u_short a = RG(reg);
	int dspace = (reg != 7);
	int offs = (ll ? 4 : 2);

	dst = 0;

	switch(proc.cmd & 070) {

	case 000:
		src = a;
		return;

	case 010:
		break;

	case 020:
		if(reg == 7 && ll)
			ll = 0;
		MOD(reg,offs,0);
		break;
	case 030:
		MOD(reg,2,0); 
		a = WFETCH(a, dspace); dspace = 1;
		break;
	case 040:
		MOD(reg,-offs,0);
		SPUSH(reg);
		a = RG(reg);
		break;
	case 050:
		MOD(reg,-2,0); 
		SPUSH(reg);
		a = WFETCH(RG(reg), dspace); dspace = 1;
		break;
	case 060:
		a = mfetch(RG(7),0); RG(7)+=2; a += RG(reg); dspace = 1;
		break;
	case 070:
		a = mfetch(RG(7),0); RG(7)+=2; a = WFETCH(RG(reg) + a, 1); dspace = 1;
		break;
	}


	src = WFETCH(a + 0, dspace);
	if(ll)
		dst = WFETCH(a + 2, dspace);
}
