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

/*
 * PDP11
 *
 * this is an implementation of a memory that pre-translates
 * the page boundaries to host physical addresses
 */
# include "proc.h"

RCSID("$Id: mem_small.c 465 2001-02-26 12:11:48Z hbb $")

typedef struct space	space_t;
typedef struct spacereg	spacereg_t;

struct spacereg {
	u_int		par;		/* page address 		*/
	u_short		pdr;		/* orig pdr			*/
	u_short		*ppdr;		/* pointer to real pdr		*/
	u_char		*base;		/* page base			*/
	u_char		*high;		/* upper limit			*/
	u_char		*low;		/* lower limit			*/
	int		w;		/* write access			*/
	int		r;		/* r/w access			*/
};
	
struct space {
	spacereg_t	r[8];		/* registers			*/
};

/*
 * The 's' member is able to describe all spaces K/U/S and I/D.
 * The 'a' member points to the appropriate 's' member. If split I/D
 * is off for a given address space, the a's point to the same s. If
 * It is on it points to differents s'es.
 */
struct mmg {
	space_t		s[4][2];	/* address spaces		*/
	space_t		*a[4][2];	/* access spaces		*/
	spacereg_t	*s_last;	/* last accessed space		*/
	u_short		mmr0;		/* copy of proc.mmr0		*/
	u_int		mmr3;		/* copy of proc.mmr3		*/
	u_short	mem[Off(PHYSMEM)];	/* real memory			*/
};

static void 	comp_all(mmg_t *);
static void 	comp_page(mmg_t *, int, int, int);

void	sm_reset(mmg_t *);		/* bus reset		*/
void	sm_mmr0(mmg_t *);		/* mmr0 written		*/
void	sm_mmr3(mmg_t *);		/* mmr3 written		*/
void	sm_par(mmg_t *, int, int, int);	/* par written		*/
void	sm_pdr(mmg_t *, int, int, int);	/* pdr written		*/
void	sm_ccr(mmg_t *);		/* ccr written		*/
void	sm_info(mmg_t *);		/* odt info		*/
void	sm_store16(u_int, int, int, u_short);
void	sm_store18(u_int, int, int, u_short);
void	sm_store22(u_int, int, int, u_short);
void	sm_storeb16(u_int, int, int, u_short);
void	sm_storeb18(u_int, int, int, u_short);
void	sm_storeb22(u_int, int, int, u_short);
u_int	sm_fetch16(u_int, int, int);
u_int	sm_fetch18(u_int, int, int);
u_int	sm_fetch22(u_int, int, int);
u_short	*sm_addrphys(mmg_t *, u_int);	/* for dma		*/

static mmg_t	mmg;		/* can have only one		*/
static memops_t	memops = {
	sm_reset,		/* reset	*/
	sm_mmr0,		/* mmr0		*/
	sm_mmr3,		/* mmr3		*/
	sm_par,			/* par		*/
	sm_pdr,			/* pdr		*/
	sm_ccr,			/* ccr		*/
	sm_info,		/* info		*/
	{ sm_store16, sm_storeb16, sm_fetch16 },
	{ sm_store18, sm_storeb18, sm_fetch18 },
	{ sm_store22, sm_storeb22, sm_fetch22 },
	sm_addrphys,		/* addrphys	*/
};


/*
 * 16-bit mapping.
 */
u_int
sm_fetch16(u_int a, int dspace UNUSED, int mode UNUSED)
{
	if(a >= 0160000)
		return iofetch(a | 017760000);
	if(a >= proc.physmem)
		Trap4(040);
	return GETW(mmg.mem[Off(a)]);
}

void
sm_store16(u_int a, int dspace UNUSED, int mode UNUSED, u_short v)
{
	if(a >= 0160000) {
		iostore(a | 017760000, v);
		return;
	}
	if(a >= proc.physmem)
		Trap4(040);

	SETW(mmg.mem[Off(a)], v);
}

void
sm_storeb16(u_int a, int dspace UNUSED, int mode UNUSED, u_short v)
{
	if(a >= 0160000) {
		iostoreb(a | 017760000, v);
		return;
	}
	if(a >= proc.physmem)
		Trap4(040);

	if(a & 1)
		SETHB(mmg.mem[Off(a)], v);
	else
		SETLB(mmg.mem[Off(a)], v);
}


/* 
 * 18/22 bit mapping
 */
# define GEN(NAME, SIZE, OR)						\
/*									\
 * Translate logical to physical address with check.			\
 */									\
static inline u_char *							\
physaddr##NAME(u_int a, int mode, int dspace, int store)		\
{									\
	u_int 		page;						\
	u_char 		*pa;						\
	space_t		*s;						\
	spacereg_t	*r;						\
									\
	page = a >> 13;							\
	s = mmg.a[mode][dspace];					\
	r = mmg.s_last = &s->r[page];					\
									\
	/*								\
	 * no need to check on mode==2 because mode2 r field is always 0\
	 */								\
	if(!r->r)							\
		MemTrap(page, mode, dspace, MMR0_ANR);			\
	if(store && !r->w)						\
		MemTrap(page, mode, dspace, MMR0_ARO);			\
									\
	pa = r->base + (a & 017777);					\
									\
	if(pa >= r->high || pa < r->low)				\
		MemTrap(page, mode, dspace, MMR0_APL);			\
									\
	/* 								\
	 * the following implements the wrap around at the end of the	\
	 * physical address space					\
	 */								\
	if(pa >= (u_char *)&mmg.mem[Off(SIZE)])				\
		pa -= SIZE;						\
									\
	return pa;							\
}									\
									\
									\
/*									\
 * Fetch from logical address a, in space 				\
 */									\
u_int									\
sm_fetch##NAME(u_int a, int dspace, int mode)				\
{									\
	u_char *pa = physaddr##NAME(a, mode, dspace, 0);		\
									\
	if(pa >= (u_char *)&mmg.mem[Off(SIZE-020000)])			\
		return iofetch((pa - (u_char *)&mmg.mem[Off(SIZE-020000)]) | OR);\
									\
	if(pa >= (u_char *)&mmg.mem[Off(PHYSMEM)])			\
		Trap4(040);						\
									\
	return GETW(*(u_short *)pa);					\
}									\
									\
void									\
sm_store##NAME(u_int a, int dspace, int mode, u_short v)		\
{									\
	u_char *pa = physaddr##NAME(a, mode, dspace, 1);		\
									\
	if(pa >= (u_char *)&mmg.mem[Off(SIZE-020000)]) {		\
		iostore((pa - (u_char *)&mmg.mem[Off(SIZE-020000)]) | OR, v);\
		*mmg.s_last->ppdr |= 0100;				\
		return;							\
	}								\
									\
	if(pa >= (u_char *)&mmg.mem[Off(PHYSMEM)])			\
		Trap4(040);						\
									\
	SETW(*(u_short *)pa, v);					\
	*mmg.s_last->ppdr |= 0100;					\
}									\
									\
void									\
sm_storeb##NAME(u_int a, int dspace, int mode, u_short v)		\
{									\
	u_char *pa = physaddr##NAME(a, mode, dspace, 1);		\
									\
	if(pa >= (u_char *)&mmg.mem[Off(SIZE-020000)]) {		\
		iostoreb((pa - (u_char *)&mmg.mem[Off(SIZE-020000)]) | OR, v);\
		*mmg.s_last->ppdr |= 0100;				\
		return;							\
	}								\
									\
	if(pa >= (u_char *)&mmg.mem[Off(PHYSMEM)])			\
		Trap4(040);						\
									\
	*pa = v;							\
	*mmg.s_last->ppdr |= 0100;					\
}

GEN(18,  01000000, 017760000)
GEN(22, 020000000, 017760000)

/*
 * routines called from generic interface
 */
void
mem_init(void)
{
	int i, j, k;

	for(i = 0; i < 4; i++)
		mmg.a[i][0] = mmg.a[i][1] = &mmg.s[i][0];
	for(i = 0; i < 4; i++)
		for(j = 0; j < 2; j++)
			for(k = 0; k < 8; k++)
				mmg.s[i][j].r[k].ppdr = &proc.pdr[i][j][k];

	proc.mmg = &mmg;
	proc.memops = &memops;
}

void	
sm_reset(mmg_t *m)
{
	sm_mmr0(m);
	sm_mmr3(m);
}

void
sm_mmr0(mmg_t *m)
{
	/*
	 * if mapping is switched on set funcs and compute all registers
 	 */
	if((m->mmr0 & MMR0_ENABLE) != (proc.mmr0 & MMR0_ENABLE)) {
		comp_all(m);
	}
	m->mmr0 = proc.mmr0 & MMR0_ENABLE;
}

void
sm_mmr3(mmg_t *m)
{
	if((m->mmr3 & MMR3_22B) != (proc.mmr3 & MMR3_22B))
		comp_all(m);

	/*
	 * recompute pointers to accessible address spaces 
	 */
	m->a[0][1] = &m->s[0][(proc.mmr3 & MMR3_KSEP) != 0];
	m->a[1][1] = &m->s[1][(proc.mmr3 & MMR3_SSEP) != 0];
	m->a[3][1] = &m->s[3][(proc.mmr3 & MMR3_USEP) != 0];
	m->mmr3 = proc.mmr3;
}


void
sm_par(mmg_t *m, int mode, int space, int reg)
{
	if(m->s[mode][space].r[reg].par != proc.par[mode][space][reg]) {
		m->s[mode][space].r[reg].par = proc.par[mode][space][reg];
		comp_page(m, mode, space, reg);
	}
}


void
sm_pdr(mmg_t *m, int mode, int space, int reg)
{
	if(m->s[mode][space].r[reg].pdr != (proc.pdr[mode][space][reg] & 077416)) {
		m->s[mode][space].r[reg].pdr = proc.pdr[mode][space][reg] & 077416;
		comp_page(m, mode, space, reg);
	}
}

void
sm_ccr(mmg_t *m UNUSED)
{
	/* nothing to do */
}

void
sm_info(mmg_t *m)
{
	u_int i, j, k, n;

	printf("Pointer translated memory\n");
	printf("mmr0:\t%06o (%s)\n", m->mmr0, print_mmr0(m->mmr0));
	printf("mmr3:\t%06o (%s)\n", m->mmr3, print_mmr3(m->mmr3));

	for(i = 0; i < 4; i++)
		for(j = 0; j < 2; j++) {
			for(k = 0; k < 4; k++)
				for(n = 0; n < 2; n++)
					if(m->a[i][j] == &m->s[k][n])
						goto found;
			printf("consistency: a[%d][%d] (%p) points nowhere\n",
				i, j, m->a[i][j]);
			continue;
  found:		printf("a[%d][%d] -> s[%d][%d]\n", i, j, k, n);
		}
	for(i = 0; i < 4; i++)
		for(j = 0; j < 2; j++)
			for(n = 0; n < 8; n++)
				if(m->s_last == &m->s[i][j].r[n]) {
					printf("s_last -> s[%d][%d].r[%d]\n",
						i, j, n);
					goto found1;
				}
  found1:
	if(i >= 4)
		printf("consistency: s_last (%p) points nowhere\n", m->s_last);

	printf("Access flags:\n");
	printf("K/I K/D   S/I S/D   U/I U/D\n");
	for(i = 0; i < 8; i++) {
		for(j = 0; j < 4; j++) {
			if(j == 2)
				continue;
			for(n = 0; n < 2; n++) {
				printf("%c%c  ",
					".R"[m->s[j][n].r[i].r != 0],
					".W"[m->s[j][n].r[i].w != 0]);
			}
			printf("  ");
		}
		printf("\n");
	}
}

/*************************************************************
 *
 * get an u_short* for a physical address for dma
 * functions for monitor and dma support
 */
u_short *
sm_addrphys(mmg_t *m, u_int a)
{
	return &m->mem[Off(a & ~1)];
}

/*************************************************************
 *
 * something changed - recompute pointers
 */
static void
comp_all(mmg_t *m)
{
	int reg;

	for(reg = 0; reg < 8; reg++) {
		comp_page(m, 0, 0, reg);
		comp_page(m, 0, 1, reg);
		comp_page(m, 1, 0, reg);
		comp_page(m, 1, 1, reg);
		comp_page(m, 3, 0, reg);
		comp_page(m, 3, 1, reg);
	}
}

static void
comp_page(mmg_t *m, int mode, int space, int page)
{
	space_t		*s = &m->s[mode][space];
	spacereg_t	*r = &s->r[page];
	u_int	base, low, high, bno;

	if(!(r->r = r->pdr & PDR_RES))
		return;
	r->w = r->pdr & PDR_RW;

	base = r->par << 6;
	bno  = (r->pdr & PDR_LEN) >> (PDR_LEN_SHIFT - 6);
	if(r->pdr & PDR_STACK) {
		high = base + 020000;
		low  = base + bno;
	} else {
		high = base + bno + 0100;
		low  = base;
	}
	r->base = (u_char *)&m->mem[Off(base)];
	r->low  = (u_char *)&m->mem[Off(low)];
	r->high = (u_char *)&m->mem[Off(high)];
}
