~kris/9p

9hist

b1beaa16e1e4f6ab50aaf756d291286c407d10e6 — David du Colombier 26 years ago 6480bd3
Plan 9 from Bell Labs 2000-09-04
A bitsy/devuart.c => bitsy/devuart.c +506 -0
@@ 0,0 1,506 @@
#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"../port/error.h"

#include	"../port/netif.h"

enum
{
	Nuart = 1,
	Stagesize= 1024,
};

/* hardware registers */
typedef struct Uartregs Uartregs;
struct Uartregs
{
	ulong	ctl0;
	ulong	ctl1;
	ulong	ctl2;
	ulong	ctl3;
	uchar	dummyd[4];
	ulong	data;
	uchar	dummyf[4];
	ulong	status0;
	ulong	status1;
};
#define	UART3REGS IOA(Uartregs, 0x50000)

/* ctl0 bits */
enum
{
	XmitNotFull = (1 << 2),
};

/* software representation */
typedef struct Uart Uart;
struct Uart
{
	QLock;
	int	opens;

	int	enabled;
	Uart	*elist;			/* next enabled interface */
	char	name[NAMELEN];

	uchar	sticky[8];		/* sticky write register values */
	uchar	osticky[8];		/* kernel saved sticky write register values */
	ulong	port;			/* io ports */
	ulong	freq;			/* clock frequency */
	uchar	mask;			/* bits/char */
	int	dev;
	int	baud;			/* baud rate */

	uchar	istat;			/* last istat read */
	int	frame;			/* framing errors */
	int	overrun;		/* rcvr overruns */

	/* buffers */
	int	(*putc)(Queue*, int);
	Queue	*iq;
	Queue	*oq;

	Lock	flock;			/* fifo */
	uchar	fifoon;			/* fifo's enabled */
	uchar	type;			/* chip version */

	Lock	rlock;			/* receive */
	uchar	istage[Stagesize];
	uchar	*ip;
	uchar	*ie;

	int	haveinput;

	Lock	tlock;			/* transmit */
	uchar	ostage[Stagesize];
	uchar	*op;
	uchar	*oe;

	int	modem;			/* hardware flow control on */
	int	xonoff;			/* software flow control on */
	int	blocked;
	int	cts, dsr, dcd, dcdts;		/* keep track of modem status */ 
	int	ctsbackoff;
	int	hup_dsr, hup_dcd;	/* send hangup upstream? */
	int	dohup;

	int	kinuse;		/* device in use by kernel */

	Rendez	r;
};
static	Uart*	uart[Nuart];
static	int	nuart;

static Dirtab *uartdir;
static int uartndir;

/*
 * means the kernel is using this for debugging output
 */
static char	Ekinuse[] = "device in use by kernel";

/*
 *  default is 9600 baud, 1 stop bit, 8 bit chars, no interrupts,
 *  transmit and receive enabled, interrupts disabled.
 */
static void
uartsetup0(Uart *p)
{
	memset(p->sticky, 0, sizeof(p->sticky));
	/*
	 *  set rate to 9600 baud.
	 *  8 bits/character.
	 *  1 stop bit.
	 *  interrupts enabled.
	 */
//	p->sticky[Format] = Bits8;
//	uartwrreg(p, Format, 0);
//	p->sticky[Mctl] |= Inton;
//	uartwrreg(p, Mctl, 0x0);

//	uartsetbaud(p, 9600);

//	p->iq = qopen(4*1024, 0, uartflow, p);
//	p->oq = qopen(4*1024, 0, uartkick, p);
	if(p->iq == nil || p->oq == nil)
		panic("uartsetup0");

	p->ip = p->istage;
	p->ie = &p->istage[Stagesize];
	p->op = p->ostage;
	p->oe = p->ostage;
}

/*
 *  called by main() to create a new duart
 */
void
uartsetup(ulong port, ulong freq, char *name, int type)
{
	Uart *p;

	if(nuart >= Nuart)
		return;

	p = xalloc(sizeof(Uart));
	uart[nuart] = p;
	strcpy(p->name, name);
	p->dev = nuart;
	nuart++;
	p->port = port;
	p->freq = freq;
	p->type = type;
	uartsetup0(p);
}

static void
uartenable(Uart *p)
{
	USED(p);
}

static void
uartdisable(Uart *p)
{
	USED(p);
}

static long
uartstatus(Chan*, Uart *p, void *buf, long n, long offset)
{
	USED(p);
//		"b%d c%d d%d e%d l%d m%d p%c r%d s%d i%d\n"
//		"dev(%d) type(%d) framing(%d) overruns(%d)%s%s%s%s\n",
	return readstr(offset, buf, n, "");
}

static void
setlength(int i)
{
	Uart *p;

	if(i > 0){
		p = uart[i];
		if(p && p->opens && p->iq)
			uartdir[3*i].length = qlen(p->iq);
	} else for(i = 0; i < nuart; i++){
		p = uart[i];
		if(p && p->opens && p->iq)
			uartdir[3*i].length = qlen(p->iq);
	}
}

static void
uartreset(void)
{
	int i;
	Dirtab *dp;

	nuart = Nuart;

	uartndir = 3*nuart;
	uartdir = xalloc(uartndir * sizeof(Dirtab));
	dp = uartdir;
	for(i = 0; i < nuart; i++){
		/* 3 directory entries per port */
		sprint(dp->name, "eia%d", i);
		dp->qid.path = NETQID(i, Ndataqid);
		dp->perm = 0660;
		dp++;
		sprint(dp->name, "eia%dctl", i);
		dp->qid.path = NETQID(i, Nctlqid);
		dp->perm = 0660;
		dp++;
		sprint(dp->name, "eia%dstat", i);
		dp->qid.path = NETQID(i, Nstatqid);
		dp->perm = 0444;
		dp++;
	}

}


static Chan*
uartattach(char *spec)
{
	return devattach('t', spec);
}

static int
uartwalk(Chan *c, char *name)
{
	return devwalk(c, name, uartdir, uartndir, devgen);
}

static void
uartstat(Chan *c, char *dp)
{
	if(NETTYPE(c->qid.path) == Ndataqid)
		setlength(NETID(c->qid.path));
	devstat(c, dp, uartdir, uartndir, devgen);
}

static Chan*
uartopen(Chan *c, int omode)
{
	Uart *p;

	c = devopen(c, omode, uartdir, uartndir, devgen);

	switch(NETTYPE(c->qid.path)){
	case Nctlqid:
	case Ndataqid:
		p = uart[NETID(c->qid.path)];
		if(p->kinuse)
			error(Ekinuse);
		qlock(p);
		if(p->opens++ == 0){
			uartenable(p);
			qreopen(p->iq);
			qreopen(p->oq);
		}
		qunlock(p);
		break;
	}

	return c;
}

static void
uartclose(Chan *c)
{
	Uart *p;

	if(c->qid.path & CHDIR)
		return;
	if((c->flag & COPEN) == 0)
		return;
	switch(NETTYPE(c->qid.path)){
	case Ndataqid:
	case Nctlqid:
		p = uart[NETID(c->qid.path)];
		if(p->kinuse)
			error(Ekinuse);
		qlock(p);
		if(--(p->opens) == 0){
			uartdisable(p);
			qclose(p->iq);
			qclose(p->oq);
			p->ip = p->istage;
			p->dcd = p->dsr = p->dohup = 0;
		}
		qunlock(p);
		break;
	}
}

static long
uartread(Chan *c, void *buf, long n, vlong off)
{
	Uart *p;
	ulong offset = off;

	if(c->qid.path & CHDIR){
		setlength(-1);
		return devdirread(c, buf, n, uartdir, uartndir, devgen);
	}

	p = uart[NETID(c->qid.path)];
	if(p->kinuse)
		error(Ekinuse);
	switch(NETTYPE(c->qid.path)){
	case Ndataqid:
		return qread(p->iq, buf, n);
	case Nctlqid:
		return readnum(offset, buf, n, NETID(c->qid.path), NUMSIZE);
	case Nstatqid:
		return uartstatus(c, p, buf, n, offset);
	}

	return 0;
}

static void
uartctl(Uart *p, char *cmd)
{
	int i, n;
	char *f[32];
	int nf;

	/* let output drain for a while */
	for(i = 0; i < 16 && qlen(p->oq); i++)
		tsleep(&p->r, (int(*)(void*))qlen, p->oq, 125);

	nf = getfields(cmd, f, nelem(f), 1, " \t\n");

	for(i = 0; i < nf; i++){

		if(strncmp(f[i], "break", 5) == 0){
//			uartbreak(p, 0);
			continue;
		}

		n = atoi(f[i]+1);
		switch(*f[i]){
		case 'B':
		case 'b':
//			uartsetbaud(p, n);
			break;
		case 'C':
		case 'c':
//			uartdcdhup(p, n);
			break;
		case 'D':
		case 'd':
//			uartdtr(p, n);
			break;
		case 'E':
		case 'e':
//			uartdsrhup(p, n);
			break;
		case 'f':
		case 'F':
			qflush(p->oq);
			break;
		case 'H':
		case 'h':
			qhangup(p->iq, 0);
			qhangup(p->oq, 0);
			break;
		case 'i':
		case 'I':
//			lock(&p->flock);
//			uartfifo(p, n);
//			unlock(&p->flock);
			break;
		case 'L':
		case 'l':
//			uartbits(p, n);
			break;
		case 'm':
		case 'M':
//			uartmflow(p, n);
			break;
		case 'n':
		case 'N':
			qnoblock(p->oq, n);
			break;
		case 'P':
		case 'p':
//			uartparity(p, *(cmd+1));
			break;
		case 'K':
		case 'k':
//			uartbreak(p, n);
			break;
		case 'R':
		case 'r':
//			uartrts(p, n);
			break;
		case 'Q':
		case 'q':
			qsetlimit(p->iq, n);
			qsetlimit(p->oq, n);
			break;
		case 'T':
		case 't':
//			uartdcdts(p, n);
			break;
		case 'W':
		case 'w':
			/* obsolete */
			break;
		case 'X':
		case 'x':
			ilock(&p->tlock);
			p->xonoff = n;
			iunlock(&p->tlock);
			break;
		}
	}
}

static long
uartwrite(Chan *c, void *buf, long n, vlong)
{
	Uart *p;
	char cmd[32];

	if(c->qid.path & CHDIR)
		error(Eperm);

	p = uart[NETID(c->qid.path)];
	if(p->kinuse)
		error(Ekinuse);

	switch(NETTYPE(c->qid.path)){
	case Ndataqid:
		return qwrite(p->oq, buf, n);
	case Nctlqid:
		if(n >= sizeof(cmd))
			n = sizeof(cmd)-1;
		memmove(cmd, buf, n);
		cmd[n] = 0;
		uartctl(p, cmd);
		return n;
	}
}

static void
uartwstat(Chan *c, char *dp)
{
	Dir d;
	Dirtab *dt;

	if(!iseve())
		error(Eperm);
	if(CHDIR & c->qid.path)
		error(Eperm);
	if(NETTYPE(c->qid.path) == Nstatqid)
		error(Eperm);

	dt = &uartdir[3 * NETID(c->qid.path)];
	convM2D(dp, &d);
	d.mode &= 0666;
	dt[0].perm = dt[1].perm = d.mode;
}

Dev uartdevtab = {
	't',
	"uart",

	uartreset,
	devinit,
	uartattach,
	devclone,
	uartwalk,
	uartstat,
	uartopen,
	devcreate,
	uartclose,
	uartread,
	devbread,
	uartwrite,
	devbwrite,
	devremove,
	uartwstat,
};

void
putuartstr(char *str)
{
	Uartregs *ur;

	ur = UART3REGS;
	while(*str){
		/* wait for output ready */
		while(ur->status1 & XmitNotFull)
			;
		ur->data = *str++;

	}
}

M bitsy/fns.h => bitsy/fns.h +1 -0
@@ 15,6 15,7 @@ int	iprint(char*, ...);
#define	procrestore(p)
void	procsave(Proc*);
void	procsetup(Proc*);
void	putuartstr(char*);
void	screeninit(void);
int	screenprint(char*, ...);			/* debugging */
void	(*screenputs)(char*, int);

M bitsy/io.h => bitsy/io.h +5 -112
@@ 1,112 1,5 @@
/* replicated in part from the boot code, for now */

#define XTAL		3686400
#define	CORECLOCK	199066000
#define	MONITOR		0x00020000

#define	SYS_REGBASE	0x90000000

#define	SYSR_FIRQ	0x1000
#define	SYSR_IRQ	0x1400
#define	SIRQ_PCI	(1<<2)
#define	SIRQ_UART	(1<<8)
#define SYSR_UART_SR	0x3400	/* UART Status Register */
#define SYSR_UART_CR	0x3800	/* UART Control Register */
#define SYSR_UART_DR	0x3C00	/* UART Data Register */

#define	SYS_FIRQ	(0x90000000+SYSR_FIRQ)
#define	SYS_IRQ		(0x90000000+SYSR_IRQ)

#define	PCI_REGBASE	0x42000000
#define	PCI_IPXRESET	0x07C
#define	PCI_IRQSTATUS	0x180
#define	PCI_IRQRSTATUS	0x184
#define	PCI_IRQENABLE	0x188
#define	PCI_IRQENA_SET	0x188
#define	PCI_IRQENA_CLR	0x18C
#define	PCI_IRQSOFT	0x190
#define	PCI_FIRQSTATUS	0x280
#define	PCI_FIRQRSTATUS	0x284
#define	PCI_FIRQENABLE	0x288
#define	PCI_FIRQENA_SET	0x288
#define	PCI_FIRQENA_CLR	0x28C
#define	PCI_FIRQSOFT	0x290
#define	PCI_TIMERLOAD	0x300
#define	PCI_TIMERVALUE	0x304
#define	PCI_TIMERCTL	0x308
#define	PCI_TIMERCLEAR	0x30C

#define	TIMER_OFF	0x020

#define	TIMER1_LOAD	(PCI_REGBASE+PCI_TIMERLOAD)
#define	TIMER1_VALUE	(PCI_REGBASE+PCI_TIMERVALUE)
#define	TIMER1_CTL	(PCI_REGBASE+PCI_TIMERCTL)
#define	TIMER1_CLEAR	(PCI_REGBASE+PCI_TIMERCLEAR)

#define	TIMER_PERIODIC	(1<<6)
#define	TIMER_ENABLE	(1<<7)

#define	IRQ_STATUS	(PCI_REGBASE+PCI_IRQSTATUS)
#define	IRQ_ENABLE_SET	(PCI_REGBASE+PCI_IRQENA_SET)
#define	IRQ_ENABLE_CLR	(PCI_REGBASE+PCI_IRQENA_CLR)

#define	FIRQ_OFF	0x100

#define	IRQ_SOFT	1
#define	IRQ_TIMER1	4
#define	IRQ_TIMER2	5
#define	IRQ_TIMER3	6
#define	IRQ_TIMER4	7
#define	IRQ_DOOR	15
#define	IRQ_DMA1	16
#define	IRQ_DMA2	17
#define	IRQ_PCI		18
#define	IRQ_DMA1NB	20
#define	IRQ_DMA2NB	21
#define	IRQ_BIST	22
#define	IRQ_SERR	23
#define	IRQ_I2OIN	25
#define	IRQ_POWER	26
#define	IRQ_DTIMER	27
#define	IRQ_PARITY	28
#define	IRQ_MABORT	29
#define	IRQ_TABORT	30
#define	IRQ_DPARITY	31

#define PCI_CONF1_BASE	0x52000000
#define PCI_CONF0_BASE	0x53000000
#define PCI_IO_BASE	0x54000000
#define	PCI_IO_SIZE	0x00010000
#define PCI_MEM_BASE	0x60000000
#define PCI_MEM_SIZE	0x20000000

/* MMU. */

#define MMUCR_M_ENABLE	(1<<0)	/* MMU enable */
#define MMUCR_A_ENABLE	(1<<1)	/* Address alignment fault enable */
#define MMUCR_C_ENABLE	(1<<2)	/* (data) cache enable */
#define MMUCR_W_ENABLE	(1<<3)	/* write buffer enable */
#define MMUCR_PROG32	(1<<4)	/* PROG32 */
#define MMUCR_DATA32	(1<<5)	/* DATA32 */
#define MMUCR_L_ENABLE	(1<<6)	/* Late abort on earlier CPUs */
#define MMUCR_BIGEND	(1<<7)	/* Big-endian (=1), little-endian (=0) */
#define MMUCR_SYSTEM	(1<<8)	/* System bit, modifies MMU protections */
#define MMUCR_ROM	(1<<9)	/* ROM bit, modifies MMU protections */
#define MMUCR_F		(1<<10)	/* Should Be Zero */
#define MMUCR_Z_ENABLE	(1<<11)	/* Branch prediction enable on 810 */
#define MMUCR_I_ENABLE	(1<<12)	/* Instruction cache enable on SA110 */

#define	SDRAM_BASE	0xC0000000
#define	SDRAM_RESV	(16*1024*1024)
#define	SDRAM_SIZE	(32*1024*1024)

#define	PMEM_BASE	(SDRAM_BASE+SDRAM_RESV)
#define	PMEM_TOP	(SDRAM_BASE+SDRAM_SIZE)
#define	KTOP		(KZERO + (SDRAM_SIZE - SDRAM_RESV))

#define	SLOW_BASE	0x38400000
#define	SLEDOFFSET	0x00108000

#define	LEDADDR		(SLOW_BASE+SLEDOFFSET)

#define	SACADDR		0x08100000
/*
 *  all register offsets are relative to 0x8000000 so that
 *  IOZERO can be changed at some future point
 */
#define IOA(t, x)	((t*)(IOZERO|x))

M bitsy/l.s => bitsy/l.s +73 -59
@@ 25,9 25,9 @@ _main:
	BL	main(SB)
	BL	exit(SB)
	/* we shouldn't get here */
	BL	_div(SB)			/* hack to get _div etc loaded */
_mainloop:
	B	_mainloop
	BL	_div(SB)			/* hack to get _div etc loaded */

/* flush tlb's */
TEXT flushmmu(SB), $-4


@@ 79,10 79,13 @@ TEXT getfar(SB), $-4
	MRC	CpMMU, 0, R0, C(CpFAR), C(0x0)
	RET

/* st the translation table base */
/* set the translation table base */
TEXT setttb(SB), $-4
	MCR	CpMMU, 0, R0, C(CpTTB), C(0x0)

/*
 *  set the stack value for the mode passed in R0
 */
TEXT setr13(SB), $-4
	MOVW	4(FP), R1



@@ 97,97 100,108 @@ TEXT setr13(SB), $-4
	MOVW	R2, CPSR
	RET

/*
 *  exception vectors, copied by trapinit() to somewhere useful
 */
TEXT vectors(SB), $-4
	MOVW	0x18(R15), R15			/* reset */
	MOVW	0x18(R15), R15			/* undefined */
	MOVW	0x18(R15), R15			/* SWI */
	MOVW	0x18(R15), R15			/* prefetch abort */
	MOVW	0x18(R15), R15			/* data abort */
	MOVW	0x18(R15), R15			/* reserved */
	MOVW	0x18(R15), R15			/* IRQ */
	MOVW	0x18(R15), R15			/* FIQ */

TEXT vtable(SB), $-4
	WORD	$_vsvc(SB)			/* reset, in svc mode already */
	WORD	$_vund(SB)			/* undefined, switch to svc mode */
	WORD	$_vsvc(SB)			/* swi, in svc mode already */
	WORD	$_vpab(SB)			/* prefetch abort, switch to svc mode */
	WORD	$_vdab(SB)			/* data abort, switch to svc mode */
	WORD	$_vsvc(SB)			/* reserved */
	WORD	$_virq(SB)			/* IRQ, switch to svc mode */
	WORD	$_vfiq(SB)			/* FIQ, switch to svc mode */

TEXT _vund(SB), $-4				/* undefined */
	MOVM.IA	[R0-R3], (R13)
	MOVW	0x18(R15), R15		/* reset */
	MOVW	0x18(R15), R15		/* undefined */
	MOVW	0x18(R15), R15		/* SWI */
	MOVW	0x18(R15), R15		/* prefetch abort */
	MOVW	0x18(R15), R15		/* data abort */
	MOVW	0x18(R15), R15		/* reserved */
	MOVW	0x18(R15), R15		/* IRQ */
	MOVW	0x18(R15), R15		/* FIQ */
	WORD	$_vsvc(SB)		/* reset, in svc mode already */
	WORD	$_vund(SB)		/* undefined, switch to svc mode */
	WORD	$_vsvc(SB)		/* swi, in svc mode already */
	WORD	$_vpab(SB)		/* prefetch abort, switch to svc mode */
	WORD	$_vdab(SB)		/* data abort, switch to svc mode */
	WORD	$_vsvc(SB)		/* reserved */
	WORD	$_virq(SB)		/* IRQ, switch to svc mode */
	WORD	$_vfiq(SB)		/* FIQ, switch to svc mode */

TEXT _vsvc(SB), $-4			/* reset or SWI or reserved */
	SUB	$12, R13		/* make room for pc, psr, & type */
	MOVW	R14, 8(R13)		/* ureg->pc = interupted PC */
	MOVW	SPSR, R14		/* ureg->psr = SPSR */
	MOVW	R14, 4(R13)		/* ... */
	MOVW	$PsrMsvc, R14		/* ureg->type = PsrMsvc */
	MOVW	R14, (R13)		/* ... */
	MOVM.DB.W.S [R0-R14], (R13)	/* save user level registers, at end r13 points to ureg */
	B	_vexcep			/* call the exception handler */

TEXT _vund(SB), $-4			/* undefined */
	MOVM.IA	[R0-R3], (R13)		/* free some working space */
	MOVW	$PsrMund, R0
	B	_vswitch

TEXT _vsvc(SB), $-4				/* reset or SWI or reserved */
	SUB	$12, R13			/* make room for pc, psr, & type */
	MOVW	R14, 8(R13)			/* ureg->pc = interupted PC */
	MOVW	SPSR, R14			/* ureg->psr = SPSR */
	MOVW	R14, 4(R13)			/* ... */
	MOVW	$PsrMsvc, R14			/* ureg->type = PsrMsvc */
	MOVW	R14, (R13)			/* ... */
	B	_vsaveu

TEXT _vpab(SB), $-4				/* prefetch abort */
	MOVM.IA	[R0-R3], (R13)			/* free some working space */
	MOVW	$PsrMabt, R0			/* r0 = type */
TEXT _vpab(SB), $-4			/* prefetch abort */
	MOVM.IA	[R0-R3], (R13)		/* free some working space */
	MOVW	$PsrMabt, R0		/* r0 = type */
	B	_vswitch

TEXT _vdab(SB), $-4				/* data abort */
	MOVM.IA	[R0-R3], (R13)			/* free some working space */
	MOVW	$(PsrMabt+1), R0		/* r0 = type */
	B	_vswitch			/* 

TEXT _virq(SB), $-4				/* IRQ */
	MOVM.IA	[R0-R3], (R13)			/* free some working space */
	MOVW	$PsrMirq, R0			/* r0 = type */
TEXT _vdab(SB), $-4			/* data abort */
	MOVM.IA	[R0-R3], (R13)		/* free some working space */
	MOVW	$(PsrMabt+1), R0	/* r0 = type */
	B	_vswitch

TEXT _vfiq(SB), $-4				/* FIQ */
	MOVM.IA	[R0-R3], (R13)			/* free some working space */
	MOVW	$PsrMfiq, R0			/* r0 = type */
TEXT _virq(SB), $-4			/* IRQ */
	MOVM.IA	[R0-R3], (R13)		/* free some working space */
	MOVW	$PsrMirq, R0		/* r0 = type */
	B	_vswitch

TEXT _vfiq(SB), $-4			/* FIQ */
	RFE				/* RIQ is special, ignore it for now */

	/*
	 *  come here with type in R0 and R13 pointing above saved [r0-r3]
	 */
_vswitch:				/* switch to svc, type in R0 */
	MOVW	SPSR, R1		/* SPSR for ureg */
	MOVW	R14, R2			/* interrupted pc for ureg */
	MOVW	R13, R3			/* [R0-R3] save area */
	MOVW	SPSR, R1		/* save SPSR for ureg */
	MOVW	R14, R2			/* save interrupted pc for ureg */
	MOVW	R13, R3			/* save pointer to where the original [R0-R3] are */

	/* switch to svc mode, we get new R13 pointing to top of svc stack */
	/* switch to svc mode */
	MOVW	CPSR, R14
	BIC	$PsrMask, R14
	ORR	$(PsrDirq|PsrDfiq|PsrMsvc), R14
	MOVW	R14, CPSR

	/*
	 *  R13 and R14 is now R13_SVC and R14_SVC.  The values of the previous mode's
	 *  R13 and R14 are no longer accessible.  That's why R3 was left to point to where
	 *  the old [r0-r3] are stored.
	 */
	MOVM.DB.W [R0-R2], (R13)	/* set ureg->{pc, psr, type}; r13 points to ureg->type  */
	MOVM.IA	  (R3), [R0-R3]		/* restore [R0-R3] from previous mode's stack */
	B	_vsaveu
	MOVM.DB.W.S [R0-R14], (R13)	/* save user level registers, at end r13 points to ureg */

	/* 
	 *  come here with R13 pointing to ureg->type
	/*
	 *  if the original interrupt happened while executing SVC mode, the User R14 in the Ureg is
	 *  wrong.  We need to save the SVC one there.
	 */
_vsaveu:
	MOVM.DB.W.S [R0-R14], (R13)	/* save user level registers, r13 points to ureg */

	MOVW	$setR12(SB), R12	/* the SB from user mode is different */
	MOVW	0x40(R13), R1
	AND.S	$0xf, R1
	MOVW.NE	R14,0x38(R13)
	B	_vexcep

	/*
 	 *  call the exception routine, the ureg is at the bottom of the stack
	 */
_vexcep:
	MOVW	$setR12(SB), R12	/* Make sure we've got the kernel's SB loaded */
	MOVW	R13, R0			/* first arg is pointer to ureg */
	SUB	$8, R13			/* space for argument+link */
	BL	exception(SB)

_vrfe: 
	ADD	$(8+4*15), R13		/* r13 points to ureg->type */
	ADD	$(8+4*15), R13		/* make r13 point to ureg->type */
	MOVW	8(R13), R14		/* restore link */
	MOVW	4(R13), R0		/* restore SPSR */
	MOVW	R0, SPSR		/* ... */
	MOVM.DB.S (R13), [R0-R14]	/* restore registers */
	ADD	$8, R13			/* skip saved type+SPSR */
	ADD	$8, R13			/* pop past ureg->{type+psr} */
	RFE				/* MOVM.IA.S.W (R13), [R15] */

TEXT splhi(SB), $-4

M bitsy/main.c => bitsy/main.c +5 -2
@@ 14,7 14,7 @@ Conf conf;
void
main(void)
{

	putuartstr("hello ken");
}

/*


@@ 23,7 23,10 @@ main(void)
void
exit(int ispanic)
{
	USED(ispanic);
	void (*f)();

	f = nil;
	(*f)();
}

/*

M bitsy/mem.h => bitsy/mem.h +15 -2
@@ 33,13 33,26 @@
#define MACHADDR	(KZERO+0x00001000)

/*
 *  Address spaces
 *  Address spaces:
 *
 *  We've direct mapped the DRAM's and all the
 *  special use space.  Since the DRAM's lie at
 *  0xc0000000 and since the Compaq boot loader
 *  copies the kernel to the start of DRAM, we've
 *  made KZERO 0xc0000000.  We've also direct mapped
 *  the special use io space between 0x8000000 and KZERO.
 *  That means we can leave the MMU off till we're pretty
 *  far into the boot.
 *
 *  Both these decisions can be changed by putting an MMU setup in
 *  l.s and changing the definitions below.
 */
#define	UZERO		0			/* base of user address space */
#define	UTZERO		(UZERO+BY2PG)		/* first address in user text */
#define	KZERO		0xC0000000		/* base of kernel address space */
#define	KTZERO		0xC0008000		/* first address in kernel text */
#define	USTKTOP		(KZERO-BY2PG)		/* byte just beyond user stack */
#define	IOZERO		0x80000000		/* 1 gig of special regs */
#define	USTKTOP		(IOZERO-BY2PG)		/* byte just beyond user stack */
#define	USTKSIZE	(16*1024*1024)		/* size of user stack */
#define	TSTKTOP		(USTKTOP-USTKSIZE)	/* end of new stack in sysexec */
#define TSTKSIZ 	100

A pc/vga3dfx.c => pc/vga3dfx.c +78 -0
@@ 0,0 1,78 @@
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"

#define	Image	IMAGE
#include <draw.h>
#include <memdraw.h>
#include <cursor.h>
#include "screen.h"

static ulong
tdfxlinear(VGAscr* scr, int* size, int* align)
{
	ulong aperture, oaperture;
	int oapsize, wasupamem;
	Pcidev *p;

	oaperture = scr->aperture;
	oapsize = scr->apsize;
	wasupamem = scr->isupamem;

	aperture = 0;
	if(p = pcimatch(nil, 0x121A, 0)){
		switch(p->did){
		case 0x0005:		/* Voodoo 3 3000 */
			aperture = p->mem[1].bar & ~0x0F;
			*size = p->mem[1].size;
			break;
		default:
			break;
		}
	}

	if(wasupamem){
		if(oaperture == aperture)
			return oaperture;
		upafree(oaperture, oapsize);
	}
	scr->isupamem = 0;

	aperture = upamalloc(aperture, *size, *align);
	if(aperture == 0){
		if(wasupamem && upamalloc(oaperture, oapsize, 0)){
			aperture = oaperture;
			scr->isupamem = 1;
		}
		else
			scr->isupamem = 0;
	}
	else
		scr->isupamem = 1;

	return aperture;
}

VGAdev vga3dfxdev = {
	"3dfx",

	nil,				/* enable */
	nil,				/* disable */
	nil,				/* page */
	tdfxlinear,			/* linear */
	nil,				/* drawinit */
	nil,				/* fill */
};

VGAcur vga3dfxcur = {
	"3dfxhwgc",

	nil,				/* enable */
	nil,				/* disable */
	nil,				/* load */
	nil,				/* move */
};

M pc/vgas3.c => pc/vgas3.c +8 -3
@@ 567,15 567,20 @@ s3drawinit(VGAscr *scr)
	 * above.
	 */
	switch(id){
	case VIRGE:				/* ViRGE */
	case VIRGEVX:				/* ViRGE/VX */
	case VIRGEGX2:				/* ViRGE/GX2 */
	case VIRGE:
	case VIRGEVX:
	case VIRGEGX2:
		scr->mmio = (ulong*)(scr->aperture+0x1000000);
		scr->fill = hwfill;
		scr->scroll = hwscroll;
		/* scr->blank = hwblank; */
		break;
	case SAVAGEIXMV:
		scr->mmio = (ulong*)(scr->aperture+0x1000000);
		savageinit(scr);	
		break;
	case SAVAGE4:
		/* scr->mmio is set by s3linear */
		savageinit(scr);
		break;
	}

M pc/vgasavage.c => pc/vgasavage.c +45 -7
@@ 69,7 69,7 @@ enum {
 * 32-bit reads of the status registers seem just fine.
 */

/* 2D graphics engine registers */
/* 2D graphics engine registers for Savage4; others appear to be mostly the same */
enum {
	SubsystemStatus = 0x8504,	/* Subsystem Status: read only */
	  /* read only: whether we get interrupts on various events */


@@ 344,21 344,49 @@ enum {

};

struct {
	ulong idletimeout;
} savagestats;

enum {
	Maxloop = 1<<23
	Maxloop = 1<<25
};

static void
savagewaitidle(VGAscr *scr)
{
	long x;
	ulong *statw;

	statw = (ulong*)((uchar*)scr->mmio+AltStatus0);
	ulong *statw, mask, goal;

	switch(scr->id){
	case SAVAGE4:
		statw = (ulong*)((uchar*)scr->mmio+AltStatus0);
		mask = Ge2Idle;
		goal = 0;
		break;
	/* case SAVAGEMXMV: ? */
	/* case SAVAGEMX: ? */
	/* case SAVAGEIXMV: ? */
	/* case SAVAGEIX: ? */
	case SAVAGEIXMV:
		/* wait for engine idle and FIFO empty */
		statw = (ulong*)((uchar*)scr->mmio+XStatus0);
		mask = 0x0008FFFF;
		goal = 0x00080000;
		break;
	default:
		/* 
		 * best we can do: can't print or we'll call ourselves.
		 * savageinit is supposed to not let this happen.
		 */	
		return;
	}

	for(x=0; x<Maxloop; x++)
		if(*statw & Ge2Idle)
		if((*statw & mask) == goal)
			return;

	savagestats.idletimeout++;
}

static int


@@ 429,12 457,22 @@ savageinit(VGAscr *scr)
	uchar *mmio;
	ulong bd;

	mmio = (uchar*)scr->mmio;
	/* if you add chip IDs here be sure to update savagewaitidle */
	switch(scr->id){
	case SAVAGE4:
	case SAVAGEIXMV:
		break;
	default:
		print("unknown savage %.4lux\n", scr->id);
		return;
	}

	mmio = (uchar*)scr->mmio;
	if(mmio == nil) {
		print("savageinit: no mmio\n");
		return;
	}

	/* 2D graphics engine software reset */
	*(ushort*)(mmio+SubsystemCtl) = GeSoftReset;
	delay(2);

M port/chan.c => port/chan.c +1 -1
@@ 641,7 641,7 @@ namec(char *name, int amode, int omode, ulong perm)
	if(name[0] == 0)
		error(Enonexist);

	if(!((ulong)name & KZERO)) {
	if(((ulong)name & KZERO) != KZERO) {
		p = name;
		t = BY2PG-((ulong)p&(BY2PG-1));
		while(vmemchr(p, 0, t) == 0) {

M port/fault.c => port/fault.c +1 -1
@@ 345,7 345,7 @@ vmemchr(void *s, int c, int n)
		t = vmemchr(s, c, m);
		if(t)
			return t;
		if(!(a & KZERO))
		if((a & KZERO) != KZERO)
			validaddr(a+m, 1, 0);
		return vmemchr((void*)(a+m), c, n-m);
	}

M port/sysproc.c => port/sysproc.c +2 -2
@@ 242,7 242,7 @@ sysexec(ulong *arg)
		text = l2be(exec.text);
		entry = l2be(exec.entry);
		if(n==sizeof(Exec) && (magic == AOUT_MAGIC)){
			if((text&KZERO)
			if((text&KZERO) == KZERO
			|| entry < UTZERO+sizeof(Exec)
			|| entry >= UTZERO+sizeof(Exec)+text)
				error(Ebadexec);


@@ 278,7 278,7 @@ sysexec(ulong *arg)
	d = (t + data + (BY2PG-1)) & ~(BY2PG-1);
	bssend = t + data + bss;
	b = (bssend + (BY2PG-1)) & ~(BY2PG-1);
	if((t|d|b) & KZERO)
	if(((t|d|b) & KZERO) == KZERO)
		error(Ebadexec);

	/*