
#define MACHDEP

#define MEMDEBUG

#include "unix.h"
#include "extern.h"


/* This is called at the very beginning to initialize everything. */
/* It is the equivalent of main() */

fs_init()
{
#asm 8080
	DI
	LXI	SP, ub? + 512
#endasm

    /* Initialize the interrupt vector */
    initvec();
    initmmu();
    inint = 0;
    udata.u_insys = 1;
    /* Turn off clock */
    out(0,0xf1);
    ei();

    init2();   /* in process.c */
}


/* This checks to see if a user-suppled address is legitimate */
valadr(base,size)
char *base;
uint16 size;
{
    if (base >= udata.u_break && base+size < usp())
    {
	udata.u_error = EFAULT;
	return(0);
    }
    return(1);
}


/* This adds two tick counts together.
The t_time field holds up to one second of ticks,
while the t_date field counts minutes */

addtick(t1,t2)
time_t *t1, *t2;
{

    t1->t_time += t2->t_time;
    t1->t_date += t2->t_date;
    if (t1->t_time >= 60*TICKSPERSEC)
    {
	t1->t_time -= 60*TICKSPERSEC;
	++t1->t_date;
    }
}

incrtick(t)
time_t *t;
{
    if (++t->t_time == 60*TICKSPERSEC)
    {
	t->t_time = 0;
	++t->t_date;
    }
}



tempstack()
{
#asm 8080
	POP	H
	LXI	SP, tstk+100
	PCHL

tstk:	ds 100	;temporary system stack to use during context switches

#endasm
}



initvec()
{
#asm 8080
	PUSH	B
	LXI	H, 0C010H	;Trap table address 0C01000H physical
				;System logical address 1000H
	MVI	C, 6
	LDCTL
	IM3

	lxi	h, ub? + 200
	mov	a,l
	ani	0f0h
	mov	l,a	;Calculate lower stack limit AND FFF0
	mvi	c, 04
	ldctl		; Load stack limit register

	lxi	h,0001h
	mvi	c, 10h
	ldctl		; Turn on stack warning trap

	call	iopgfe?
	mvi	a,0c0h
	out	14h	; Turn on UART receiver interrupt
	call	iopg00?


	; Set up DMA0 for software ready (for pg_copy)
	call	iopgff?
	MVI	C,1FH
	LXI	H,0020H
	OUTW
	call	iopg00?

	POP	B
	RET
#endasm
}


initmmu()
{
#asm 8080
	PUSH	B

	CALL	IOPGFF?
	MVI	A,60
	OUT	0E8H	;TURN OFF REFRESH
	CALL	IOPG00?


; Turn on the cache
	PCACHE
	LXI	H,0020H	;Turn on cache for code only w/o burst (was 0028H)
	MVI	C,12H
	LDCTL


; This loads the system portion of the MMU 
; Set the I/O page register to ffh
	CALL	IOPGFF?

	lxi	h, 08800H  ;Set MMU for user and system translation
	mvi	c, 0f0h
	outw

; Load the MMU PDR pointer (at I/O address FFxxF1h) to 10h
	mvi	a,10h
	out	0f1h

	lxi	h,ptab	;LOAD HL WITH ADDRESS OF PAGE TABLE 

; Do block output to port FFxxF4h to fill descriptors
	mvi	c, 0f4h
	mvi	b, 16
	otirw

; invalidate the user portion of the MMU
	mvi	a,0ch
	out	0f2h

; Set the I/O page register back to 00h
	CALL	IOPG00?
	POP	B
	RET


PTAB:
;	DW	0800ah	
;	DW	0801ah
;	DW	0802ah
;	DW	0803ah
;	DW	0804ah
;	DW	0805ah
;	DW	0806ah
;	DW	0807ah
;	DW	0808ah
;	DW	0809ah
;	DW	080aah
;	DW	080bah
;	DW	080cah
;	DW	080dah
;	DW	080eah
;	DW	080fah

;The system part of the MMU:
	DW	00000h		;First page mapped out to trap nullpointers
	DW	0C01ah
	DW	0C02ah
	DW	0C03ah
	DW	0C04ah
	DW	0C05ah
	DW	0C06ah
	DW	0C07ah
	DW	0C08ah
	DW	0C09ah
	DW	0C0aah
	DW	0C0bah
	DW	0C0c0h		;last 4 pages not used yet, so they are
	DW	0C0d0h		;made available for user processes
	DW	0C0e0h
	DW	0C0f0h


#endasm
}


#asm 8080

IOPEI:	DW	0	;storage for interrupt enable for iopg routines

IOPGFE::
	PUSH	B

	lxi	h,0	;disable interrupts, and store previous status
	push	h
	call	spl?
	pop	d
	shld	iopei

	MVI	L,0FEH
	MVI	C,08
	LDCTL    
	POP	B        
	RET

IOPGFF::
	PUSH	B

	lxi	h,0	;disable interrupts, and store previous status
	push	h
	call	spl?
	pop	d
	shld	iopei

	MVI	L,0FFH
	MVI	C,08
	LDCTL    

	POP	B        
	RET

IOPG00::
	PUSH	B
	MVI	L,00
	MVI	C,08
	LDCTL

	LHLD	iopei	;restore interrupts to saved status
	push	h
	call	spl?
	pop	d

	POP	B
	RET


; This is the common interrupt-scall-trap return routine.
; It restores the registers, and will set up a return to a
; signal handler if necessary.

IRET::
	LDHLSX	14	;Grab MSR we are returning to
	MOV	A,H
	ANI	40H	;Mask off U/S bit
	JNZ	USRRET  ;Handle case of returning into user context different

SYSRET:
.Z80
	POP	IY
	POP	IX
.8080
	POP	H
	POP	D
	POP	B
	POP	PSW
	INX	SP
	INX	SP	;Effectively push off reason code
	RETIL


; For returning into user context, we must check to see if we want
; to call a signal catcher.

USRRET:
	CALL	gettrap?
	JZ	SYSRET		;If no signal handler, proceed normally
	XCHG			;get handler address in DE
	LDHLSX	16		;Get user return address
	XCHG			;orig user addr in DE, handler addr in HL

	STHLSX	16		;put handler address in stack over old user
	XCHG			;get orig user address back in HL

	PUSH	H
	CALL	upush?		;shove on user stack
	POP	H

	CALL	upush?		;shove IY on user stack
	POP	H
	CALL	upush?		;IX
	POP	H
	CALL	upush?		;HL
	POP	H
	CALL	upush?		;DE
	POP	H
	CALL	upush?		;BC
	POP	H
	CALL	upush?		;AF
	POP	H

	LHLD	ub? + ?OSIG	;get udata.u_cursig
	PUSH	H
	CALL	upush?		;shove on user stack as arg to handler
	POP	H

	LXI	H,0000
	SHLD	ub? + ?OSIG  ;zero out udata.u_cursig

	LXI	H, 07FF6H	;get address of special handler return routine
	PUSH	H
	call	upush?
	POP	H
	
	INX	SP
	INX	SP		;pull reason code off stack
	RETIL			;return back into user context to signal
				;handler routine that returns to special
				;user code at 7ff6 (that restores registers)
				;that returns to original return address.


#endasm


clk_int()
{
#asm 8080
	PUSH	PSW
	PUSH	B
	PUSH	D
	PUSH	H
.Z80
	PUSH	IX
	PUSH	IY
.8080

	LDHLSX	14	;Grab MSR we are coming from
	MOV	A,H
	ANI	40H	;Mask off U/S bit
	MOV	H,A
	MVI	L,0
	PUSH	H
	CALL	clkint2?##	;Call clkint2() with User/Sys arg
	POP	D

	JMP	IRET
#endasm

}



bad_int()
{
    panic("Bad interrupt");
}

stk_trap()
{
    trappc();  /* Args are already on stack */
    panic("System stack overflow");
}


ill_trap()
{
#asm 8080
	PUSH	H	;Dummy push because there is no reason code 
	PUSH	PSW
	PUSH	B
	PUSH	D
	PUSH	H
.Z80
	PUSH	IX
	PUSH	IY
.8080
	LDHLSX	14	;Grab MSR we are coming from
	MOV	A,H
	ANI	40H	;Mask off U/S bit
	JNZ	USRILL  ;Handle case of user mode illegal instruction
#endasm

    trap2();  /* Args are already on stack */
    panic("Illegal instruction");

#asm 8080
USRILL:
#endasm

    ssig(udata.u_ptab, SIGILL);   /* Send the SIGILL signal to ourself */
    chksigs();		/* This will exit if appropriate */

#asm 8080
	JMP	IRET	;The IRET routine will call the handler if necessary
#endasm
}


segv_trap()
{
#asm 8080
	PUSH	H	;Dummy push because there is no reason code 
	PUSH	PSW
	PUSH	B
	PUSH	D
	PUSH	H
.Z80
	PUSH	IX
	PUSH	IY
.8080
	LDHLSX	14	;Grab MSR we are coming from
	MOV	A,H
	ANI	40H	;Mask off U/S bit
	JNZ	USRSEG  ;Handle case of user mode illegal segv
#endasm

    trap2();  /* Args are already on stack */
    kprintf("page %d  ", badpage());
    panic("System segv");

#asm 8080
USRSEG:
#endasm

    if (usegv2())	/* Try to read in the page */
    {
	ssig(udata.u_ptab, SIGILL);   /* Send the SIGILL signal to ourself */
	chksigs();		/* This will exit if appropriate */
    }

    ;  /* Dummy statement necessary because of #asm */

#asm 8080
	JMP	IRET	;The IRET routine will call the handler if necessary
#endasm

}


trap2(pc, msr, reason, psw, bc, de, hl, ix, iy)
char *pc;
unsigned msr;
unsigned reason, psw, bc, de, hl, ix, iy;
{
    trappc(pc, msr);
}


trappc(pc, msr)
char *pc;
unsigned msr;
{
    kprintf("%s pc: %x msr: %x\n", (msr&0x4000) ? "user" : "system", pc, msr);
}




/* This returns the faulted page number */
badpage()
{
#asm 8080
	push	b
	call	iopgff?
	mvi	c,0f0h
	inw
	push	h
	call	iopg00?
	pop	h
	mov	a,l
	ani	01fh
	mov	l,a
	mvi	h,0
	pop	b
	ret
#endasm
}


/* This returns the physical address of the base of the page
containing the given user address.  It is in a format that
will allow it to be sent directly to the DMA controller.
A value of -1 means there is no valid address */

padr(user, ladr)
int user;
char *ladr;
{
    if (user)
	return (upadr(ladr));
    else
	return (spadr(ladr));
}
	

upadr()  /* upadr(char *uladr) */
{
#asm 8080
	push 	b
	call	iopgff?
	ldhlsx	4	;get arg
	mov	a,h
.Z80
	rrca
	rrca
	rrca
	rrca
.8080
	ani	0fh    ;get high 4 bits of HL into a
	out	0f1h   ;load page descripter point
	mvi	c,0f5h
	inw		;read page descriptor
	push	h
	call	iopg00?
	pop	h
	mov	a,l
	ani	08h	;test valid bit
	jz	badup   ;jump if not valid
	mov	a,l
	ani	0f0h   ;clear off status bits		
	mov	l,a
	ora	h	;set flags
	pop	b
	ret

badup:
	lxi	h,-1
	mov	a,h
	ora	l
	pop	b
	ret
#endasm
}



/* Same as above, except for system */
spadr()  /* spadr(char *sladr) */
{
#asm 8080
	push 	b
	call	iopgff?
	ldhlsx	4	;get arg
	mov	a,h
.Z80
	rrca
	rrca
	rrca
	rrca
.8080
	ani	0fh    ;get high 4 bits of HL into a
	ori	10h	;adjust value to point into system registers
			;(Only difference from above)
	out	0f1h   ;load page descripter point
	mvi	c,0f5h
	inw		;read page descriptor
	push	h
	call	iopg00?
	pop	h
	mov	a,l
	ani	08h	;test valid bit
	jz	badsp   ;junp if not valid
	mov	a,l
	ani	0f0h   ;clear off status bits		
	mov	l,a
	ora	h	;set flags
	pop	b
	ret

badsp:
	lxi	h,-1
	mov	a,h
	ora	l
	pop	b
	ret
#endasm
}



doexec()
{

#asm 8080
	DI
    	POP	H
	POP	H	;get argument
	LDUSP		;set user stack pointer
	LXI	SP, ub? + 512  ;reset system stack pointer
	XRA	A
	STA	ub? + ?OSYS

	LXI	H, 100H		;program entry point is at 100h
	PUSH	H
	LXI	H, 0507FH	;MSR value: user mode, break on halt,
				;interrupts on
	PUSH	H
	RETIL			;"return" into user process at 100H
#endasm
}


usp()
{
#asm 8080
	RDUSP  ;Get user stack pointer into HL
	MOV	A,H
	ORA	L
#endasm
}

msr()
{
#asm 8080
	PUSH	B
	MVI	C,00
	RDCTL
	POP	B
	MOV	A,H
	ORA	L
#endasm
}



gettrap()
{
    int (*curvec)();

    /* Get the vector for a pending caught signal, if any. */

    if (udata.u_cursig)
    {
        curvec = udata.u_sigvec[udata.u_cursig];
	udata.u_sigvec[udata.u_cursig] = SIG_DFL;   /* Reset to default */
	return (curvec);
    } 
    return (NULL);
}



/* Port addresses of clock chip registers. */

#define SECS 0xe2
#define MINS 0xe3
#define HRS 0xe4
#define DAY 0xe6
#define MON 0xe7
#define YEAR 90

sttime()
{
    panic("Calling sttime");
}


rdtime(tloc)
time_t *tloc;
{
    register is;

    is = spl(0);
    tloc->t_time = tod.t_time;
    tloc->t_date = tod.t_date;
    spl(is);
}


/* Update global time of day */
rdtod()
{
    tod.t_time = (tread(SECS)>>1) | (tread(MINS)<<5) | (tread(HRS)<<11);
    tod.t_date = tread(DAY) | (tread(MON)<<5) | (YEAR<<9);
}


/* Read BCD clock register, convert to binary. */
tread(port)
uint16 port;
{
    int n;

    n = in(port);
    return ( 10*((n>>4)&0x0f) + (n&0x0f) );
}


di()  /* Same as spl(0) */
{
#asm 8080
	DI
#endasm
}


ei()    /* Same as spl(0x7f) */
{
#asm 8080
	EI
#endasm
}


/* This sets the interrupt enable bits to the new value, and returns the old*/
spl()   /* oldbits = spl(newbits) */
{
#asm 8080
	push	b

	ldhlsx  4	;get bits
	mov	e,l	;save new bits in e
	mvi	c,0
	rdctl
	mov	d,l	;save original bits in d

	mov	a,e
	ani	7fh
	mov	l,a	;replace interrupt bits with new ones in hl
	
	mvi	c,0	;reload MSR
	ldctl	

	mvi	h,0	;return original bits
	mov	a,d
	ani	7fh
	mov	l,a

	pop	b
#endasm
}



/* This shifts an unsigned int right 8 places. */

shift8()
{
#asm 8080
	POP	D  	;ret addr
	POP	H
	MOV	L,H
	MVI	H,0
	MOV	A,L
	ANA	A	;set Z flag on result
	PUSH	H
	PUSH	D	;restore stack
#endasm
}



/* This is called to service a user segv trap */
usegv2()
{
    unsigned pageno;

    pageno = badpage();  /* Get the page number from the MMU (0 to 15) */

    if (pageno >= 16)
	panic("User segv of system page");

#ifdef MEMDEBUG
    kprintf("Proc %d has user-induced page %d fault: ",
	 udata.u_ptab->p_pid, pageno);
#endif

    return(getpag(pageno));
}


/* This tries to load the page for the given user address.
If this is possible (or it is already loaded), it returns zero.
Otherwise, the address must be illegal, and nonzero is returned */

upage(uptr)
char  *uptr;
{
#ifdef MEMDEBUG
    kprintf("Proc %d has system-induced page %d fault: ",
	 udata.u_ptab->p_pid, (unsigned)uptr>>12);
#endif

    return (getpag((unsigned)uptr >> 12));
}


/* Used for current "date" for setting pg_age. */
unsigned age; 


/* This gets a free page, sets its reference count to 1, and returns
its descriptor address */

pg_descr *
pg_alloc()
{

    unsigned bestage;
    pg_descr *pg, *bestpg;

    bestpg = NULL;
    bestage = 0;

    /* Try to find an unused page we can just take */
    for (pg=pgtab; pg < pgtab+npages; ++pg)
    {
	if (pg->pg_refs == 0)
	{
	    bestpg = pg;
	    goto found;
	}
    }


    /* Try to find an page with only one ref */
    bestpg = NULL;
    bestage = 0;
    for (pg=pgtab; pg < pgtab+npages; ++pg)
    {
	if (pg->pg_refs == 1 && (age - pg->pg_age ) >= bestage)
	{
	    bestage = age - pg->pg_age;
	    bestpg = pg;
	}
    }

    if (bestpg)
	goto found;


    /* Try to find any page */
    bestpg = NULL;
    bestage = 0;
    for (pg=pgtab; pg < pgtab+npages; ++pg)
    {
	if ((age - pg->pg_age ) >= bestage)
	{
	    bestage = age - pg->pg_age;
	    bestpg = pg;
	}
    }

    ifnot(bestpg)
	panic("No pages");


found:
    pg_out(bestpg);

#ifdef MEMDEBUG
    kprintf("pg_alloc returning %d-refed page %x\n", bestpg->pg_refs, bestpg->pg_addr);
#endif

    bestpg->pg_refs = 1;
    pg_zero(bestpg->pg_addr);
    bestpg->pg_age = ++age;
    bestpg->pg_addr &= ~0x000f;
    bestpg->pg_addr |= 0x0a;   /* Set various page status bits */

    return (bestpg);

}



/* This looks through all the processes' page tables,
and swaps out every instance of the given page. */

pg_out(pg)
pg_descr *pg;
{
    ptptr p;
    pageaddr *pages;
    int pnum;

    for(p=ptab;p < ptab+PTABSIZE; ++p)
    {
	if (p->p_status == P_EMPTY)
	    continue;

	if (p == udata.u_ptab)
	    pages = udata.u_page;   /* Current process */
	else
	    pages = p->p_ublk->u_d.u_page;

	for (pnum=0; pnum < 16; ++pnum)
	{
	    if (pg == pages[pnum])
	    {
#ifdef MEMDEBUG
    kprintf("Process %d has page %d addr %x taken\n", p->p_pid, pnum, pg->pg_addr);
#endif
		/* Swap out if modified */
/* TEMP!!!		if (pages[pnum] & 0x01)   /* Modified bit */
		    pagewrite(SWAPDEV, p->p_swap + (8*pnum), pg->pg_addr);

		pages[pnum] = OUTPAGE;
		if (p == udata.u_ptab)
		    setmmu(pnum, 0);  /* Current process */
	    }

	}
    }
}



/* This increments the reference count of a page */
pg_realloc(pg)
pg_descr *pg;
{
    ++pg->pg_refs;
}



/* This decreases the reference count of a page,
possibly freeing it. */

pg_free(pg)
pg_descr *pg;
{
    if (!pg->pg_refs)
	panic("pg_free");
    return(--(pg->pg_refs));
}


/* This copies the contents of one page to another, using the DMA */

pg_copy()	/* pg_copy(pageaddr src, pageaddr dest)  */
{
#asm 8080
	push	b

	ldhlsx	4	;get dest
	shld	dstpg   ;put into dma program
	ldhlsx	6	;get source
	shld	srcpg	;put into dma program

	MVI	A,00	;use DMA0
	LXI	H,CPBLK

	;SEND PROGRAM TO DMA CONTROLLER
	call	dodma

	pop	b
	RET

CPBLK:
	DW	0000
DSTPG:	DW	0000
	DW	0000	
SRCPG:	DW	0000	
	DW	1000H / 2	 ;length in 16-bit words
	DW	08300H		;continous, memory++ -> memory++, 16-bit

#endasm
}



pg_zero()	/* pg_zero(pageaddr pg)  */
{
#asm 8080
	push	b

	ldhlsx	4	;get page addr
	shld	zerpg   ;put into dma program

	lxi	h,zeroa
	inx	h
	mov	a,l
	ani	0feh
	mov	l,a	;generate aligned word address into zeroa
	shld	zrsrc
	push	h
	call	spadr?
	pop	d
	shld	zrsrc+2  ;generate and store high order part of zero address

	MVI	A,00	;use DMA0
	LXI	H,ZRBLK

	;SEND PROGRAM TO DMA CONTROLLER
	call	dodma

	pop	b
	RET

ZRBLK:
	DW	0000
ZERPG:	DW	0000
ZRSRC:	DW	0000	
	DW	0000	
	DW	1000H / 2	 ;length in 16-bit words
	DW	0A300H		;continous, memory -> memory++, 16-bit

ZEROA:	DB	0,0,0		;memory location containing zero

#endasm
}



#asm 8080

; This loads dma program in (hl) into dma controller in a:
DODMA::
	PUSH	H
	PUSH	PSW
	CALL	IOPGFF?    ;This disables interrupts
	POP	PSW
	POP	H
	MOV	C,A
	MVI	B,6
..LOOP:	OUTIW
	INR	C
	INR	B
	DCR	B
	JNZ	..LOOP
	CALL	IOPG00?		;This restores interrupts
	RET

#endasm



/* This tries to load the page for the given user page number (0 to 15).
If this is possible (or it is already loaded), it returns zero.
Otherwise, the address must be illegal, and nonzero is returned */

getpag(pnum)
int pnum;
{
    pg_descr *pg, *newpg;

    pg = udata.u_page[pnum];
    if (pg == NOPAGE)
    {
#ifdef MEMDEBUG
	kprintf("Page %d not in address space\n", pnum);
#endif
	return (-1);

    }
    else if (pg == OUTPAGE)
    {
	pg = pg_alloc();
#ifdef MEMDEBUG
	kprintf("Swapped out page read into address %x\n", pg->pg_addr);
#endif
	pageread(SWAPDEV, udata.u_ptab->p_swap + (8*pnum), pg->pg_addr);
	udata.u_page[pnum] = pg;
	setmmu(pnum, pg->pg_addr | 0x0a);
    }
    else if (pg == VIRGPAGE)
    {
	pg = pg_alloc();
#ifdef MEMDEBUG
	kprintf("Virgin page given address %x\n", pg->pg_addr);
#endif
	udata.u_page[pnum] = pg;
	setmmu(pnum, pg->pg_addr | 0x0b);  /* Page is pre-marked as modified */
    }
    else
    {
	/* See if the page is write-protected, which implies
	copy-on-write (after forking) */

 	if (pg->pg_addr & 0x04)
	{
	
	    /* If the page is copy-on-write, but there are no other refs
		to it, panic. */
	    if (pg->pg_refs == 1)
	    {
		panic("R/O page for no reason");
	    }
	    else
	    {
		newpg = pg_alloc();
		pg_copy(pg->pg_addr, newpg->pg_addr);
#ifdef MEMDEBUG
		kprintf("COW page %x copied to %x\n", pg->pg_addr, newpg->pg_addr);
#endif
		/* TODO: This marks the copied page as modified, but it should
		   inherit the modified bit from the old page */
		udata.u_page[pnum] = newpg;
		setmmu(pnum, newpg->pg_addr | 0x0b);

		/* If there is only one more reference to this COW page,
		    make it normal (R/W).  */

		if (--pg->pg_refs == 1)   /* Deref page */
		    pg->pg_addr &= ~0x04;

	    }
	}
	else
	{
#ifdef MEMDEBUG
	    kprintf("Valid page with address %x reloaded into MMU\n",
		     pg->pg_addr);
#endif
	    setmmu(pnum, pg->pg_addr);
	}
    }

    return (0);
}


/* This sets the user mmu entry pgnum to value */
setmmu()  /* setmmu(int pgnum, unsigned value) */
{
#asm 8080
	push	b
	call iopgff

	ldhlsx	6	;get pgnum
	mov	a,l
	out	0f1h	;set the PDR pointer in the MMU to the page number

	ldhlsx  4	;get value
	mvi	c,0f5h
	outw		;write value to I/O address f5 to put it into the MMU

	call	iopg00?
	pop	b
#endasm
}

/* This gets the user mmu entry pgnum */
getmmu()  /* getmmu(int pgnum) */
{
#asm 8080
	push	b
	call iopgff

	ldhlsx	4	;get pgnum
	mov	a,l
	out	0f1h	;set the PDR pointer in the MMU to the page number

	mvi	c,0f5h
	inw		;read value from I/O address f5 

	push	h
	call	iopg00?
	pop	h
	mov	a,h
	ora	l
	pop	b
#endasm
}




/* This reads all 16 user MMU entries to the given addresses */
rdmmu(addr)
pg_descr *addr[];
{
    register j;

    for (j=0; j<16; ++j)
	if (realpage(addr[j]))
	    addr[j]->pg_addr = getmmu(j);
}


/* This loads all 16 user MMU entries from the given addresses */
ldmmu(addr)
pg_descr *addr[];
{
    register j;

    for (j=0; j<16; ++j)
	if (realpage(addr[j]))
	    setmmu(j, addr[j]->pg_addr);
	else
	    setmmu(j, 0);
}


/* This invalidates the user part of the MMU */
clrmmu()
{
#asm 8080
	push	b
	call	iopgff
	mvi	a,0ch
	out	0f2h
	call	iopg00?
	pop	b
#endasm
}



ufault()
{
    udata.u_error = EFAULT;
}


/* These read and write data from/to the user address space */

ugetw(uptr)
char  *uptr;
{
    int w;

    uget(uptr, &w, sizeof(int));
    return (w);
}

uputw(w, uptr)
int w;
char  *uptr;
{
    uput(&w, uptr, sizeof(int));
}


uputc(c, uptr)
int c;
char  *uptr;
{
    uput(&c, uptr, 1);
}


ugetc(uptr)
char  *uptr;
{
    char c;

    uget(uptr, &c, 1);
    return (c);
}


uget()  /* uget(char *uptr, char *sptr, unsigned nbytes) */
{
#asm 8080
	push	b
	ldhlsx  4	;get count
	mov	b,h
	mov	c,l	;put into bc
	ldhlsx	6	;get dest
	xchg		;put into de
	ldhlsx  8	;get source into hl

ugetlp:
	ldud		;load a with (hl) from user data space
	cc	ulflt	;call if access violation occurred
	stax	d	;store in system space
	inx	d
	inx	h
	dcx	b
	mov	a,b
	ora	c
	jnz	ugetlp
	lxi	h,0000
	xra	a	;return 0 if OK
	pop	b
	ret
#endasm
}


/* See if a portion of user space is accessable */
utest()  /* utest(char *uptr, unsigned nbytes) */
{
#asm 8080
	push	b
	ldhlsx  4	;get count
	mov	b,h
	mov	c,l	;put into bc
	ldhlsx  6	;get source into hl

utstlp:
	ldud		;load a with (hl) from user data space
	cc	ulflt	;call if access violation occurred
	inx	h
	dcx	b
	mov	a,b
	ora	c
	jnz	utstlp
	lxi	h,0000
	xra	a	;return 0 if OK
	pop	b
	ret
#endasm
}


/* This will not set EFAULT if there is a fault */
uzero()  /* uzero(char *uptr, unsigned count) */
{
#asm 8080
	push	b
	ldhlsx  4	;get count
	mov	b,h
	mov	c,l	;put into bc
	ldhlsx	6	;get dest

uzlp:
	xra	a
	stud		;load (hl) in user data space with 0
	inx	h
	dcx	b
	mov	a,b
	ora	c
	jnz	uzlp

	pop	b
	ret
#endasm
}


/* Get a string upto count */
ugets()  /* ugets(char *uptr, char *sptr, unsigned count) */
{
#asm 8080
	push	b
	ldhlsx  4	;get count
	mov	b,h
	mov	c,l	;put into bc
	ldhlsx	6	;get dest
	xchg		;put into de
	ldhlsx  8	;get source into hl

ugslp:
	ldud		;load a with (hl) from user data space
	cc	ulflt	;call if access violation occurred
	stax	d	;store in system space
	ana	a
	jz	ugsdn   ;exit if hit null
	inx	d
	inx	h
	dcx	b
	mov	a,b
	ora	c
	jnz	ugslp
ugsdn:	lxi	h,0000
	xra	a	;return 0 if OK
	pop	b
	ret
#endasm
}


uput()  /* uput(char *sptr, char *uptr, unsigned nbytes) */
{
#asm 8080
	push	b
	ldhlsx  4	;get count
	mov	b,h
	mov	c,l	;put into bc
	ldhlsx	8	;get source
	xchg		;put into de
	ldhlsx  6	;get dest into hl

uputlp:
	ldax	d	;get byte from system area
	stud		;store byte into (hl) in user data space
	cc	usflt	;call if access violation occurred
	inx	d
	inx	h
	dcx	b
	mov	a,b
	ora	c
	jnz	uputlp
	lxi	h,0000
	xra	a	;return 0 if OK
	pop	b
	ret
#endasm
}


/* Push something onto user stack */

upush()	  /* upush(int val); */
{
#asm 8080
	PUSH	B

	LDHLSX	4	;get arg
	XCHG		;into DE
	
	RDUSP  		;Get user stack pointer into HL
	DCX	H
	MOV	A,D
	STUD
	cc	usflt	;call if access violation occurred
	DCX	H
	MOV	A,E	;shove DE onto user stack
	STUD
	cc	usflt	;call if access violation occurred

	LDUSP		;update user SP

	POP	B
	RET
#endasm
}


/* Ulflt or usflt are called when one of the routines above has an
access violation. They try to read in the faulted page.  If this works,
a stud or ldud are re-excuted, and they return.  Otherwise they return
to the next higher level calling routine with a failure code. */

#asm 8080
ulflt:
	push	b
	push	d
	push	h
	call	upage?	;Call upage() with HL as argument
	pop	h
	pop	d
	pop	b
	jz	ulfok
	;this was an illegal address
	pop	h	;pop off this level's return address
	call	ufault?
	lxi	h,0001
	mov	a,h
	ora	l	;return 1 if fail
	pop	b
	ret
ulfok:
	ldud		;re-execute the faulted ldud
	ret


usflt:
	push	psw
	push	b
	push	d
	push	h
	call	upage?	;Call upage() with HL as argument
	pop	h
	pop	d
	pop	b
	jz	usfok
	pop	psw	;pop after conditional jump because of flags
	;this was an illegal address
	pop	h	;pop off this level's return address
	call	ufault?
	lxi	h,0001
	mov	a,h
	ora	l	;return 1 if fail
	pop	b
	ret
usfok:
	pop	psw	;get back accumulator
	stud		;re-execute the faulted stud
	ret

#endasm



abort()
{
	di();
	for (;;) ;
}


/* This prints an error message and dies. */

panic(s)
char *s;
{
    di();
    kprintf("PANIC: %s\n",s);
    idump();
    abort();
}


warning(s)
char *s;
{
    kprintf("WARNING: %s\n",s);
}


puts(s)
char *s;
{
    while (*s)
	kputchar(*(s++));
}

kputchar(c)
int c;
{
    if (c == '\n')
	_putc(1, '\r');
    _putc(1, c);
    if (c == '\t')
        puts("\177\177\177\177\177\177\177\177\177\177");
}



idump()
{
    inoptr ip;
    ptptr pp;
    extern struct cinode i_tab[];

/*****

    kprintf(
        "\tMAGIC\tDEV\tNUM\tMODE\tNLINK\t(DEV)\tREFS\tDIRTY root %d\n",
            root - i_tab);

    for (ip=i_tab; ip < i_tab+ITABSIZE; ++ip)
    {
	ifnot (ip->c_magic)	
	    continue;

	kprintf("%d\t%d\t%d\t%u\t0%o\t%d\t%d\t%d\t%d\n",
	       ip-i_tab, ip->c_magic,ip->c_dev, ip->c_num,
	       ip->c_node.i_mode,ip->c_node.i_nlink,ip->c_node.i_addr[0],
	       ip->c_refs,ip->c_dirty);
    }

****/

    kprintf("\n\tSTAT\tWAIT\tPID\tPPTR\tALARM\tPENDING\tIGNORED\n");
    for (pp=ptab; pp < ptab+PTABSIZE; ++pp)
    {
	kprintf("%d\t%d\t0x%x\t%d\t%d\t%d\t0x%x\t0x%x\n",
	       pp-ptab, pp->p_status, pp->p_wait,  pp->p_pid,
	       pp->p_pptr-ptab, pp->p_alarm, pp->p_pending,
		pp->p_ignored);
        ifnot(pp->p_pptr)
	    break;
    }	
    

/*    bufdump(); */

    kprintf("\ninsys %d ptab %d call %d cwd %d sp 0x%x inint %d\n",
	udata.u_insys,udata.u_ptab-ptab, udata.u_callno, udata.u_cwd-i_tab,
       udata.u_ssp, inint);
}



/* Short version of printf to save space */
kprintf(nargs)
	{
	register char **arg, *fmt;
	register c, base;
	char s[7], *itob();

	arg = (char **)&nargs + nargs;
	fmt = *arg;
	while (c = *fmt++) {
		if (c != '%') {
			kputchar(c);
			continue;
			}
		switch (c = *fmt++) {
		case 'c':
			kputchar(*--arg);
			continue;
		case 'd':
			base = -10;
			goto prt;
		case 'o':
			base = 8;
			goto prt;
		case 'u':
			base = 10;
			goto prt;
		case 'x':
			base = 16;
		prt:
			puts(itob(*--arg, s, base));
			continue;
		case 's':
			puts(*--arg);
			continue;
		default:
			kputchar(c);
			continue;
			}
		}
	}
