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

/*
 * PDP11
 *
 * Generic memory interface
 */
# include "proc.h"

RCSID("$Id: mem.c 472 2001-03-15 14:36:02Z hbb $")

/*
# define IODBG
*/

static void	set_mapfunc(void);
static u_short 	get_mmr1(void);
static void	freeze_mmr(void);


/********
 *
 * I/O page
 */
u_short 
iofetch(u_int pa)
{
	iodev_t *dev;


	if(!(dev = proc.iopage[IOP(pa)]) || !dev->ioops->fetch) {
# ifdef IODBG
		printf("iofetch(%o) *******\n", pa);
# endif
		Trap4(020);
	}

# ifdef IODBG
	if(pa < 017765000 || (pa >= 017766000 && pa < 01777300) ||
	   (pa >= 017774000 && pa < 017777776))
		printf("iofetch(%o) %s\n", pa, dev->name);
# endif

	return (*dev->ioops->fetch)(dev, pa);
}


void 
iostore(u_int pa, u_short v)
{
	iodev_t *dev;

	if(!(dev = proc.iopage[IOP(pa)]) || !dev->ioops->store) {
# ifdef IODBG
		printf("iofetch(%o) *******\n", pa);
# endif
		Trap4(020);
	}

# ifdef IODBG
	if(pa < 017765000 || (pa >= 017766000 && pa < 01777300) ||
	   (pa >= 017774000 && pa < 017777776))
		printf("iofetch(%o) %s\n", pa, dev->name);
# endif
	(*dev->ioops->store)(dev, pa, M_Word, v);
}

void 
iostoreb(u_int pa, u_short v)
{
	iodev_t *dev;

	if(!(dev = proc.iopage[IOP(pa)]) || !dev->ioops->store) {
# ifdef IODBG
		printf("iofetch(%o) *******\n", pa);
# endif
		Trap4(020);
	}

# ifdef IODBG
	if(pa < 017765000 || (pa >= 017766000 && pa < 01777300) ||
	   (pa >= 017774000 && pa < 017777776))
		printf("iofetch(%o) %s\n", pa, dev->name);
# endif
	if(pa & 1)
		(*dev->ioops->store)(dev, pa & ~1, M_High, v << 8);
	else
		(*dev->ioops->store)(dev, pa, M_Low, v);
}

/************
 *
 * Trap
 */
void
MemTrap(int page, int mode, int dspace, u_short err)
{
	if(!proc.mmfrozen) {
		proc.mmr0 &= MMR0_ENABLE;
		proc.mmr0 |= err;
		proc.mmr0 |= mode << MMR0_MODE_SHIFT;
		proc.mmr0 |= dspace << MMR0_SPACE_SHIFT;
		proc.mmr0 |= page << MMR0_PAGE_SHIFT;
		freeze_mmr();
	}
	Trap(0250);
}


/**********
 * 
 * Memory manager I/O
 */
static void	memsys_ctrl_create(iodev_t *, int, char **);
static void	memsys_ctrl_complete(iodev_t *);
static void	memsys_reset(iodev_t *);
static u_short	memsys_fetch(iodev_t *, u_int);
static void	memsys_store(iodev_t *, u_int, mmode_t, u_short);
static void	memsys_info(iodev_t *);

static int memsysc_info(iodev_t *dev, int argc, char **argv);

iocmd_t memsys_cmds[] = {
	{ "?",		"[command ...]",	dev_help },
	{ "help",	"[command ...]",	dev_help },
	{ "info",	"",			memsysc_info },
	{ NULL,		NULL,			NULL }
};

ioops_t memsys_ops = {
	memsys_ctrl_create,	/* ctrl_create 		*/
	0,			/* dev_create		*/
	memsys_ctrl_complete,	/* ctrl_complete	*/
	memsys_reset,		/* reset		*/
	memsys_fetch,		/* fetch		*/
	memsys_store,		/* store		*/
	0,			/* vector		*/
	0,			/* dma			*/
	0,			/* async		*/
	memsys_info,		/* info			*/
	0,			/* flush 		*/
	memsys_cmds		/* cmds */
};

static void
memsys_ctrl_create(iodev_t *dev, int argc UNUSED, char **argv UNUSED)
{
	if(dev->instance != 0)
		conf_panic("cannot have more than one memory module\n");
}

static void	
memsys_ctrl_complete(iodev_t *dev)
{
	int i;

	mem_init();

	proc.iopage[IOP(MMG_0)] = dev;
	proc.iopage[IOP(MMG_1)] = dev;
	proc.iopage[IOP(MMG_2)] = dev;
	proc.iopage[IOP(MMG_3)] = dev;
	proc.iopage[IOP(MEM_CACHE  )] = dev;
	proc.iopage[IOP(MEM_HITMISS)] = dev;
	proc.iopage[IOP(MEM_ERROR  )] = dev;
	
	for(i = 0; i < 0100; i += 2) {
		proc.iopage[IOP(MMG_UIPDR0 + i)] = dev;
		proc.iopage[IOP(MMG_SIPDR0 + i)] = dev;
		proc.iopage[IOP(MMG_KIPDR0 + i)] = dev;
	}
	
	set_mapfunc();
}

static void	
memsys_reset(iodev_t *dev UNUSED)
{
	proc.mmr0 = 0;
	proc.mmr3 = 0;

	set_mapfunc();
	proc.memops->reset(proc.mmg);
}



static u_short	
memsys_fetch(iodev_t *dev UNUSED, u_int a)
{
	u_short	v;
	int	amode, aspace;

	switch(a) {
	
	case MMG_0:	
		v = proc.mmfrozen
			? ((proc.fmmr0&~MMR0_ENABLE) | (proc.mmr0&MMR0_ENABLE))
			: proc.mmr0; 
		break;

	case MMG_1:	
		v = proc.mmfrozen ? proc.fmmr1 : get_mmr1();
		break;
	case MMG_2:	
		v = proc.mmfrozen ? proc.fmmr2 : proc.mmr2;
		break;

	case MMG_3:	
		v = proc.mmr3; 
		break;

	case MEM_CACHE: v = proc.ccr; break;
	case MEM_HITMISS:
		v = 0;
		break;
	case MEM_ERROR:
		v = 0;
		break;

	default:
		if(a >= MMG_KIPDR0 && a < MMG_KIPDR0 + 0100)
			amode = 0;
		else if(a >= MMG_SIPDR0 && a < MMG_SIPDR0 + 0100)
			amode = 1;
		else if(a >= MMG_UIPDR0 && a < MMG_UIPDR0 + 0100)
			amode = 3;
		else {
			warn("mmg_fetch(%o)", a);
			Trap4(020);
		}
		aspace = ((a & 020) != 0);
		if(a & 040)
			v = proc.par[amode][aspace][(a >> 1) & 7];
		else
			v = proc.pdr[amode][aspace][(a >> 1) & 7];
		break;
	}
	return v;
}


static void
memsys_store(iodev_t *dev UNUSED, u_int a, mmode_t mode, u_short v)
{
	int	amode, aspace, areg;

	switch(a) {
	case MMG_0:
		v &= 0160001;
		SETWORD(proc.mmr0, mode, v);
		if(proc.mmr0 & MMR0_ABORT_MASK)
			freeze_mmr();
		else
			proc.mmfrozen = 0;
		set_mapfunc();
		proc.memops->mmr0(proc.mmg);
		break;

	case MMG_1:	break;
	case MMG_2:	break;

	case MMG_3:	
		if(mode == M_High)
			break;
		proc.mmr3 = v & MMR3_MASK;
		if(proc.no22)
			proc.mmr3 &= ~MMR3_22B;
		set_mapfunc();
		proc.memops->mmr3(proc.mmg);
		break;

	case MEM_CACHE:
		SETWORD(proc.ccr, mode, v);
		proc.memops->ccr(proc.mmg);
		proc.ccr &= 03377;
		break;

	case MEM_HITMISS:
		break;

	case MEM_ERROR:
		break;
	
	default:
		if(a >= MMG_KIPDR0 && a < MMG_KIPDR0 + 0100)
			amode = 0;
		else if(a >= MMG_SIPDR0 && a < MMG_SIPDR0 + 0100)
			amode = 1;
		else if(a >= MMG_UIPDR0 && a < MMG_UIPDR0 + 0100)
			amode = 3;
		else {
			warn("mmg_store(%o)", a);
			Trap4(020);
		}
		aspace = ((a & 020) != 0);
		areg = (a >> 1) & 7;
		if(a & 040) {
			/*	
			 * write to PAR
			 */
			SETWORD(proc.par[amode][aspace][areg], mode, v);
			proc.pdr[amode][aspace][areg] &= ~0100;
			proc.memops->par(proc.mmg, amode, aspace, areg);
		} else {
			/*
			 * write to PDR
			 */
			v &= 0177416;
			SETWORD(proc.pdr[amode][aspace][areg], mode, v);
			proc.pdr[amode][aspace][areg] &= ~0100;
			proc.memops->pdr(proc.mmg, amode, aspace, areg);
		}
		break;
	}
}


static void
set_mapfunc(void)
{
	if(proc.mmr0 & MMR0_ENABLE) {
		if(proc.mmr3 & MMR3_22B) {
			proc.mop = proc.memops->a22;
		} else {
			proc.mop = proc.memops->a18;
		}
	} else {
		proc.mop = proc.memops->a16;
	}
}

static void
memsys_info(iodev_t *dev UNUSED)
{
	printf("Memory system: physmem=%o (%d)\n", proc.physmem, proc.physmem);
	printf("mmr0:\t%06o (%s)\n", proc.mmr0, print_mmr0(proc.mmr0));
	printf("mmr1:\t%06o (%s)\n", get_mmr1(), print_mmr1(get_mmr1()));
	printf("mmr2:\t%06o\n", proc.mmr2);
	printf("mmr3:\t%06o (%s)\n", proc.mmr3, print_mmr3(proc.mmr3));
	printf("registers %sfrozen\n", proc.mmfrozen?"":"not ");
	if(proc.mmfrozen) {
		printf("fmmr0:\t%06o (%s)\n", proc.fmmr0, print_mmr0(proc.fmmr0));
		printf("fmmr1:\t%06o (%s)\n", proc.fmmr1, print_mmr1(proc.fmmr1));
		printf("fmmr2:\t%06o\n", proc.fmmr2);
	}

	proc.memops->info(proc.mmg);
}

char *
print_mmr0(u_short mmr0)
{
	static char ret[100];

	sprintf(ret, "%s%s%s%s%c %s %1o %s",
		(mmr0 & MMR0_ANR) ? "ANR " : "",
		(mmr0 & MMR0_APL) ? "APL " : "",
		(mmr0 & MMR0_ARO) ? "ARO " : "",
		(mmr0 & MMR0_MAI) ? "MAI " : "",
		"KSXU"[(mmr0 & MMR0_MODE) >> MMR0_MODE_SHIFT],
		 ((mmr0 & MMR0_SPACE) == 0) ? "I"
		:			      "D",
		(mmr0 & MMR0_PAGE) >> MMR0_PAGE_SHIFT,
		(mmr0 & MMR0_ENABLE) ? "ENABLED" : "DISABLED");
	return ret;
}

char *
print_mmr1(u_short mmr1)
{
	static char ret[100];
	short chg1, chg0;

	chg0 = (mmr1 >> 3) & 037;
	if(chg0 & 020)
		chg0 = -((~chg0 & 037) + 1);
	chg1 = (mmr1 >> 11) & 037;
	if(chg1 & 020)
		chg1 = -((~chg1 & 037) + 1);
	sprintf(ret, "dst=%1o(%d) src=%1o(%d)",
		(mmr1 >> 8) & 7, chg1,
		(mmr1 >> 0) & 7, chg0);
	return ret;
}
char *
print_mmr3(u_short mmr3)
{
	static char ret[100];

	sprintf(ret, "%s%s%s%s%s",
		(mmr3 & MMR3_IOM) ? "IOM " : "",
		(mmr3 & MMR3_KSEP) ? "KSEP " : "",
		(mmr3 & MMR3_SSEP) ? "SSEP " : "",
		(mmr3 & MMR3_USEP) ? "USEP " : "",
		(mmr3 & MMR3_22B) ? "22bit" : "not-22bit");
	return ret;
}


static int
memsysc_info(iodev_t *dev, int argc, char **argv UNUSED)
{
	if(argc != 0)
		return 1;
	memsys_info(dev);
	return 0;
}

/*
 * it's not clear, how it should work
 */
static u_short
get_mmr1(void)
{
	u_short	u;

	u = ((proc.mmr1reg[1] &   7) << 0)
	  | ((proc.mmr1chg[1] & 037) << 3);
	if((proc.mmr1chg[0] & 037) != 0)
		u = (u << 8)
		  | ((proc.mmr1reg[0] &   7) << 0)
	  	  | ((proc.mmr1chg[0] & 037) << 3);
	return u;
}

/*
 * if they are already frozen - no changes
 */
static void
freeze_mmr(void)
{
	if(!proc.mmfrozen) {
		proc.mmfrozen = 1;
		proc.fmmr0 = proc.mmr0;
		proc.fmmr1 = get_mmr1();
		proc.fmmr2 = proc.mmr2;
	}
}
