#include "sys/param.h"
#include "sys/config.h"
#include "sys/mmu.h"
#include "sys/types.h"
#include "sys/sysmacros.h"
#include "sys/systm.h"
#include "sys/sysinfo.h"
#include "sys/callo.h"
#include "sys/dir.h"
#include "sys/signal.h"
#include "sys/user.h"
#include "sys/proc.h"
#include "sys/text.h"
#include "sys/ipc.h"
#include "sys/shm.h"
#include "sys/psl.h"
#include "sys/var.h"
#include "sys/seg.h"
#include "sys/context.h"
#include "sys/map.h"
#include "sys/errno.h"
#include "sys/scat.h"

typedef int	mem_t;
short	segoff;		/* mmu segment offset */
extern struct shmid_ds	*shm_shmem[];	/* ptrs to attached segments */
extern struct shmpt_ds	shm_pte[];	/* segment attach points */
extern struct shminfo	shminfo;	/* shared memory info structure */

/* #define DUMPMM */
/* #define HOWFAR */
/* #define TRACEALL */

/*
 * Load the user hardware page map.
 */
sureg()
{
	register struct user *up;
	register struct phys *ph;
	register struct shmid_ds *sp;
	register short *addr;
	register a, i, j, page;
	struct text *tp;
	struct proc *p;

	up = &u;
	p = up->u_procp;
	tp = p->p_textp;
#ifdef HOWFAR
printf("sureg:p_addr=0x%x, tsize=%d dsize=%d ssize=%d\n",
	p->p_addr, up->u_ptsize, up->u_pdsize, up->u_pssize);
#endif
	SEG1_1 = 1;
	/* SEG2_0 = 1; */
	clearmmu();

	addr = (short *)vtoseg(v.v_ustart);
	if (tp != NULL) {
		page = tp->x_caddr + segoff;
		/* map a max of NPAGEPERSEG (256) 512-byte pages per segment */
		for (i = up->u_ptsize; i > 0; i -= a) {
			a = min(NPAGEPERSEG, (unsigned)i);
			setmmu((short *)((int)addr | ACCSEG), page);
			setmmu((short *)((int)addr | ACCLIM),
				((up->u_xrw==RO)?ASRO:ASRW) | ((256-a) & 0xFF));
			page += a;
			addr = (short *)((long)addr + (1 << SEGSHIFT));
		}
		addr=(short *)vtoseg(ctob(stoc(ctos(btoc(v.v_ustart)+up->u_ptsize))));
	} else
		if (up->u_ptsize != 0)
			addr = (short *)((long)addr +
				ctob(stoc(ctos(up->u_ptsize))));

		/* set up data segment */
	page = p->p_addr + v.v_usize + segoff;
	/* map a max of NPAGEPERSEG (256) 512-byte pages per segment */
	for (i = up->u_pdsize; i > 0; i -= a) {
		a = min(NPAGEPERSEG, (unsigned)i);
		setmmu((short *)((int)addr | ACCSEG), page);
		setmmu((short *)((int)addr | ACCLIM), ASRW | ((256-a) & 0xFF));
		page += a;
		addr = (short *)((long)addr + (1 << SEGSHIFT));
	}

		/* set up stack segment */
	addr = (short *)(vtoseg(v.v_uend));
	page += up->u_pssize;		/* stack is right after data */
	for (i = up->u_pssize; i > 0; i -= a) {
		addr = (short *)((long)addr - (1 << SEGSHIFT));
		a = min(NPAGEPERSEG, (unsigned)i);
		page -= NPAGEPERSEG;
		setmmu((short *)((int)addr | ACCSEG), page);
		setmmu((short *)((int)addr | ACCLIM), ASRWS | (a-1));
	}

		/* set up phys() */
	for (ph = &u.u_phys[0]; ph < &u.u_phys[v.v_phys]; ph++) {
	    if (ph->u_phsize) {
		page = (ph->u_phpaddr >> PAGESHIFT) + segoff;
		addr = (short *)vtoseg(ph->u_phladdr);
		for (i = ph->u_phsize; i > 0; i -= a) {
			a = min(NPAGEPERSEG, (unsigned)i);
			/* if ((getmmu(vtoseg(addr)|ACCLIM)&PROTMASK) == ASINVAL) { */
			if ((getmmu((short *)((int)addr | ACCLIM))&PROTMASK) == ASINVAL) {
				setmmu((short *)((int)addr | ACCSEG), page);
				setmmu((short *)((int)addr | ACCLIM), ASRW | ((256-a) & 0xFF));
			}
			page += a;
			addr = (short *)((long)addr + (1 << SEGSHIFT));
		    }
		/*dumpmm1(1);	/**** DEBUG ****/
	    }
	}

		/* set up shared memory */
	for (i = (p - proc) * shminfo.shmseg; /* index of first shm_shmem[] */
	     i < ((p - proc) + 1) * shminfo.shmseg; i++) {
		sp = shm_shmem[i];
		if (sp == NULL)
			/* no more shared mem segments this process */
			continue;
		/* shm_scat is starting physical click number */
		page = sp->shm_scat + segoff;
		addr = (short *)vtoseg(shm_pte[i].shm_segbeg);
		for (j = btoc(sp->shm_segsz); j > 0; j -= a) {
			a = min(NPAGEPERSEG, (unsigned)j);
			if ((getmmu((short *)((int)addr | ACCLIM))&PROTMASK) == ASINVAL) {
				setmmu((short *)((int)addr | ACCSEG), page);
				setmmu((short *)((int)addr | ACCLIM),
						ASRW | ((256-a) & 0xFF));
			}
			page += a;
			addr = (short *)((long)addr + (1 << SEGSHIFT));
		}
	}
	
}

setmmu(addr, data)
register short *addr;
register data;
{
	int s;

#ifdef TRACEALL
#ifdef HOWFAR
if (data != ASINVAL)
if (((int)addr & ACCSEG) == ACCSEG)
	printf("setmmu:addr=0x%x, data=0x%x (0x%x)\n", addr, data, data<<9);
else
	printf("setmmu:addr=0x%x prot=0x%x, length=0x%x (%d)\n",
		addr, data & PROTMASK, data, 256 - (data & 0xFF));
#endif
#endif
	s = spl7();
	SETUP_1 = 1;
	*addr = data;
	SETUP_0 = 1;
	splx(s);
}

getmmu(addr)
register short *addr;
{
	register data;
	int s;

	s = spl7();
	SETUP_1 = 1;
	data = *addr;
	SETUP_0 = 1;
	splx(s);
	data &= 0xFFF;
#ifdef TRACEALL
#ifdef HOWFAR
if (((int)addr & ACCSEG) == ACCSEG)
	printf("getmmu:addr=0x%x, data=0x%x (0x%x)\n", addr, data, data<<9);
else
	printf("getmmu:addr=0x%x prot=0x%x, length=0x%x (%d)\n",
		addr, data & PROTMASK, data, 256 - (data & 0xFF));
#endif
#endif
	return(data);
}

clearmmu()
{
	register data = 0xC00;		/* ASINVAL (d7) */
	register inc = 0x20000;		/* address increment (d6) */
	register s = 0;			/* saved priority (d5) */
	register short i = 32-1;	/* loop counter (d4) */
	register char *addr = (char *)0x8000;	/* address ACCLIM (a5) */

#ifdef lint
	*addr = (char)i;
	*addr = (char)inc;
	*addr = (char)data;
#endif
	s = spl7();
	SETUP_1 = 1;
	asm("loop:");
	asm("	movw	d7,a5@");
	asm("	addl	d6,a5");
	asm("	movw	d7,a5@");
	asm("	addl	d6,a5");
	asm("	movw	d7,a5@");
	asm("	addl	d6,a5");
	asm("	movw	d7,a5@");
	asm("	addl	d6,a5");
	asm("	dbra	d4,loop");
	SETUP_0 = 1;
	splx(s);
}

/*
 * In V7, Set up software prototype segmentation
 * registers to implement the 3 pseudo
 * text,data,stack segment sizes passed
 * as arguments.
 * The argument sep specifies if the
 * text and data+stack segments are to
 * be separated.
 * The last argument determines whether the text
 * segment is read-write or read-only.
 *
 * u.u_ptsize etc replace the proto entries on the pdp11.  They
 * are used by sureg to set up the page map.
 */
/* ARGSUSED */
estabur(nt, nd, ns, sep, xrw)
unsigned nt, nd, ns;
{
#ifdef HOWFAR
printf("estabur:nt=%d nd=%d ns=%d rw=%d\n", nt, nd, ns, xrw);
#endif
	if (verureg(nt, nd, ns, xrw))
		return(-1);
	sureg();
	return(0);
}

/*
 * verify user registers can be set up
 */
verureg(nt, nd, ns, xrw)
register unsigned nt;
unsigned nd, ns;
{
	register int s;

	/*
	 * check for sufficient number of segment registers
	 */
	if (ctos(nt) + ctos(nd) + ctos(ns) > ctos(btoc(v.v_uend-v.v_ustart)))
		goto bad;

	s = nd + ns + v.v_usize;
	if (nt == 0) {			/* non shared text */
		if (s > maxmem)
			goto bad;
	} else {			/* shared text */
		if (nt + s <= maxmem)	/* text+data can fit in largest hole */
			goto ok;
		goto bad;
	}
ok:
	u.u_ptsize = nt;	/* essentially these pass args to sureg */
	u.u_pdsize = nd;
	u.u_pssize = ns;
	u.u_xrw = xrw;
	return(0);
bad:
#ifdef HOWFAR
printf("verureg failure:nt=%d  nd=%d  ns=%d\n", nt, nd, ns);
#endif
	u.u_error = ENOMEM;
	return(-1);
}

#ifdef DUMPMM

char *mmu_codes[] = {
	"UNPREDICT-0",		/* 0 */
	"UNPREDICT-1",		/* 1 */
	"UNPREDICT-2",		/* 2 */
	"UNDEFINED",		/* 3 */
	"RO stack",		/* 4 */
	"RO",			/* 5 */
	"RW stack",		/* 6 */
	"RW",			/* 7 */
	"UNPREDICT-8",		/* 8 */
	"IO",			/* 9 */
	"UNPREDICT-A",		/* A */
	"UNPREDICT-B",		/* B */
	"INVALID",		/* C */
	"UNPREDICT-D",		/* D */
	"UNPREDICT-E",		/* E */
	"SPIO",			/* F */
};

/*
 * dumpmm(system)
 *	dump the memory management registers
 *	if system is non-zero, also dump system registers
 */
dumpmm(system)
int system;
{
	printf("p_addr=0x%x tsize=0x%x dsize=0x%x ssize=0x%x\n",
		u.u_procp->p_addr, u.u_ptsize, u.u_pdsize, u.u_pssize);
	if (system)
		dumpmm1(0);
	dumpmm1(1);
}

dumpmm1(space)
{
	register i, addr, prot, j, len;

	printf("Context %d mmu registers\n", space);
	printf("seg  logical physical  (clicks)  permission\n");
	for (i = 0; i < 128; i++) {
		if (space == 0)
			SEG1_0 = 1;
		else
			SEG1_1 = 1;
		/* SEG2_0 = 1; */
		addr = getmmu((i << SEGSHIFT) | ACCSEG);
		prot = getmmu((i << SEGSHIFT) | ACCLIM);
		if ((prot & PROTMASK) == ASINVAL)
			continue;
		addr -= segoff;
		len = prot & 0xFF;
		if (prot & 0x100) {		/* data or stack segment */
			j = i << SEGSHIFT;
			len = 256 - len;
			printf("0x%x  0x%x-0x%x 0x%x-0x%x (%d) %s\n", i, j,
				j+ctob(len), addr, addr+len, len,
				mmu_codes[(prot&PROTMASK)>>8]);
		} else {
			len++;
			j = (i+1) << SEGSHIFT;
			addr += NPAGEPERSEG;
			printf("0x%x  0x%x-0x%x 0x%x-0x%x (%d) %s\n", i,
				j-ctob(len), j, addr-len, addr, len,
				mmu_codes[(prot&PROTMASK)>>8]);
		}
	}
}
#endif DUMPMM
/*
 * check the size of a process
 */
chksize(nt, nd, ns)
register unsigned nt, nd, ns;
{
	if (nt + nd + ns + v.v_usize < maxmem )
		return(0);
	u.u_error = ENOMEM;
	return(1);
}
