~kris/9p

9hist

de239c022b3f82273d466a6bac40147188e92793 — David du Colombier 26 years ago d7501ed
Plan 9 from Bell Labs 1999-10-05
M carrera/kbd.c => carrera/kbd.c +5 -5
@@ 47,7 47,7 @@ enum

	PF=		Spec|0x20,	/* num pad function key */
	View=		Spec|0x00,	/* view (shift window up) */
	KF=		Spec|0x40,	/* function key */
	KF=		0xF000,	/* function key (begin Unicode private space) */
	Shift=		Spec|0x60,
	Break=		Spec|0x61,
	Ctrl=		Spec|0x62,


@@ 76,7 76,7 @@ enum
#define INWAIT	while(!(KBDCTL & Sobf)); kdbdly(1)
#define ACKWAIT INWAIT ; if(KBDDAT != Rack) print("bad response\n"); kdbdly(1)

uchar kbtab[] = 
Rune kbtab[] = 
{
[0x00]	No,	0x1b,	'1',	'2',	'3',	'4',	'5',	'6',
[0x08]	'7',	'8',	'9',	'0',	'-',	'=',	'\b',	'\t',


@@ 92,7 92,7 @@ uchar kbtab[] =
[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
};

uchar kbtabshift[] =
Rune kbtabshift[] =
{
[0x00]	No,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^',
[0x08]	'&',	'*',	'(',	')',	'_',	'+',	'\b',	'\t',


@@ 108,7 108,7 @@ uchar kbtabshift[] =
[0x58]	KF|12,	No,	No,	No,	No,	No,	No,	No,
};

uchar kbtabesc1[] =
Rune kbtabesc1[] =
{
[0x00]	No,	No,	No,	No,	No,	No,	No,	No,
[0x08]	No,	No,	No,	No,	No,	No,	No,	No,


@@ 199,7 199,7 @@ kbdintr(void)
	static int ctl;
	static int num;
	static int collecting, nk;
	static uchar kc[5];
	static Rune kc[5];
	static int shift;
	int keyup;


M carrera/main.c => carrera/main.c +6 -3
@@ 38,10 38,12 @@ int	int1mask = 0xff;	/* interrupts enabled for second 8259 */

extern	uchar rdbgcode[];
extern	ulong	rdbglen;
extern	int	noprint;

void
main(void)
{
	noprint = 1;
	tlbinit();
	ioinit(1);		/* Very early to establish IO mappings */
	rdbginit();


@@ 54,10 56,10 @@ main(void)
	iomapinit();
	printinit();
	serialinit();
	iprint("Brazil\n");		/**/
	vecinit();
	screeninit();
/*	iprint("\n\nBrazil\n");		/**/
	print("Brazil config reg %#ux\n", configreg);
//	print("Brazil config reg %#ux\n", configreg);
	pageinit();
	procinit0();
	initseg();


@@ 65,6 67,7 @@ main(void)
	chandevreset();
	swapinit();
	userinit();
	noprint = 0;
	schedinit();
}



@@ 430,7 433,7 @@ exit(int type)

	splhi();
	if(type == 1)
		for(;;);
		rdb();

	/* Turn off the NMI hander for the debugger */
	vec = (uchar*)0xA0000420;

M carrera/mem.h => carrera/mem.h +1 -1
@@ 23,7 23,7 @@
/*
 * Time
 */
#define	HZ		1000			/* clock frequency */
#define	HZ		100			/* clock frequency */
#define	MS2HZ		(1000/HZ)		/* millisec per clock tick */
#define	TK2SEC(t)	((t)/HZ)		/* ticks to seconds */
#define	TK2MS(t)	((t)*MS2HZ)		/* ticks to milliseconds */

M carrera/screen.c => carrera/screen.c +63 -126
@@ 30,7 30,7 @@ struct Dac
};

char s1[] = { 0x00, 0x00, 0xC0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
#define	Backgnd		0x00
#define	Backgnd		0xFF	/* white */

		Memsubfont	*memdefont;
static	ulong		rep(ulong, int);


@@ 44,8 44,6 @@ Lock	screenlock;

Point	ZP = {0, 0};

static Memdata	hwcursordata;

static Memdata gscreendata =
{
	nil,


@@ 53,96 51,25 @@ static Memdata gscreendata =
	1,
};

Memimage gscreen =
static Memimage xgscreen =
{
	{ 0, 0, 1597, 1234 },	/* r */
	{ 0, 0, 1597, 1234 },	/* clipr */
	3,	/* ldepth */
	0,	/* repl */
	8,	/* depth */
	1,	/* nchan */
	CMAP8,	/* chan */
	nil,	/* cmap */
	&gscreendata,	/* data */
	0,	/* zero */
	512,	/* width */
	0,	/* layer */
	Frepl,	/* flags */
};

static Memimage hwcursor=
{
	{0, 0, 64, 64},
	{0, 0, 64, 64},
	1,
	0,
	&hwcursordata,		/* base filled in by malloc when needed */
	0,
	4,
	0,
};

ulong	consbits = ~0;
Memdata	consdata = {
	nil,
	&consbits
};
Memimage conscol =
{
	{ 0, 0, 1, 1 },
	{ -100000, -100000, 100000, 100000 },
	3,
	1,
	&consdata,
	0,
	1
};

ulong	onesbits = ~0;
Memdata	onesdata = {
	nil,
	&onesbits,
};
Memimage	xones =
{
	{ 0, 0, 1, 1 },
	{ -100000, -100000, 100000, 100000 },
	3,
	1,
	&onesdata,
	0,
	1
};
Memimage *memones = &xones;

ulong	zerosbits = 0;
Memdata	zerosdata = {
	nil,
	&zerosbits,
};
Memimage	xzeros =
{
	{ 0, 0, 1, 1 },
	{ -100000, -100000, 100000, 100000 },
	3,
	1,
	&zerosdata,
	0,
	1
};
Memimage *memzeros = &xzeros;

ulong	backbits = (Backgnd<<24)|(Backgnd<<16)|(Backgnd<<8)|Backgnd;
Memdata	backdata = {
	nil,
	&backbits
};
Memimage	xback =
{
	{ 0, 0, 1, 1 },
	{ -100000, -100000, 100000, 100000 },
	3,
	1,
	&backdata,
	0,
	1
};
Memimage *back = &xback;
Memimage *gscreen;
Memimage *conscol;
Memimage *back;
Memimage *hwcursor;

Cursor	arrow = {
	{ -1, -1 },


@@ 161,19 88,27 @@ Cursor	arrow = {
static Rectangle window;
static Point curpos;
static int h, w;
int drawdebug;

void
screenwin(void)
{
	Dac *d;
	Point p, q;
	int y;
	Cursor zero;
	char *greet;
	Memimage *grey;

	gscreen = &xgscreen;
	memsetchan(gscreen, CMAP8);
	hwcursor = allocmemimage(Rect(0,0,64,64), GREY2);
	memsetchan(hwcursor, GREY2);
	
	back = memwhite;
	conscol = memblack;

	memset((void*)Screenvirt, 0xff, 3*1024*1024);
	for(y=gscreen.r.min.y; y<gscreen.r.max.y; y++)
		memset(byteaddr(&gscreen, Pt(gscreen.r.min.x, y)), 0xba, Dx(gscreen.r));
	memset((void*)Screenvirt, 0, 3*1024*1024);
	memfillcolor(gscreen, 0x444488FF);

	w = memdefont->info[' '].width;
	h = memdefont->height;


@@ 181,20 116,24 @@ screenwin(void)
	window.min = Pt(100, 100);
	window.max = addpt(window.min, Pt(10+w*120, 10+h*60));

	memimagedraw(&gscreen, window, memones, ZP, memones, ZP);
	memimagedraw(gscreen, window, memblack, ZP, memopaque, ZP);
	window = insetrect(window, 4);
	memimagedraw(&gscreen, window, back, ZP, memones, ZP);
	/* hack to get a grey color */
	memzeros->data->data[0] = 0x33333333;
	memimagedraw(&gscreen, Rect(window.min.x, window.min.y,
			window.max.x, window.min.y+h+5+6), memzeros, ZP, memones, ZP);
	memzeros->data->data[0] = 0;
	memimagedraw(gscreen, window, memwhite, ZP, memopaque, ZP);

	/* a lot of work to get a grey color */
	grey = allocmemimage(Rect(0,0,1,1), CMAP8);
	grey->flags |= Frepl;
	grey->clipr = gscreen->r;
	grey->data->bdata[0] = 0xAA;
	memimagedraw(gscreen, Rect(window.min.x, window.min.y,
			window.max.x, window.min.y+h+5+6), grey, ZP, nil, ZP);
	freememimage(grey);
	window = insetrect(window, 5);

	greet = " Brazil Console: Draw version ";
	greet = " Brazil Console: New Draw version ";
	p = addpt(window.min, Pt(10, 0));
	q = memsubfontwidth(memdefont, greet);
	memimagestring(&gscreen, p, &conscol, memdefont, greet);
	memimagestring(gscreen, p, conscol, memdefont, greet);
	window.min.y += h+6;
	curpos = window.min;
	window.max.y = window.min.y+((window.max.y-window.min.y)/h)*h;


@@ 254,7 193,7 @@ dacinit(void)
	for(i = 0; i < 0x400; i++)
		d->cr2 = 0xff;

	drawcmap(0);
	drawcmap(1);

	/* Overlay Palette Ram */
	d->cr0 = 0x00;


@@ 280,6 219,7 @@ screeninit(void)
{
	dacinit();

	memimageinit();
	memdefont = getmemdefont();

	out.pos.x = MINX;


@@ 299,9 239,9 @@ scroll(void)
	o = 8*h;
	r = Rpt(window.min, Pt(window.max.x, window.max.y-o));
	p = Pt(window.min.x, window.min.y+o);
	memimagedraw(&gscreen, r, &gscreen, p, memones, p);
	memimagedraw(gscreen, r, gscreen, p, nil, p);
	r = Rpt(Pt(window.min.x, window.max.y-o), window.max);
	memimagedraw(&gscreen, r, back, ZP, memones, ZP);
	memimagedraw(gscreen, r, back, ZP, nil, ZP);

	curpos.y -= o;
}


@@ 309,15 249,16 @@ scroll(void)
/* 
 * export screen to devdraw
 */
ulong*
attachscreen(Rectangle *r, int *ld, int *width, int *softscreen)
uchar*
attachscreen(Rectangle *r, ulong *chan, int* d, int *width, int *softscreen)
{
	*r = gscreen.r;
	*ld = gscreen.ldepth;
	*width = gscreen.width;
	*r = gscreen->r;
	*d = gscreen->depth;
	*chan = gscreen->chan;
	*width = gscreen->width;
	*softscreen = 0;

	return gscreendata.data;
	return gscreendata.bdata;
}

/*


@@ 335,10 276,10 @@ flushmemscreen(Rectangle r)
	if(Dx(r)<=0 || Dy(r)<=0)
		return;
	x = allocmemimage(r, 3);
	memimagedraw(x, r, &gscreen, r.min, memones, r.min);
	memimagedraw(&gscreen, r, memones, r.min, memones, r.min);
	memimagedraw(x, r, &gscreen, r.min, memopaque, r.min);
	memimagedraw(&gscreen, r, memblack, r.min, memopaque, r.min);
	for(i=1000000; --i>0; );
	memimagedraw(&gscreen, r, x, r.min, memones, r.min);
	memimagedraw(&gscreen, r, x, r.min, memopaque, r.min);
	freememimage(x);
*/
}


@@ 373,7 314,7 @@ screenputc(char *buf)
		pos = (curpos.x-window.min.x)/w;
		pos = 8-(pos%8);
		r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y + h);
		memimagedraw(&gscreen, r, back, back->r.min, memones, back->r.min);
		memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min);
		curpos.x += pos*w;
		break;
	case '\b':


@@ 381,7 322,7 @@ screenputc(char *buf)
			break;
		xp--;
		r = Rect(*xp, curpos.y, curpos.x, curpos.y + h);
		memimagedraw(&gscreen, r, back, back->r.min, memones, back->r.min);
		memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min);
		curpos.x = *xp;
		break;
	default:


@@ 393,8 334,8 @@ screenputc(char *buf)

		*xp++ = curpos.x;
		r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y + h);
		memimagedraw(&gscreen, r, back, back->r.min, memones, back->r.min);
		memimagestring(&gscreen, curpos, &conscol, memdefont, buf);
		memimagedraw(gscreen, r, back, back->r.min, nil, back->r.min);
		memimagestring(gscreen, curpos, conscol, memdefont, buf);
		curpos.x += w;
	}
}


@@ 542,9 483,7 @@ setcursor(Cursor *curs)
		*p = curs->clr[2*i];
		*(p+1) = curs->clr[2*i+1];
	}
	hwcursor.data->data = (ulong *)malloc(1024);
	if(hwcursor.data->data == 0)
		error(Enomem);
	memset(hwcursor->data->bdata, 0, sizeof(long)*hwcursor->width*Dy(hwcursor->r));
	/* hw cursor is 64x64 with hot point at (32,32) */
	org = addpt(Pt(32,32), curs->offset); 
	for(y = 0; y < 16; y++)


@@ 553,7 492,7 @@ setcursor(Cursor *curs)
			cpix = (c[y]>>(15-x))&1;
			dpix = (spix<<1) | cpix;
			/* point(&hwcursor, addpt(Pt(x,y), org), dpix, S), by hand */
			*byteaddr(&hwcursor, Pt(x+org.x, y+org.y)) |= dpix<<(6-(2*((x+org.x)&3)));
			*byteaddr(hwcursor, Pt(x+org.x, y+org.y)) |= (dpix<<(6-(2*((x+org.x)&3))));
		}

	d = DAC;


@@ 571,14 510,12 @@ setcursor(Cursor *curs)
	d->cr1 = 0x04;
	d->cr0 = 0x00;
	for(x = 0; x < 1024; x++)
		d->cr2 = ((uchar *)hwcursor.data->data)[x];
		d->cr2 = hwcursor->data->bdata[x];
	/* set y back */
	d->cr1 = 0x03;
	d->cr0 = 0x03;
	d->cr2 = ylow;
	d->cr2 = yhigh;
	
	free(hwcursor.data->data);
}

int


@@ 611,7 548,7 @@ cursoron(int dolock)
int
screenbits(void)
{
	return 1<<gscreen.ldepth;
	return gscreen->depth;
}

extern	cursorlock(Rectangle);


@@ 632,14 569,14 @@ screenload(Rectangle r, uchar *data, int tl, int l, int dolock)

	USED(dolock);

	if(!rectclip(&r, gscreen.r) || tl<=0)
	if(!rectclip(&r, gscreen->r) || tl<=0)
		return;

	lock(&screenlock);
	q = byteaddr(&gscreen, r.min);
	q = byteaddr(gscreen, r.min);
	for(y=r.min.y; y<r.max.y; y++){
		memmove(q, data, tl);
		q += gscreen.width*sizeof(ulong);
		q += gscreen->width*sizeof(ulong);
		data += l;
	}
	unlock(&screenlock);


@@ 663,10 600,10 @@ screenunload(Rectangle r, uchar *data, int tl, int l, int dolock)
	USED(dolock);

	lock(&screenlock);
	q = byteaddr(&gscreen, r.min);
	q = byteaddr(gscreen, r.min);
	for(y=r.min.y; y<r.max.y; y++){
		memmove(data, q, tl);
		q += gscreen.width*sizeof(ulong);
		q += gscreen->width*sizeof(ulong);
		data += l;
	}
	unlock(&screenlock);

M ip/ip.c => ip/ip.c +3 -13
@@ 180,20 180,10 @@ ipoput(Fs *f, Block *bp, int gating, int ttl, int tos)
	else
		gate = r->v4.gate;

	if(gating){
		switch(eh->ttl){
		case 0:
		case 1:
			goto free;
		default:
			eh->ttl--;
			break;
		}
	} else {
	if(!gating)
		eh->vihl = IP_VER|IP_HLEN;
		eh->ttl = ttl;
		eh->tos = tos;
	}
	eh->ttl = ttl;
	eh->tos = tos;

	if(!canrlock(ifc))
		goto free;

M pc/cga.c => pc/cga.c +27 -6
@@ 5,12 5,28 @@
#include "fns.h"
#include "../port/error.h"

enum
{
	Width		= 160,
enum {
	Black,
	Blue,
	Green,
	Cyan,
	Red,
	Magenta,
	Brown,
	Grey,

	Bright = 0x08,
	Blinking = 0x80,

	Yellow = Bright|Brown,
	White = Bright|Grey,
};
	
enum {
	Width		= 80*2,
	Height		= 25,

	Attr		= 7,		/* white on black */
	Attr		= (Black<<4)|White,		/* high nibble background, low foreground */
};

#define CGASCREENBASE	((uchar*)KADDR(0xB8000))


@@ 45,6 61,7 @@ static void
cgascreenputc(int c)
{
	int i;
	uchar *p;

	if(c == '\n'){
		cgapos = cgapos/Width;


@@ 67,7 84,11 @@ cgascreenputc(int c)
	}
	if(cgapos >= Width*Height){
		memmove(CGASCREENBASE, &CGASCREENBASE[Width], Width*(Height-1));
		memset(&CGASCREENBASE[Width*(Height-1)], 0, Width);
		p = &CGASCREENBASE[Width*(Height-1)];
		for(i=0; i<Width/2; i++){
			*p++ = ' ';
			*p++ = Attr;
		}
		cgapos = Width*(Height-1);
	}
	movecursor();


@@ 85,7 106,7 @@ screeninit(void)
static void
cgascreenputs(char* s, int n)
{
	if(!islo()) {
	if(!islo()){
		/*
		 * Don't deadlock trying to
		 * print in an interrupt.

M pc/devlml.c => pc/devlml.c +0 -8
@@ 7,14 7,6 @@
#include	"io.h"

#include	"devlml.h"
#include	"index.h"
#include	"avctl.h"
#include	"misc.h"

Medium		*m;

AVctl	*ctl = &ctlinit;


static void *		pciPhysBaseAddr;
static ulong		pciBaseAddr;

A pc/devusb.c => pc/devusb.c +1934 -0
@@ 0,0 1,1934 @@
/*
 * UHCI USB driver
 *	(c) 1998, 1999 C H Forsyth, forsyth@caldo.demon.co.uk
 * to do:
 *	endpoint open/close
 *	build Endpt on open from attributes stored in Udev?
 *	build data0/data1 rings for bulk and interrupt endpoints
 *	endpoint TD rings (can there be prefetch?)
 *	hubs?
 *	special handling of isochronous traffic?
 *	is use of Queues justified? (could have client clean TD rings on wakeup)
 *	bandwidth check
 */

#include	"u.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"io.h"
#include	"../port/error.h"

/*
#ifndef Inferno
#include	"devtab.h"
#include	"brazilia.h"
#endif
*/

#define Pprint
#define Chatty	1
#define DPRINT if(Chatty)print
#define XPRINT if(0)print

/*
 * USB packet definitions
 */
enum {
	TokIN = 0x69,
	TokOUT = 0xE1,
	TokSETUP = 0x2D,

	/* request type */
	RH2D = 0<<7,
	RD2H = 1<<7,
	Rstandard = 0<<5,
	Rclass = 1<<5,
	Rvendor = 2<<5,
	Rdevice = 0,
	Rinterface = 1,
	Rendpt = 2,
	Rother = 3,
};

typedef uchar byte;

typedef struct Ctlr Ctlr;
typedef struct Endpt Endpt;
typedef struct QTree QTree;
typedef struct Udev Udev;

/*
 * UHCI hardware structures, aligned on 16-byte boundary
 */
typedef struct QH QH;
typedef struct TD TD;

struct TD {
	ulong	link;
	ulong	status;	/* controller r/w */
	ulong	dev;
	ulong	buffer;

	/* software */
	ulong	flags;
	Block*	bp;
	Endpt*	ep;
	TD*	next;
};
#define	TFOL(p)	((TD*)KADDR((ulong)(p) & ~0xF))

struct QH {
	ulong	head;
	ulong	entries;	/* address of next TD or QH to process (updated by controller) */

	/* software */
	union {
		TD*	first;
		QH*	next;		/* free list */
	};
	TD*	last;
};
#define	QFOL(p)	((QH*)KADDR((ulong)(p) & ~0xF))

/*
 * UHCI interface registers and bits
 */
enum {
	/* i/o space */
	Cmd = 0,
	Status = 2,
	Usbintr = 4,
	Frnum = 6,
	Flbaseadd = 8,
	SOFMod = 0xC,
	Portsc0 = 0x10,
	Portsc1 = 0x12,

	/* port status */
	Suspend = 1<<12,
	PortReset = 1<<9,
	SlowDevice = 1<<8,
	ResumeDetect = 1<<6,
	PortChange = 1<<3,	/* write 1 to clear */
	PortEnable = 1<<2,
	StatusChange = 1<<1,	/* write 1 to clear */
	DevicePresent = 1<<0,

	FRAMESIZE=	4096,	/* fixed by hardware; aligned to same */
	NFRAME = 	FRAMESIZE/4,

	Vf = 1<<2,	/* TD only */
	IsQH = 1<<1,
	Terminate = 1<<0,

	/* TD.status */
	SPD = 1<<29,
	ErrLimit0 = 0<<27,
	ErrLimit1 = 1<<27,
	ErrLimit2 = 2<<27,
	ErrLimit3 = 3<<27,
	LowSpeed = 1<<26,
	IsoSelect = 1<<25,
	IOC = 1<<24,
	Active = 1<<23,
	Stalled = 1<<22,
	DataBufferErr = 1<<21,
	Babbling = 1<<20,
	NAKed = 1<<19,
	CRCorTimeout = 1<<18,
	BitstuffErr = 1<<17,
	AnyError = (Stalled | DataBufferErr | Babbling | NAKed | CRCorTimeout | BitstuffErr),

	/* TD.dev */
	IsDATA1 =	1<<19,

	/* TD.flags (software) */
	CancelTD=	1<<0,
};

/*
 * software structures
 */
struct QTree {
	QLock;
	int	nel;
	int	depth;
	QH*	root;
	ulong*	bw;
};

#define	GET2(p)	((((p)[1]&0xFF)<<8)|((p)[0]&0xFF))
#define	PUT2(p,v)	(((p)[0] = (v)), ((p)[1] = (v)>>8))

/*
 * active USB device
 */
struct Udev {
	Ref;
	Lock;
	int	x;	/* index in usbdev[] */
	int	busy;
	int	state;
	int	id;
	byte	port;		/* port number on connecting hub */
	byte	class;
	byte	subclass;
	byte	proto;
	int	ls;
	int	npt;
	Endpt*	ep[16];		/* active end points */
	Udev*	ports;	/* active ports, if hub */
	Udev*	next;		/* next device on this hub */
};

/* device parameters */
enum {
	/* Udev.state */
	Disabled = 0,
	Attached,
	Enabled,
	Assigned,
	Configured,

	/* Udev.class */
	Noclass = 0,
	Hubclass = 9,
};

static char *devstates[] = {
	[Disabled]	"Disabled",
	[Attached] "Attached",
	[Enabled] "Enabled",
	[Assigned] "Assigned",
	[Configured] "Configured",
};

/*
 * device endpoint
 */
struct Endpt {
	Ref;
	Lock;
	int	x;	/* index in Udev.ep */
	int	id;		/* hardware endpoint address */
	int	maxpkt;	/* maximum packet size (from endpoint descriptor) */
	int	data01;	/* 0=DATA0, 1=DATA1 */
	byte	eof;
	byte	class;
	byte	subclass;
	byte	proto;
	byte	mode;	/* OREAD, OWRITE, ORDWR */
	byte	nbuf;	/* number of buffers allowed */
	byte	periodic;
	byte	iso;
	byte	debug;
	byte	active;	/* listed for examination by interrupts */
	int	sched;	/* schedule index; -1 if undefined or aperiodic */
	int	setin;
	ulong	bw;	/* bandwidth requirement */
	int	pollms;	/* polling interval in msec */
	QH*	epq;	/* queue of TDs for this endpoint */

	QLock	rlock;
	Rendez	rr;
	Queue*	rq;
	QLock	wlock;
	Rendez	wr;
	Queue*	wq;

	int	ntd;
	char*	err;

	Udev*	dev;	/* owning device */

	Endpt*	activef;	/* active endpoint list */

	ulong	nbytes;
	ulong	nblocks;
};

struct Ctlr {
	Lock;	/* protects state shared with interrupt (eg, free list) */
	int	io;
	ulong*	frames;	/* frame list */
	int	idgen;	/* version number to distinguish new connections */
	QLock	resetl;	/* lock controller during USB reset */

	TD*	tdpool;
	TD*	freetd;
	QH*	qhpool;
	QH*	freeqh;

	QTree*	tree;	/* tree for periodic Endpt i/o */
	QH*	ctlq;	/* queue for control i/o */
	QH*	bwsop;	/* empty bandwidth sop (to PIIX4 errata specifications) */
	QH*	bulkq;	/* queue for bulk i/o (points back to bandwidth sop) */

	Udev*	ports[2];
};
#define	IN(x)	ins(ub->io+(x))
#define	OUT(x, v)	outs(ub->io+(x), (v))

static	Ctlr	ubus;
static	char	Estalled[] = "usb endpoint stalled";

static	QLock	usbstate;	/* protects name space state */
static	Udev*	usbdev[32];
static struct {
	Lock;
	Endpt*	f;
} activends;

static long readusb(Endpt*, void*, long);
static long writeusb(Endpt*, void*, long, int);

static TD *
alloctd(Ctlr *ub)
{
	TD *t;

	ilock(ub);
	t = ub->freetd;
	if(t == nil)
		panic("alloctd");	/* TO DO */
	ub->freetd = t->next;
	t->next = nil;
	iunlock(ub);
	t->ep = nil;
	t->bp = nil;
	t->status = 0;
	t->link = Terminate;
	t->buffer = 0;
	t->flags = 0;
	return t;
}

static void
freetd(TD *t)
{
	Ctlr *ub;

	ub = &ubus;
	t->ep = nil;
	if(t->bp)
		freeb(t->bp);
	t->bp = nil;
	ilock(ub);
	t->buffer = 0xdeadbeef;
	t->next = ub->freetd;
	ub->freetd = t;
	iunlock(ub);
}

static void
dumpdata(Block *b, int n)
{
	int i;

	XPRINT("\tb %8.8lux[%d]: ", (ulong)b->rp, n);
	if(n > 16)
		n = 16;
	for(i=0; i<n; i++)
		XPRINT(" %2.2ux", b->rp[i]);
	XPRINT("\n");
}

static void
dumptd(TD *t, int follow)
{
	int i, n;
	char buf[20], *s;
	TD *t0;

	t0 = t;
	while(t){
		i = t->dev & 0xFF;
		if(i == TokOUT || i == TokSETUP)
			n = ((t->dev>>21) + 1) & 0x7FF;
		else if((t->status & Active) == 0)
			n = (t->status + 1) & 0x7FF;
		else
			n = 0;
		s = buf;
		if(t->status & Active)
			*s++ = 'A';
		if(t->status & Stalled)
			*s++ = 'S';
		if(t->status & DataBufferErr)
			*s++ = 'D';
		if(t->status & Babbling)
			*s++ = 'B';
		if(t->status & NAKed)
			*s++ = 'N';
		if(t->status & CRCorTimeout)
			*s++ = 'T';
		if(t->status & BitstuffErr)
			*s++ = 'b';
		if(t->status & LowSpeed)
			*s++ = 'L';
		*s = 0;
		print("td %8.8lux: l=%8.8lux s=%8.8lux d=%8.8lux b=%8.8lux %8.8lux f=%8.8lux\n",
			t, t->link, t->status, t->dev, t->buffer, t->bp?(ulong)t->bp->rp:0, t->flags);
		print("\ts=%s,ep=%ld,d=%ld,D=%ld\n", buf, (t->dev>>15)&0xF, (t->dev>>8)&0xFF, (t->dev>>19)&1);
		if(t->bp)
			dumpdata(t->bp, n);
		if(!follow || t->link & Terminate || t->link & IsQH)
			break;
		t = TFOL(t->link);
		if(t == t0)
			break;	/* looped */
	}
}

static TD *
alloctde(Endpt *e, int pid, int n)
{
	TD *t;
	int tog, id;

	t = alloctd(&ubus);
	id = (e->x<<7)|(e->dev->x&0x7F);
	tog = 0;
	if(e->data01 && pid != TokSETUP)
		tog = IsDATA1;
	t->ep = e;
	t->status = ErrLimit3 | Active | IOC;	/* or put IOC only on last? */
	if(e->dev->ls)
		t->status |= LowSpeed;
	t->dev = ((n-1)<<21) | ((id&0x7FF)<<8) | pid | tog;
	return t;
}

static TD *
alloctdring(int nbuf, Endpt *e, int pid, int maxn)
{
	TD *t, *lt, *ft;
	int id, i;
	ulong status, dev;

	if(!e->iso)
		nbuf = (nbuf+1)&~1;
	id = (e->x<<7)|(e->dev->x&0x7F);
	dev = ((maxn-1)<<21) | ((id&0x7FF)<<8) | pid;
	status = 0;
	if(e->dev->ls)
		status |= LowSpeed;
	ft = lt = nil;
	for(i=0; i<nbuf; i++){
		t = alloctd(&ubus);
		t->ep = e;
		t->status = status;
		t->dev =  dev;
		if(!e->iso)
			dev ^= IsDATA1;
		if(pid == TokIN){
			t->bp = allocb(maxn);
			t->buffer = PADDR(t->bp->wp);
		}
		if(ft != nil){
			lt->next = t;
			lt->link = PADDR(t);
		}else
			ft = t;
		lt = t;
	}
	if(lt != nil)
		lt->link = PADDR(ft);	/* loop to form ring */
	return ft;
}

static QH *
allocqh(Ctlr *ub)
{
	QH *qh;

	ilock(ub);
	qh = ub->freeqh;
	if(qh == nil)
		panic("allocqh");	/* TO DO */
	ub->freeqh = qh->next;
	qh->next = nil;
	iunlock(ub);
	qh->head = Terminate;
	qh->entries = Terminate;
	qh->first = nil;
	qh->last = nil;
	return qh;
}

static void
freeqh(Ctlr *ub, QH *qh)
{
	ilock(ub);
	qh->next = ub->freeqh;
	ub->freeqh = qh;
	iunlock(ub);
}

static void
dumpqh(QH *q)
{
	int i;
	QH *q0;

	q0 = q;
	for(i = 0; q != nil && i < 10; i++){
		pprint("qh %8.8lux: %8.8lux %8.8lux\n", q, q->head, q->entries);
		if((q->entries & Terminate) == 0)
			dumptd(TFOL(q->entries), 1);
		if(q->head & Terminate)
			break;
		if((q->head & IsQH) == 0){
			pprint("head:");
			dumptd(TFOL(q->head), 1);
			break;
		}
		q = QFOL(q->head);
		if(q == q0)
			break;	/* looped */
	}
}

static void
queuetd(Ctlr *ub, QH *q, TD *t, int vf)
{
	TD *lt;

	for(lt = t; lt->next != nil; lt = lt->next)
		lt->link = PADDR(lt->next) | vf;
	lt->link = Terminate;
	ilock(ub);
	if(q->first != nil){
		q->last->link = PADDR(t) | vf;
		q->last->next = t;
	}else{
		q->first = t;
		q->entries = PADDR(t);
	}
	q->last = lt;
	iunlock(ub);
}

static void
cleantd(TD *t, int discard)
{
	Block *b;
	int n, err;

	XPRINT("cleanTD: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
	if(t->ep != nil && t->ep->debug)
		dumptd(t, 0);
	if(t->status & Active)
		panic("cleantd Active");
	err = t->status & (AnyError&~NAKed);
	/* TO DO: on t->status&AnyError, q->entries will not have advanced */
	if (err)
		print("cleanTD: Error %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
	switch(t->dev&0xFF){
	case TokIN:
		if(discard || (t->flags & CancelTD) || t->ep == nil || t->ep->x!=0&&err){
			if(t->ep != nil){
				if(err != 0)
					t->ep->err = err==Stalled? Estalled: Eio;
				wakeup(&t->ep->rr);	/* in case anyone cares */
			}
			break;
		}
		b = t->bp;
		n = (t->status + 1) & 0x7FF;
		if(n > b->lim - b->wp)
			n = 0;
		b->wp += n;
		if(Chatty)
			dumpdata(b, n);
		t->bp = nil;
		t->ep->nbytes += n;
		t->ep->nblocks++;
		qpass(t->ep->rq, b);	/* TO DO: flow control */
		wakeup(&t->ep->rr);	/* TO DO */
		break;
	case TokSETUP:
		XPRINT("cleanTD: TokSETUP %lux\n", &t->ep);
		/* don't really need to wakeup: subsequent IN or OUT gives status */
		if(t->ep != nil) {
			wakeup(&t->ep->wr);	/* TO DO */
			XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr);
		}
		break;
	case TokOUT:
		/* TO DO: mark it done somewhere */
		XPRINT("cleanTD: TokOut %lux\n", &t->ep);
		if(t->ep != nil){
			if(t->bp){
				n = BLEN(t->bp);
				t->ep->nbytes += n;
				t->ep->nblocks++;
			}
			if(t->ep->x!=0 && err != 0)
				t->ep->err = err==Stalled? Estalled: Eio;
			if(--t->ep->ntd < 0)
				panic("cleantd ntd");
			wakeup(&t->ep->wr);	/* TO DO */
			XPRINT("cleanTD: wakeup %lux\n", &t->ep->wr);
		}
		break;
	}
	freetd(t);
}

static void
cleanq(QH *q, int discard)
{
	TD *t;
	Ctlr *ub;

	ub = &ubus;
	ilock(ub);
	for(t = q->first; t != nil;){
//		XPRINT("cleanq: %8.8lux %8.8lux %8.8lux %8.8lux\n", t->link, t->status, t->dev, t->buffer);
		if(t->status & Active){
			if(t->flags & CancelTD){
				XPRINT("cancelTD: %8.8lux\n", (ulong)t);
				t->status = (t->status & ~Active) | IOC;	/* ensure interrupt next frame */
				t = t->next;
				continue;
			}
			break;
		}
		t->status &= ~IOC;
		/* TO DO: need to deal with TDs as chain if necessary */
		q->first = t->next;
		if(q->first != nil)
			q->entries = PADDR(q->first);
		else
			q->entries = Terminate;
		t->next = nil;
		iunlock(ub);
		cleantd(t, discard);
		ilock(ub);
		t = q->first;
	}
	iunlock(ub);
}

static void
canceltds(Ctlr *ub, QH *q, Endpt *e)
{
	TD *t;

	if(q != nil){
		ilock(ub);
		for(t = q->first; t != nil; t = t->next)
			if(t->ep == e)
				t->flags |= CancelTD;
		iunlock(ub);
		pprint("cancel:\n");
		dumpqh(q);
	}
}

static void
eptcancel(Endpt *e)
{
	Ctlr *ub;

	if(e == nil)
		return;
	ub = &ubus;
	canceltds(ub, e->epq, e);
	canceltds(ub, ub->ctlq, e);
	canceltds(ub, ub->bulkq, e);
}

static void
eptactivate(Endpt *e)
{
	ilock(&activends);
	if(e->active == 0){
		e->active = 1;
		e->activef = activends.f;
		activends.f = e;
	}
	iunlock(&activends);
}

static void
eptdeactivate(Endpt *e)
{
	Endpt **l;

	/* could be O(1) but not worth it yet */
	ilock(&activends);
	if(e->active){
		e->active = 0;
		for(l = &activends.f; *l != e; l = &(*l)->activef)
			if(*l == nil){
				iunlock(&activends);
				panic("usb eptdeactivate");
			}
		*l = e->activef;
	}
	iunlock(&activends);
}

static QH*
qxmit(Endpt *e, Block *b, int pid)
{
	TD *t;
	int n, vf;
	Ctlr *ub;
	QH *qh;

	if(b != nil){
		n = BLEN(b);
		t = alloctde(e, pid, n);
		t->bp = b;
		t->buffer = PADDR(b->rp);
	}else
		t = alloctde(e, pid, 0);
	ub = &ubus;
	ilock(ub);
	e->ntd++;
	iunlock(ub);
if(e->debug)pprint("QTD: %8.8lux n=%ld\n", t, b?BLEN(b): 0);
	vf = 0;
	if(e->x == 0){
		qh = ub->ctlq;
		vf = 0;
	}else if((qh = e->epq) == nil || e->mode != OWRITE){
		qh = ub->bulkq;
		vf = Vf;
	}
	queuetd(ub, qh, t, vf);
	return qh;
}

static QH*
qrcv(Endpt *e)
{
	TD *t;
	Block *b;
	Ctlr *ub;
	QH *qh;
	int vf;

	t = alloctde(e, TokIN, e->maxpkt);
	b = allocb(e->maxpkt);
	t->bp = b;
	t->buffer = PADDR(b->wp);
	ub = &ubus;
	vf = 0;
	if(e->x == 0){
		qh = ub->ctlq;
		vf = 0;
	}else if((qh = e->epq) == nil || e->mode != OREAD){
		qh = ub->bulkq;
		vf = Vf;
	}
	queuetd(ub, qh, t, vf);
	return qh;
}

static Block *
usbreq(int type, int req, int value, int offset, int count)
{
	Block *b;

	b = allocb(8);
	b->wp[0] = type;
	b->wp[1] = req;
	PUT2(b->wp+2, value);
	PUT2(b->wp+4, offset);
	PUT2(b->wp+6, count);
	b->wp += 8;
	return b;
}

/*
 * return smallest power of 2 >= n
 */
static int
flog2(int n)
{
	int i;

	for(i=0; (1<<i)<n; i++)
		;
	return i;
}

/*
 * build the periodic scheduling tree:
 * framesize must be a multiple of the tree size
 */
static QTree *
mkqhtree(ulong *frame, int framesize, int maxms)
{
	int i, n, d, o, leaf0, depth;
	QH *tree, *qh;
	QTree *qt;

	depth = flog2(maxms);
	n = (1<<(depth+1))-1;
	qt = mallocz(sizeof(*qt), 1);
	if(qt == nil)
		return nil;
	qt->nel = n;
	qt->depth = depth;
	qt->bw = mallocz(n*sizeof(qt->bw), 1);
	if(qt->bw == nil){
		free(qt);
		return nil;
	}
	tree = xspanalloc(n*sizeof(QH), 16, 0);
	if(tree == nil){
		free(qt);
		return nil;
	}
	qt->root = tree;
	tree->head = Terminate;	/* root */
	tree->entries = Terminate;
	for(i=1; i<n; i++){
		qh = &tree[i];
		qh->head = PADDR(&tree[(i-1)/2]) | IsQH;
		qh->entries = Terminate;
	}
	/* distribute leaves evenly round the frame list */
	leaf0 = n/2;
	for(i=0; i<framesize; i++){
		o = 0;
		for(d=0; d<depth; d++){
			o <<= 1;
			if(i & (1<<d))
				o |= 1;
		}
		if(leaf0+o >= n){
			pprint("leaf0=%d o=%d i=%d n=%d\n", leaf0, o, i, n);
			break;
		}
		frame[i] = PADDR(&tree[leaf0+o]) | IsQH;
	}
	return qt;
}

static void
dumpframe(int f, int t)
{
	QH *q, *tree;
	ulong p, *frame;
	int i, n;

	n = ubus.tree->nel;
	tree = ubus.tree->root;
	frame = ubus.frames;
	if(f < 0)
		f = 0;
	if(t < 0)
		t = 32;
	for(i=f; i<t; i++){
		pprint("F%.2d %8.8lux %8.8lux\n", i, frame[i], QFOL(frame[i])->head);
		for(p=frame[i]; (p & IsQH) && (p &Terminate) == 0; p = q->head){
			q = QFOL(p);
			if(!(q >= tree && q < &tree[n])){
				pprint("Q: p=%8.8lux out of range\n", p);
				break;
			}
			pprint("  -> %8.8lux h=%8.8lux e=%8.8lux\n", p, q->head, q->entries);
		}
	}
}

static int
pickschedq(QTree *qt, int pollms, ulong bw, ulong limit)
{
	int i, j, d, ub, q;
	ulong best, worst, total;

	d = flog2(pollms);
	if(d > qt->depth)
		d = qt->depth;
	q = -1;
	worst = 0;
	best = ~0;
	ub = (1<<(d+1))-1;
	for(i=(1<<d)-1; i<ub; i++){
		total = qt->bw[0];
		for(j=i; j > 0; j=(j-1)/2)
			total += qt->bw[j];
		if(total < best){
			best = total;
			q = i;
		}
		if(total > worst)
			worst = total;
	}
	if(worst+bw >= limit)
		return -1;
	return q;
}

static int
schedendpt(Endpt *e)
{
	Ctlr *ub;
	QH *qh;
	int q;

	if(!e->periodic || e->sched >= 0)
		return 0;
	ub = &ubus;
	if(e->epq == nil){
		e->epq = allocqh(ub);
		if(e->epq == nil)
			return -1;
	}
	qlock(ub->tree);
	q = pickschedq(ub->tree, e->pollms, e->bw, ~0);	/* TO DO: bus bandwidth limit */
	if(q < 0)
		return -1;
	ub->tree->bw[q] += e->bw;
	qh = &ub->tree->root[q];
	e->sched = q;
	e->epq->head = qh->entries;
	e->epq->entries = Terminate;
	qh->entries = PADDR(e->epq) | IsQH;
	qunlock(ub->tree);
	return 0;
}

static void
unschedendpt(Endpt *e)
{
	Ctlr *ub;
	ulong p;
	QH *qh;
	int q;

	ub = &ubus;
	if(e->epq == nil || (q = e->sched) < 0)
		return;
	p = PADDR(e->epq) | IsQH;
	qlock(ub->tree);
	ub->tree->bw[q] -= e->bw;
	qh = &ub->tree->root[q];
	for(; qh->entries != p; qh = QFOL(qh->entries))
		if(qh->entries & Terminate || (qh->entries & IsQH) == 0){
			qunlock(ub->tree);
			panic("usb: unschedendpt");
		}
	qh->entries = e->epq->head;
	qunlock(ub->tree);
	e->epq->head = Terminate;
}

static Endpt *
devendpt(Udev *d, int id, int add)
{
	Endpt *e, **p;

	p = &d->ep[id&0xF];
	lock(d);
	if((e = *p) != nil){
		incref(e);
		unlock(d);
		return e;
	}
	unlock(d);
	if(!add)
		return nil;
	e = mallocz(sizeof(*e), 1);
	e->ref = 1;
	e->x = id&0xF;
	e->id = id;
	e->periodic = 0;
	e->sched = -1;
	e->maxpkt = 8;
	e->pollms = 0;
	e->bw = 0;
	e->nbuf = 1;
	e->dev = d;
	e->active = 0;
	lock(d);
	if(*p != nil){
		incref(*p);
		unlock(d);
		free(e);
		return *p;
	}
	*p = e;
	unlock(d);
	e->rq = qopen(8*1024, 0, nil, e);
	e->wq = qopen(8*1024, 0, nil, e);
	return e;
}

static void
freept(Endpt *e)
{
	if(e != nil && decref(e) == 0){
		XPRINT("freept(%d,%d)\n", e->dev->x, e->x);
		unschedendpt(e);
		e->dev->ep[e->x] = nil;
		eptdeactivate(e);
		if(e->epq != nil)
			freeqh(&ubus, e->epq);
		free(e);
	}
}

static void
usbdevreset(Udev *d)
{
	d->state = Disabled;
	if(d->class == Hubclass)
		for(d = d->ports; d != nil; d = d->next)
			usbdevreset(d);
}

static void
freedev(Udev *d)
{
	int i;

	if(d != nil && decref(d) == 0){
		for(i=0; i<nelem(d->ep); i++)
			freept(d->ep[i]);
		if(d->x >= 0)
			usbdev[d->x] = nil;
		free(d);
	}
}

static void
hubportreset(Udev *h, int p)
{
	USED(h, p);
	/* reset state of each attached device? */
}

static	int	ioports[] = {-1, Portsc0, Portsc1};

static void
portreset(int port)
{
	Ctlr *ub;
	int i, p;

	/* should check that device not being configured on other port? */
	p = ioports[port];
	ub = &ubus;
	qlock(&ub->resetl);
	if(waserror()){
		qunlock(&ub->resetl);
		nexterror();
	}
	XPRINT("r: %x\n", IN(p));
	ilock(ub);
	OUT(p, PortReset);
	delay(12);	/* BUG */
	XPRINT("r2: %x\n", IN(p));
	OUT(p, IN(p) & ~PortReset);
	XPRINT("r3: %x\n", IN(p));
	OUT(p, IN(p) | PortEnable);
	microdelay(64);
	for(i=0; i<1000 && (IN(p) & PortEnable) == 0; i++)
		;
	XPRINT("r': %x %d\n", IN(p), i);
	OUT(p, (IN(p) & ~PortReset)|PortEnable);
	iunlock(ub);
	hubportreset(nil, port);
	poperror();
	qunlock(&ub->resetl);
}

static void
portenable(int port, int on)
{
	Ctlr *ub;
	int w, p;

	/* should check that device not being configured on other port? */
	p = ioports[port];
	ub = &ubus;
	qlock(&ub->resetl);
	if(waserror()){
		qunlock(&ub->resetl);
		nexterror();
	}
	ilock(ub);
	w = IN(p);
	if(on)
		w |= PortEnable;
	else
		w &= ~PortEnable;
	OUT(p, w);
	microdelay(64);
	iunlock(ub);
	XPRINT("e: %x\n", IN(p));
	if(!on)
		hubportreset(nil, port);
	poperror();
	qunlock(&ub->resetl);
}

static int
portinfo(Ctlr *ub, int *p0, int *p1)
{
	int m, v;

	ilock(ub);
	m = 0;
	if((v = IN(Portsc0)) & PortChange){
		OUT(Portsc0, v);
		m |= 1<<0;
	}
	*p0 = v;
	if((v = IN(Portsc1)) & PortChange){
		OUT(Portsc1, v);
		m |= 1<<1;
	}
	*p1 = v;
	iunlock(ub);
	return m;
}

static void
interrupt(Ureg*, void *a)
{
	Ctlr *ub;
	Endpt *e;
	int s;

	ub = a;
	s = IN(Status);
	if (s & 0x1a) {
		print("usbint: #%x f%d\n", s, IN(Frnum));
		print("cmd #%x sofmod #%x\n", IN(Cmd), inb(ub->io+SOFMod));
		print("sc0 #%x sc1 #%x\n", IN(Portsc0), IN(Portsc1));
	}
	OUT(Status, s);

//	XPRINT("cleanq(ub->ctlq, 0)\n");
	cleanq(ub->ctlq, 0);
//	XPRINT("cleanq(ub->bulkq, 0)\n");
	cleanq(ub->bulkq, 0);
	ilock(&activends);
	for(e = activends.f; e != nil; e = e->activef)
		if(e->epq != nil) {
//			XPRINT("cleanq(e->epq, 0)\n");
			cleanq(e->epq, 0);
		}
	iunlock(&activends);
}

static void
resetusbctlr(void)
{
	Ctlr *ub;
	Pcidev *cfg;
	int i;
	ulong port;
	QTree *qt;
	TD *t;

	ub = &ubus;
	memset(&cfg, 0, sizeof(cfg));
	cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 4*/
	if(cfg == nil) {
//		cfg = pcimatch(0, 0x8086, 0x7112);	/* Intel chipset PIIX 3*/
//		if(cfg == nil) {
			cfg = pcimatch(0, 0x1106, 0x0586);	/* Via chipset */
			if(cfg == nil) {
				DPRINT("No USB device found\n");
				return;
			}
//		}
	}
	port = cfg->mem[4].bar & ~0x0F;
	if (port == 0) {
		print("usb: failed to map registers\n");
		return;
	}

	print("USB: %x/%x port 0x%lux size 0x%x irq %d\n",
		cfg->vid, cfg->did, port, cfg->mem[4].size, cfg->intl);

	i = inb(port+SOFMod);
	if(0){
		OUT(Cmd, 4);	/* global reset */
		delay(15);
		OUT(Cmd, 0);	/* end reset */
		delay(4);
	}
	outb(port+SOFMod, i);
	// Interrupt handler
	intrenable(cfg->intl, interrupt, ub, cfg->tbdf, "usb");

	ub->io = port;
	ub->tdpool = xspanalloc(128*sizeof(TD), 16, 0);
	for(i=128; --i>=0;){
		ub->tdpool[i].next = ub->freetd;
		ub->freetd = &ub->tdpool[i];
	}
	ub->qhpool = xspanalloc(32*sizeof(QH), 16, 0);
	for(i=32; --i>=0;){
		ub->qhpool[i].next = ub->freeqh;
		ub->freeqh = &ub->qhpool[i];
	}

	/*
	 * the root of the periodic (interrupt & isochronous) scheduling tree
	 * points to the control queue and the bandwidth sop for bulk traffic.
	 * this is looped following the instructions in PIIX4 errata 29773804.pdf:
	 * a QH links to a looped but inactive TD as its sole entry,
	 * with its head entry leading on to the bulk traffic, the last QH of which
	 * links back to the empty QH.
	 */
	ub->bulkq = allocqh(ub);
	t = alloctd(ub);	/* inactive TD, looped */
	t->link = PADDR(t);
	ub->bwsop = allocqh(ub);
	ub->bwsop->head = PADDR(ub->bulkq) | IsQH;
	ub->bwsop->entries = PADDR(t);
	ub->ctlq = allocqh(ub);
	ub->ctlq->head = PADDR(ub->bwsop) | IsQH;
//	ub->bulkq->head = PADDR(ub->bwsop) | IsQH;	/* loop back */
	print("usbcmd\t0x%.4x\nusbsts\t0x%.4x\nusbintr\t0x%.4x\nfrnum\t0x%.2x\n",
		IN(Cmd), IN(Status), IN(Usbintr), inb(port+Frnum));
	print("frbaseadd\t0x%.4x\nsofmod\t0x%x\nportsc1\t0x%.4x\nportsc2\t0x%.4x\n",
		IN(Flbaseadd), inb(port+SOFMod), IN(Portsc0), IN(Portsc1));
	OUT(Cmd, 0);	/* stop */
	ub->frames = xspanalloc(FRAMESIZE, FRAMESIZE, 0);
	qt = mkqhtree(ub->frames, NFRAME, 32);
	if(qt == nil){
		print("usb: can't allocate scheduling tree\n");
		ub->io = 0;
		return;
	}
	qt->root->head = PADDR(ub->ctlq) | IsQH;
	ub->tree = qt;
	print("usb tree: nel=%d depth=%d\n", qt->nel, qt->depth);

	outl(port+Flbaseadd, PADDR(ub->frames));
	OUT(Frnum, 0);
	OUT(Usbintr, 0xF);	/* enable all interrupts */
	print("cmd 0x%x sofmod 0x%x\n", IN(Cmd), inb(port+SOFMod));
	print("sc0 0x%x sc1 0x%x\n", IN(Portsc0), IN(Portsc1));
}

enum
{
	Qtopdir = 0,
	Q2nd,
	Qbusctl,
	Qnew,
	Qport,
	Q3rd,
	Qctl,
	Qsetup,
	Qdebug,
	Qstatus,
	Qep0,
	/* other endpoint files */
};

/*
 * Qid path is:
 *	 8 bits of file type (qids above)
 *	10 bits of slot number +1; 0 means not attached to device
 */
#define	QSHIFT	8	/* location in qid of device # */
#define	QMASK	((1<<QSHIFT)-1)

#define	QID(q)		((q).path&QMASK)
#define	DEVPATH(q)	(((q)&~CHDIR)>>QSHIFT)

static Dirtab usbdir2[] = {
	"new",	{Qnew},	0,	0666,
	"ctl",		{Qbusctl},	0,	0666,
	"port",	{Qport},	0,	0444,
};

static Dirtab usbdir3[]={
	"ctl",		{Qctl},	0,	0666,
	"setup",	{Qsetup},	0,	0666,
	"status",	{Qstatus},	0,	0444,
	"debug",	{Qdebug},	1,	0666,
	/* epNdata names are generated on demand */
};

static Udev *
usbdeviceofpath(ulong path)
{
	int s;

	s = DEVPATH(path);
	if(s == 0)
		return nil;
	return usbdev[s-1];
}

static Udev *
usbdevice(Chan *c)
{
	Udev *d;

	d = usbdeviceofpath(c->qid.path);
	if(d == nil || d->id != c->qid.vers || d->state == Disabled)
		error(Ehungup);
	return d;
}

static Udev *
usbnewdevice(void)
{
	Udev *d;
	Endpt *e;
	int i;

	d = nil;
	qlock(&usbstate);
	if(waserror()){
		qunlock(&usbstate);
		nexterror();
	}
	for(i=0; i<nelem(usbdev); i++)
		if(usbdev[i] == nil){
			ubus.idgen++;
			d = mallocz(sizeof(*d), 1);
			d->ref = 1;
			d->x = i;
			d->id = (ubus.idgen << 8) | i;
			d->state = Enabled;
			e = devendpt(d, 0, 1);	/* always provide control endpoint 0 */
			e->mode = ORDWR;
			e->periodic = 0;
			e->sched = -1;
			usbdev[i] = d;
			break;
		}
	poperror();
	qunlock(&usbstate);
	return d;
}

static void
usbreset(void)
{
	resetusbctlr();
}

void
usbinit(void)
{
	Udev *d;

	if(ubus.io != 0 && usbdev[0] == nil){
		d = usbnewdevice();	/* reserve device 0 for configuration */
		incref(d);
		d->state = Attached;
	}
}

Chan *
usbattach(char *spec)
{
	Ctlr *ub;

	ub = &ubus;
	if(ub->io == 0)
		error(Enodev);
	if((IN(Cmd)&1)==0 || *spec)
		OUT(Cmd, 1);	/* run */
//	pprint("at: c=%x s=%x c0=%x\n", IN(Cmd), IN(Status), IN(Portsc0));
	return devattach('U', spec);
}

Chan *
usbclone(Chan *c, Chan *nc)
{
	return devclone(c, nc);
}

static int
usbgen(Chan *c, Dirtab*, int, int s, Dir *dp)
{
	int t;
	Qid q;
	ulong path;
	Udev *d;
	Dirtab *tab;
	Endpt *e;
	char buf[NAMELEN];

	/*
	 * Top level directory contains the name of the device.
	 */
	if(c->qid.path == CHDIR){
		if(s == 0){
			devdir(c, (Qid){CHDIR|Q2nd, 0}, "usb", 0, eve, 0555, dp);
			return 1;
		}
		return -1;
	}

	/*
	 * Second level contains "new" plus all the clients.
	 */
	t = QID(c->qid);
	if(t < Q3rd){
		if(s < nelem(usbdir2)){
			tab = &usbdir2[s];
			devdir(c, tab->qid, tab->name, tab->length, eve, tab->perm, dp);
			return 1;
		}
		if((s -= nelem(usbdir2)) >= 0 && s < nelem(usbdev)){
			d = usbdev[s];
			if(d == nil)
				return 0;
			sprint(buf, "%d", s);
			q = (Qid){CHDIR|((s+1)<<QSHIFT)|Q3rd, d->id};
			devdir(c, q, buf, 0, eve, 0555, dp);
			return 1;
		}
		return -1;
	}

	/*
	 * Third level.
	 */
	path = c->qid.path&~(CHDIR|QMASK);	/* slot component */
	q.vers = c->qid.vers;
	if(s < nelem(usbdir3)){
		Dirtab *tab = &usbdir3[s];
		q.path = path | tab->qid.path;
		devdir(c, q, tab->name, tab->length, eve, tab->perm, dp);
		return 1;
	}

	/* active endpoints */
	d = usbdeviceofpath(path);
	if(d == nil)
		return -1;
	s -= nelem(usbdir3);
	if(s < 0 || s >= nelem(d->ep))
		return -1;
	if(s == 0 || (e = d->ep[s]) == nil)	/* ep0data is called "setup" */
		return 0;
	sprint(buf, "ep%ddata", s);
	q.path = path | (Qep0+s);
	devdir(c, q, buf, 0, eve, e->mode==OREAD? 0444: e->mode==OWRITE? 0222: 0666, dp);
	return 1;
}

int
usbwalk(Chan *c, char *name)
{
	if(strcmp(name, "..") == 0){
		switch(QID(c->qid)){
		case Qtopdir:
			return 1;
		case Q2nd:
			c->qid = (Qid){CHDIR|Qtopdir, 0};
			break;
		case Q3rd:
			c->qid = (Qid){CHDIR|Q2nd, 0};
			break;
		default:
			panic("usbwalk %lux", c->qid.path);
		}
		return 1;
	}
	return devwalk(c, name, nil, 0, usbgen);
}

void
usbstat(Chan *c, char *db)
{
	devstat(c, db, nil, 0, usbgen);
}

Chan *
usbopen(Chan *c, int omode)
{
	Udev *d;
	int f, s;

	if(c->qid.path & CHDIR)
		return devopen(c, omode, nil, 0, usbgen);

	f = 0;
	if(QID(c->qid) == Qnew){
		d = usbnewdevice();
		if(d == nil)
			error(Enodev);
		c->qid.path = Qctl|((d->x+1)<<QSHIFT);
		c->qid.vers = d->id;
		f = 1;
	}

	if(c->qid.path < Q3rd)
		return devopen(c, omode, nil, 0, usbgen);

	qlock(&usbstate);
	if(waserror()){
		qunlock(&usbstate);
		nexterror();
	}

	switch(QID(c->qid)){
	case Qctl:
		d = usbdevice(c);
		if(0&&d->busy)
			error(Einuse);
		d->busy = 1;
		if(!f)
			incref(d);
		break;

	default:
		d = usbdevice(c);
		s = QID(c->qid) - Qep0;
		if(s >= 0 && s < nelem(d->ep)){
			Endpt *e;
			if((e = d->ep[s]) == nil)
				error(Enodev);
			if(schedendpt(e) < 0)
				error("can't schedule USB endpoint");
			eptactivate(e);
		}
		incref(d);
		break;
	}
	poperror();
	qunlock(&usbstate);
	c->mode = openmode(omode);
	c->flag |= COPEN;
	c->offset = 0;
	return c;
}

void
usbcreate(Chan *c, char *name, int omode, ulong perm)
{
	USED(c, name, omode, perm);
	error(Eperm);
}

void
usbremove(Chan*)
{
	error(Eperm);
}

void
usbwstat(Chan *c, char *dp)
{
	USED(c, dp);
	error(Eperm);
}

void
usbclose(Chan *c)
{
	Udev *d;

	if(c->qid.path & CHDIR || c->qid.path < Q3rd)
		return;
	qlock(&usbstate);
	if(waserror()){
		qunlock(&usbstate);
		nexterror();
	}
	d = usbdevice(c);
	if(QID(c->qid) == Qctl)
		d->busy = 0;
	if(c->flag & COPEN)
		freedev(d);
	poperror();
	qunlock(&usbstate);
}

static int
eptinput(void *arg)
{
	Endpt *e;

	e = arg;
	return e->eof || e->err || qcanread(e->rq);
}

static long
readusb(Endpt *e, void *a, long n)
{
	Block *b;
	uchar *p;
	long l, i;

	qlock(&e->rlock);
	if(waserror()){
		qunlock(&e->rlock);
		eptcancel(e);
		nexterror();
	}
	p = a;
	do {
		if(e->eof) {
			pprint("e->eof\n");
			break;
		}
		if(e->err)
			error(e->err);
		qrcv(e);
		if(!e->iso)
			e->data01 ^= 1;
		sleep(&e->rr, eptinput, e);
		if(e->err)
			error(e->err);
		b = qget(e->rq);	/* TO DO */
		if(b == nil) {
			pprint("b == nil\n");
			break;
		}
		if(waserror()){
			freeb(b);
			nexterror();
		}
		l = BLEN(b);
		if((i = l) > n)
			i = n;
		if(i > 0){
			memmove(p, b->rp, i);
			p += i;
		}
		poperror();
		freeb(b);
		n -= i;
	} while (l == e->maxpkt && n > 0);
	poperror();
	qunlock(&e->rlock);
	return p-(uchar*)a;
}

long
usbread(Chan *c, void *a, long n, vlong offset)
{
	Endpt *e;
	Udev *d;
	char buf[48], *s;
	int t, w0, w1, ps, l, i;

	if(c->qid.path & CHDIR)
		return devdirread(c, a, n, nil, 0, usbgen);

	t = QID(c->qid);
	switch(t){
	case Qbusctl:
		snprint(buf, sizeof(buf), "%11d %11d ", 0, 0);
		return readstr(offset, a, n, buf);

	case Qport:
		ps = portinfo(&ubus, &w0, &w1);
		snprint(buf, sizeof(buf), "0x%ux 0x%ux 0x%ux ", ps, w0, w1);
		return readstr(offset, a, n, buf);

	case Qctl:
		d = usbdevice(c);
		sprint(buf, "%11d %11d ", d->x, d->id);
		return readstr(offset, a, n, buf);

	case Qsetup:	/* endpoint 0 */
		d = usbdevice(c);
		if((e = d->ep[0]) == nil)
			error(Eio);	/* can't happen */
		e->data01 = 1;
		n = readusb(e, a, n);
		if(e->setin){
			e->setin = 0;
			e->data01 = 1;
			writeusb(e, "", 0, TokOUT);
		}
		break;

	case Qdebug:
		n=0;
		break;

	case Qstatus:
		d = usbdevice(c);
		s = smalloc(READSTR);
		if(waserror()){
			free(s);
			nexterror();
		}
		l = snprint(s, READSTR, "%s %d %d %d\n", devstates[d->state], d->class, d->subclass, d->proto);
		for(i=0; i<nelem(d->ep); i++)
			if((e = d->ep[i]) != nil)	/* TO DO: freeze e */
				l += snprint(s+l, READSTR-l, "%d bytes %lud blocks %lud\n", i, e->nbytes, e->nblocks);
		n = readstr(offset, a, n, s);
		poperror();
		free(s);
		break;

	default:
		d = usbdevice(c);
		if((t -= Qep0) < 0 || t >= nelem(d->ep))
			error(Eio);
		if((e = d->ep[t]) == nil || e->mode == OWRITE)
			error(Eio);	/* can't happen */
		n=readusb(e, a, n);
		break;
	}
	return n;
}

static int
qisempty(void *arg)
{
	return ((QH*)arg)->entries & Terminate;
}

static long
writeusb(Endpt *e, void *a, long n, int tok)
{
	long i;
	Block *b;
	uchar *p;
	QH *qh;

	p = a;
	qlock(&e->wlock);
	if(waserror()){
		qunlock(&e->wlock);
		eptcancel(e);
		nexterror();
	}
	do {
		int j;

		if(e->err)
			error(e->err);
		if((i = n) >= e->maxpkt)
			i = e->maxpkt;
		b = allocb(i);
		if(waserror()){
			freeb(b);
			nexterror();
		}
		XPRINT("out [%ld]", i);
		for (j = 0; j < i; j++) XPRINT(" %.2x", p[j]);
		XPRINT("\n");
		memmove(b->wp, p, i);
		b->wp += i;
		p += i;
		n -= i;
		poperror();
		qh = qxmit(e, b, tok);
		tok = TokOUT;
		if(!e->iso)
			e->data01 ^= 1;
		if(e->ntd >= e->nbuf) {
			XPRINT("writeusb: sleep %lux\n", &e->wr);
			sleep(&e->wr, qisempty, qh);
			XPRINT("writeusb: awake\n");
		}
	} while(n > 0);
	poperror();
	qunlock(&e->wlock);
	return p-(uchar*)a;
}

int
getfields(char *ss, char **sp, int nptrs)
{
	uchar *s = (uchar*)ss;
	uchar **p = (uchar**)sp;
	uint c;

	c = 0;
	for(;;){
		if(--nptrs < 0)
			break;
		*p++ = s;
		do {
			c = *s++;
			if (c == ' ') break;
			if (c == '\t') break;
			if (c == '\n') break;
		} while(c);
		if(c == 0)
			break;
		s[-1] = 0;
	}
	if(nptrs > 0)
		*p = 0;
	else
	if(--s >= (uchar*)ss)
		*s = c;
	return p - (uchar**)sp;
}

long
usbwrite(Chan *c, void *a, long n, vlong)
{
	Udev *d;
	Endpt *e;
	int id, nw, nf, t, i;
	char cmd[50], *fields[10];

	if(c->qid.path & CHDIR)
		error(Egreg);
	t = QID(c->qid);
	if(t == Qbusctl){
		if(n >= sizeof(cmd)-1)
			n = sizeof(cmd)-1;
		memmove(cmd, a, n);
		cmd[n] = 0;
		nf = getfields(cmd, fields, nelem(fields));
		if(nf==1 && strcmp(fields[0], "dump")==0){
			dumpframe(-1, -1);
			return n;
		}
		if(nf < 2)
			error(Ebadarg);
		id = strtol(fields[1], nil, 0);
		if(id != 1 && id != 2)
			error(Ebadarg);	/* there are two ports on the root hub */
		if(strcmp(fields[0], "reset") == 0)
			portreset(id);
		else if(strcmp(fields[0], "enable") == 0)
			portenable(id, 1);
		else if(strcmp(fields[0], "disable") == 0)
			portenable(id, 0);
		else
			error(Ebadarg);
		return n;
	}
	d = usbdevice(c);
	t = QID(c->qid);
	switch(t){
	case Qctl:
		if(n >= sizeof(cmd)-1)
			n = sizeof(cmd)-1;
		memmove(cmd, a, n);
		cmd[n] = 0;
		nf = getfields(cmd, fields, nelem(fields));
		if(nf > 1 && strcmp(fields[0], "speed") == 0){
			d->ls = strtoul(fields[1], nil, 0) == 0;
		} else if(nf > 4 && strcmp(fields[0], "class") == 0){
			/* class class subclass proto */
			d->class = strtoul(fields[4], nil, 0);
			d->subclass = strtoul(fields[5], nil, 0);
			d->proto = strtoul(fields[6], nil, 0);
		}else if(nf > 2 && strcmp(fields[0], "data") == 0){
			i = strtoul(fields[1], nil, 0);
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
				error(Ebadarg);
			e = d->ep[i];
			e->data01 = strtoul(fields[2], nil, 0) != 0;
		}else if(nf > 2 && strcmp(fields[0], "maxpkt") == 0){
			i = strtoul(fields[1], nil, 0);
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
				error(Ebadarg);
			e = d->ep[i];
			e->maxpkt = strtoul(fields[2], nil, 0);
			if(e->maxpkt > 1500)
				e->maxpkt = 1500;
		}else if(nf > 2 && strcmp(fields[0], "debug") == 0){
			i = strtoul(fields[1], nil, 0);
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
				error(Ebadarg);
			e = d->ep[i];
			e->debug = strtoul(fields[2], nil, 0);
		}else if(nf > 1 && strcmp(fields[0], "unstall") == 0){
			i = strtoul(fields[1], nil, 0);
			if(i < 0 || i >= nelem(d->ep) || d->ep[i] == nil)
				error(Ebadarg);
			e = d->ep[i];
			e->err = nil;
		}else if(nf == 6 && strcmp(fields[0], "ep") == 0){
			/* ep n maxpkt mode poll nbuf */
			i = strtoul(fields[1], nil, 0);
			if(i < 0 || i >= nelem(d->ep))
				error(Ebadarg);
			if(d->ep[i] != nil)
				error(Einuse);
			e = devendpt(d, i, 1);
			if(waserror()){
				freept(e);
				nexterror();
			}
			i = strtoul(fields[2], nil, 0);
			if(i < 8 || i > 1023)
				i = 8;
			e->maxpkt = i;
			e->mode = strcmp(fields[3],"r")==0? OREAD: strcmp(fields[3],"w") == 0? OWRITE: ORDWR;
			e->periodic = 0;
			e->sched = -1;
			if(strcmp(fields[4], "bulk") != 0){
				e->periodic = 1;
				i = strtoul(fields[4], nil, 0);
				if(i > 0 && i <= 1000)
					e->pollms = i;
				else
					error(Ebadarg);
			}
			i = strtoul(fields[5], nil, 0);
			if(i >= 1 && i <= 32)
				e->nbuf = i;
			poperror();
		}else
			error(Ebadarg);
		return n;

	case Qsetup:	/* SETUP endpoint 0 */
		/* should canqlock etc */
		if((e = d->ep[0]) == nil)
			error(Eio);	/* can't happen */
		if(n < 8 || n > 1023)
			error(Eio);
		nw = *(uchar*)a & RD2H;
		e->data01 = 0;
		n = writeusb(e, a, n, TokSETUP);
		if(nw == 0){	/* host to device: use IN[DATA1] to ack */
			e->data01 = 1;
			nw = readusb(e, cmd, 8);
			if(nw != 0)
				error(Eio);	/* could provide more status */
		}else
			e->setin = 1;	/* two-phase */
		break;

	default:	/* sends DATA[01] */
		if((t -= Qep0) < 0 || t >= nelem(d->ep))
			error(Eio);
		if((e = d->ep[t]) == nil || e->mode == OREAD)
			error(Eio);	/* can't happen */
		n = writeusb(e, a, n, TokOUT);
		break;
	}
	return n;
}

Dev usbdevtab = {
	'U',
	"usb",

	usbreset,
	usbinit,
	usbattach,
	usbclone,
	usbwalk,
	usbstat,
	usbopen,
	devcreate,
	usbclose,
	usbread,
	devbread,
	usbwrite,
	devbwrite,
	devremove,
	devwstat,
};

M pc/devvga.c => pc/devvga.c +84 -80
@@ 15,16 15,10 @@

enum {
	Qdir,
	Qvgaiob,
	Qvgaiow,
	Qvgaiol,
	Qvgactl,
};

static Dirtab vgadir[] = {
	"vgaiob",	{ Qvgaiob, 0 },		0,	0660,
	"vgaiow",	{ Qvgaiow, 0 },		0,	0660,
	"vgaiol",	{ Qvgaiol, 0 },		0,	0660,
	"vgactl",	{ Qvgactl, 0 },		0,	0660,
};



@@ 87,10 81,8 @@ checkport(int start, int end)
static long
vgaread(Chan* c, void* a, long n, vlong off)
{
	int len, port;
	int len;
	char *p, *s;
	ushort *sp;
	ulong *lp;
	VGAscr *scr;
	ulong offset = off;



@@ 115,7 107,7 @@ vgaread(Chan* c, void* a, long n, vlong off)
		if(scr->gscreen)
			len += snprint(p+len, READSTR-len, "size: %dx%dx%d\n",
				scr->gscreen->r.max.x, scr->gscreen->r.max.y,
				1<<scr->gscreen->ldepth);
				scr->gscreen->depth);
		if(scr->cur)
			s = scr->cur->name;
		else


@@ 129,33 121,6 @@ vgaread(Chan* c, void* a, long n, vlong off)

		return n;

	case Qvgaiob:
		port = offset;
		checkport(offset, offset+n);
		for(p = a; port < offset+n; port++)
			*p++ = inb(port);
		return n;

	case Qvgaiow:
		if((n & 0x01) || (offset & 0x01))
			error(Ebadarg);
		checkport(offset, offset+n+1);
		n /= 2;
		sp = a;
		for(port = offset; port < offset+n; port += 2)
			*sp++ = ins(port);
		return n*2;

	case Qvgaiol:
		if((n & 0x03) || (offset & 0x03))
			error(Ebadarg);
		checkport(offset, offset+n+3);
		n /= 4;
		lp = a;
		for(port = offset; port < offset+n; port += 4)
			*lp++ = inl(port);
		return n*4;

	default:
		error(Egreg);
		break;


@@ 168,19 133,26 @@ static void
vgactl(char* a)
{
	int align, i, n, size, x, y, z;
	char *field[4], *p;
	char *chanstr, *field[6], *p;
	ulong chan;
	VGAscr *scr;
	extern VGAdev *vgadev[];
	extern VGAcur *vgacur[];
	Rectangle r;

	n = parsefields(a, field, 4, " ");
	if(n < 2)
	n = parsefields(a, field, nelem(field), " ");
	if(n < 1)
		error(Ebadarg);

	scr = &vgascreen[0];
	if(strcmp(field[0], "hwgc") == 0){
		if(n < 2)
			error(Ebadarg);

		/* BUG: drawinit should become a different message rather than piggybacking */
		if(scr && scr->dev && scr->dev->drawinit)
			scr->dev->drawinit(scr);

		if(strcmp(field[1], "off") == 0){
			lock(&cursor);
			if(scr->cur){


@@ 221,7 193,7 @@ vgactl(char* a)
		}
	}
	else if(strcmp(field[0], "size") == 0){
		if(n < 2)
		if(n < 3)
			error(Ebadarg);
		x = strtoul(field[1], &p, 0);
		if(x == 0 || x > 2048)


@@ 235,23 207,67 @@ vgactl(char* a)
		if(*p)
			p++;

		switch(strtoul(p, &p, 0)){
		case 8:
			z = 3;
			break;
		z = strtoul(p, &p, 0);

		default:
			z = 0;
			error(Ebadarg);
		}
		chanstr = field[2];
		if((chan = strtochan(chanstr)) == 0)
			error("bad channel");

		if(chantodepth(chan) != z)
			error("depth, channel do not match");

		cursoroff(1);
		if(screensize(x, y, z))
		deletescreenimage();
		if(screensize(x, y, z, chan))
			error(Egreg);
		vgascreenwin(scr);
		cursoron(1);
		return;
	}
	else if(strcmp(field[0], "actualsize") == 0){
		if(scr->gscreen == nil)
			error("set the screen size first");

		if(n < 2)
			error(Ebadarg);
		x = strtoul(field[1], &p, 0);
		if(x == 0 || x > 2048)
			error(Ebadarg);
		if(*p)
			p++;

		y = strtoul(p, nil, 0);
		if(y == 0 || y > 2048)
			error(Ebadarg);

		if(x > scr->gscreen->r.max.x || y > scr->gscreen->r.max.y)
			error("physical screen bigger than virtual");

		r = Rect(0,0,x,y);
		if(!eqrect(r, scr->gscreen->r)){
			if(scr->cur == nil || scr->cur->doespanning == 0)
				error("virtual screen not supported");
		}

		physgscreenr = r;
		return;
	}
	else if(strcmp(field[0], "palettedepth") == 0){
		if(n < 2)
			error(Ebadarg);

		x = strtoul(field[1], &p, 0);
		if(x != 8 && x != 6)
			error(Ebadarg);

		scr->palettedepth = x;
		return;
	}
	else if(strcmp(field[0], "drawinit") == 0){
		if(scr && scr->dev && scr->dev->drawinit)
			scr->dev->drawinit(scr);
		return;
	}
	else if(strcmp(field[0], "linear") == 0){
		if(n < 2)
			error(Ebadarg);


@@ 265,6 281,24 @@ vgactl(char* a)
			error("not enough free address space");
		return;
	}
/*	else if(strcmp(field[0], "memset") == 0){
		if(n < 4)
			error(Ebadarg);
		memset((void*)strtoul(field[1], 0, 0), atoi(field[2]), atoi(field[3]));
		return;
	}
*/
	else if(strcmp(field[0], "blank") == 0){
		if(n < 2)
			error(Ebadarg);
		drawblankscreen(atoi(field[1]));
		return;
	}
	else if(strcmp(field[0], "hwacceloff") == 0){
		scr->fill = nil;
		scr->scroll = nil;
		return;
	}

	error(Ebadarg);
}


@@ 272,10 306,7 @@ vgactl(char* a)
static long
vgawrite(Chan* c, void* a, long n, vlong off)
{
	int port;
	char *p;
	ushort *sp;
	ulong *lp;
	ulong offset = off;

	switch(c->qid.path & ~CHDIR){


@@ 298,33 329,6 @@ vgawrite(Chan* c, void* a, long n, vlong off)
		free(p);
		return n;

	case Qvgaiob:
		p = a;
		checkport(offset, offset+n);
		for(port = offset; port < offset+n; port++)
			outb(port, *p++);
		return n;

	case Qvgaiow:
		if((n & 01) || (offset & 01))
			error(Ebadarg);
		checkport(offset, offset+n+1);
		n /= 2;
		sp = a;
		for(port = offset; port < offset+n; port += 2)
			outs(port, *sp++);
		return n*2;

	case Qvgaiol:
		if((n & 0x03) || (offset & 0x03))
			error(Ebadarg);
		checkport(offset, offset+n+3);
		n /= 4;
		lp = a;
		for(port = offset; port < offset+n; port += 4)
			outl(port, *lp++);
		return n*4;

	default:
		error(Egreg);
		break;

M pc/kbd.c => pc/kbd.c +9 -9
@@ 25,7 25,7 @@ enum {

	PF=		Spec|0x20,	/* num pad function key */
	View=		Spec|0x00,	/* view (shift window up) */
	KF=		Spec|0x40,	/* function key */
	KF=		0xF000,	/* function key (begin Unicode private space) */
	Shift=		Spec|0x60,
	Break=		Spec|0x61,
	Ctrl=		Spec|0x62,


@@ 39,16 39,16 @@ enum {
	Up=		KF|14,
	Pgup=		KF|15,
	Print=		KF|16,
	Left=		View,
	Right=		View,
	Left=		KF|17,
	Right=		KF|18,
	End=		'\r',
	Down=		View,
	Pgdown=		View,
	Pgdown=		KF|19,
	Ins=		KF|20,
	Del=		0x7F,
};

uchar kbtab[] = 
Rune kbtab[] = 
{
[0x00]	No,	0x1b,	'1',	'2',	'3',	'4',	'5',	'6',
[0x08]	'7',	'8',	'9',	'0',	'-',	'=',	'\b',	'\t',


@@ 68,7 68,7 @@ uchar kbtab[] =
[0x78]	No,	View,	No,	KF|14,	No,	No,	No,	No,
};

uchar kbtabshift[] =
Rune kbtabshift[] =
{
[0x00]	No,	0x1b,	'!',	'@',	'#',	'$',	'%',	'^',
[0x08]	'&',	'*',	'(',	')',	'_',	'+',	'\b',	'\t',


@@ 88,7 88,7 @@ uchar kbtabshift[] =
[0x78]	No,	KF|14,	No,	KF|14,	No,	No,	No,	No,
};

uchar kbtabesc1[] =
Rune kbtabesc1[] =
{
[0x00]	No,	No,	No,	No,	No,	No,	No,	No,
[0x08]	No,	No,	No,	No,	No,	No,	No,	No,


@@ 231,7 231,7 @@ i8042intr(Ureg*, void*)
	static int esc1, esc2;
	static int alt, caps, ctl, num, shift;
	static int collecting, nk;
	static uchar kc[5];
	static Rune kc[5];
	int keyup;

	/*


@@ 314,7 314,7 @@ i8042intr(Ureg*, void*)
	/*
 	 *  normal character
	 */
	if(!(c & Spec)){
	if(!(c & (Spec|KF))){
		if(ctl){
			if(alt && c == Del)
				exit(0);

M pc/screen.c => pc/screen.c +267 -64
@@ 13,26 13,14 @@
#include <cursor.h>
#include "screen.h"

static ulong onesbits = ~0;
static Memdata onesdata = {
	nil,
	&onesbits,
};
static Memimage xones = {
	{ 0, 0, 1, 1 },
	{ -100000, -100000, 100000, 100000 },
	3,
	1,
	&onesdata,
	0,
	1
};
Memimage *memones = &xones;
#define RGB2K(r,g,b)	((156763*(r)+307758*(g)+59769*(b))>>19)

Point ZP = {0, 0};

Rectangle physgscreenr;

Memdata gscreendata;
Memimage gscreen;
Memimage *gscreen;

VGAscr vgascreen[1];



@@ 51,10 39,11 @@ Cursor	arrow = {
};

int
screensize(int x, int y, int z)
screensize(int x, int y, int z, ulong chan)
{
	VGAscr *scr;

	memimageinit();
	scr = &vgascreen[0];

	/*


@@ 62,36 51,38 @@ screensize(int x, int y, int z)
	 * be given back if aperture is set.
	 */
	if(scr->aperture == 0){
		int width = (x*(1<<z))/BI2WD;
		int width = (x*z)/BI2WD;

		gscreendata.data = xalloc(width*BY2WD*y);
		if(gscreendata.data == 0)
		gscreendata.bdata = xalloc(width*BY2WD*y);
		if(gscreendata.bdata == 0)
			error("screensize: vga soft memory");
		memset(gscreendata.data, Backgnd, width*BY2WD*y);
/*		memset(gscreendata.bdata, 0x72, width*BY2WD*y);	/* not really black */
		scr->useflush = 1;

		scr->aperture = 0xA0000;
		scr->apsize = 1<<16;
	}
	else
		gscreendata.data = KADDR(scr->aperture);
		gscreendata.bdata = KADDR(scr->aperture);

	gscreen.data = &gscreendata;
	gscreen.ldepth = z;
	gscreen.width = (x*(1<<gscreen.ldepth)+31)/32;
	gscreen.r.min = ZP;
	gscreen.r.max = Pt(x, y);
	gscreen.clipr = gscreen.r;
	gscreen.repl = 0;
	if(gscreen)
		freememimage(gscreen);

	scr->gscreendata = gscreen.data;
	scr->memdefont = getmemdefont();
	scr->gscreen = &gscreen;
	gscreen = allocmemimaged(Rect(0,0,x,y), chan, &gscreendata);
	vgaimageinit(chan);
	if(gscreen == nil)
		return -1;

//	memset(gscreen.data->data, Backgnd, scr->apsize);
/*	memset(gscreen->data->bdata, 0x15, (x*y*z+7)/8);	/* RSC BUG */
	memfillcolor(gscreen, DRed);

	drawcmap(0);
	scr->palettedepth = 6;	/* default */
	scr->gscreendata = &gscreendata;
	scr->memdefont = getmemdefont();
	scr->gscreen = gscreen;

	physgscreenr = gscreen->r;

	drawcmap(1);
	return 0;
}



@@ 114,8 105,7 @@ screenaperture(int size, int align)
		aperture = scr->dev->linear(scr, &size, &align);
		if(aperture == 0)
			return 1;
	}
	else{
	}else{
		aperture = upamalloc(0, size, align);
		if(aperture == 0)
			return 1;


@@ 131,8 121,8 @@ screenaperture(int size, int align)
	return 0;
}

ulong*
attachscreen(Rectangle* r, int* ld, int* width, int *softscreen)
uchar*
attachscreen(Rectangle* r, ulong* chan, int* d, int* width, int *softscreen)
{
	VGAscr *scr;



@@ 141,13 131,17 @@ attachscreen(Rectangle* r, int* ld, int* width, int *softscreen)
		return nil;

	*r = scr->gscreen->r;
	*ld = scr->gscreen->ldepth;
	*chan = scr->gscreen->chan;
	*d = scr->gscreen->depth;
	*width = scr->gscreen->width;
	*softscreen = scr->useflush;

	return scr->gscreendata->data;
	return scr->gscreendata->bdata;
}

/*
 * It would be fair to say that this doesn't work for >8-bit screens.
 */
void
flushmemscreen(Rectangle r)
{


@@ 166,27 160,28 @@ flushmemscreen(Rectangle r)

	incs = scr->gscreen->width * BY2WD;

	switch(scr->gscreen->ldepth){
	switch(scr->gscreen->depth){
	default:
		len = 0;
		panic("flushmemscreen: ldepth\n");
		panic("flushmemscreen: depth\n");
		break;
	case 3:
	case 8:
		len = Dx(r);
		break;
	}
	if(len < 1)
		return;

	off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x>>(3-scr->gscreen->ldepth));
	off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8;
	page = off/scr->apsize;
	off %= scr->apsize;
	disp = KADDR(scr->aperture);
	sdisp = disp+off;
	edisp = disp+scr->apsize;

	off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x>>(3-scr->gscreen->ldepth));
	sp = ((uchar*)scr->gscreendata->data) + off;
	off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8;

	sp = scr->gscreendata->bdata + off;

	scr->dev->page(scr, page);
	for(y = r.min.y; y < r.max.y; y++) {


@@ 225,11 220,11 @@ getcolor(ulong p, ulong* pr, ulong* pg, ulong* pb)
	if(scr->gscreen == nil)
		return;

	switch(scr->gscreen->ldepth){
	switch(scr->gscreen->depth){
	default:
		x = 0x0F;
		break;
	case 3:
	case 8:
		x = 0xFF;
		break;
	}


@@ 244,37 239,58 @@ getcolor(ulong p, ulong* pr, ulong* pg, ulong* pb)
}

int
setpalette(ulong p, ulong r, ulong g, ulong b)
{
	VGAscr *scr;
	int d;

	scr = &vgascreen[0];
	d = scr->palettedepth;

	lock(&cursor);
	scr->colormap[p][0] = r;
	scr->colormap[p][1] = g;
	scr->colormap[p][2] = b;
	vgao(PaddrW, p);
	vgao(Pdata, r>>(32-d));
	vgao(Pdata, g>>(32-d));
	vgao(Pdata, b>>(32-d));
	unlock(&cursor);

	return ~0;
}

/*
 * On some video cards (e.g. Mach64), the palette is used as the 
 * DAC registers for >8-bit modes.  We don't want to set them when the user
 * is trying to set a colormap and the card is in one of these modes.
 */
int
setcolor(ulong p, ulong r, ulong g, ulong b)
{
	VGAscr *scr;
	ulong x;
	int x;

	scr = &vgascreen[0];
	if(scr->gscreen == nil)
		return 0;

	switch(scr->gscreen->ldepth){
	default:
	switch(scr->gscreen->depth){
	case 1:
	case 2:
	case 4:
		x = 0x0F;
		break;
	case 3:
	case 8:
		x = 0xFF;
		break;
	default:
		return 0;
	}
	p &= x;
	p ^= x;

	lock(&cursor);
	scr->colormap[p][0] = r;
	scr->colormap[p][1] = g;
	scr->colormap[p][2] = b;
	vgao(PaddrW, p);
	vgao(Pdata, r>>(32-6));
	vgao(Pdata, g>>(32-6));
	vgao(Pdata, b>>(32-6));
	unlock(&cursor);

	return ~0;
	return setpalette(p, r, g, b);
}

int


@@ 312,3 328,190 @@ setcursor(Cursor* curs)

	scr->cur->load(scr, curs);
}

static ulong
pixelbits(Memimage *i, Point pt)
{
	uchar *p;
	ulong val;
	int off, bpp, npack;

	val = 0;
	p = byteaddr(i, pt);
	switch(bpp=i->depth){
	case 1:
	case 2:
	case 4:
		npack = 8/bpp;
		off = pt.x%npack;
		val = p[0] >> bpp*(npack-1-off);
		val &= (1<<bpp)-1;
		break;
	case 8:
		val = p[0];
		break;
	case 16:
		val = p[0]|(p[1]<<8);
		break;
	case 24:
		val = p[0]|(p[1]<<8)|(p[2]<<16);
		break;
	case 32:
		val = p[0]|(p[1]<<8)|(p[2]<<16)|(p[3]<<24);
		break;
	}
	while(bpp<32){
		val |= val<<bpp;
		bpp *= 2;
	}
	return val;
}


static ulong
imgtorgba(Memimage *img, ulong val)
{
	uchar r, g, b, a;
	int nb, ov, v;
	ulong chan;
	uchar *p;

	a = 0xFF;
	r = g = b = 0xAA;	/* garbage */
	for(chan=img->chan; chan; chan>>=8){
		nb = NBITS(chan);
		ov = v = val&((1<<nb)-1);
		val >>= nb;

		while(nb < 8){
			v |= v<<nb;
			nb *= 2;
		}
		v >>= (nb-8);

		switch(TYPE(chan)){
		case CRed:
			r = v;
			break;
		case CGreen:
			g = v;
			break;
		case CBlue:
			b = v;
			break;
		case CAlpha:
			a = v;
			break;
		case CGrey:
			r = g = b = v;
			break;
		case CMap:
			p = img->cmap->cmap2rgb+3*ov;
			r = *p++;
			g = *p++;	
			b = *p;
			break;
		}
	}
	return (r<<24)|(g<<16)|(b<<8)|a;	
}

static ulong
rgbatoimg(Memimage *img, ulong rgba)
{
	ulong chan;
	int d, nb;
	ulong v;
	uchar *p, r, g, b, a, m;

	v = 0;
	r = rgba>>24;
	g = rgba>>16;
	b = rgba>>8;
	a = rgba;
	d = 0;
	for(chan=img->chan; chan; chan>>=8){
		nb = NBITS(chan);
		switch(TYPE(chan)){
		case CRed:
			v |= (r>>(8-nb))<<d;
			break;
		case CGreen:
			v |= (g>>(8-nb))<<d;
			break;
		case CBlue:
			v |= (b>>(8-nb))<<d;
			break;
		case CAlpha:
			v |= (a>>(8-nb))<<d;
			break;
		case CMap:
			p = img->cmap->rgb2cmap;
			m = p[(r>>4)*256+(g>>4)*16+(b>>4)];
			v |= m<<d;
			break;
		case CGrey:
			m = RGB2K(r,g,b);
			v |= m<<d;
			break;
		}
		d += nb;
	}
//	print("rgba2img %.8lux = %.*lux\n", rgba, 2*d/8, v);
	return v;
}

Memimage *lastbadi;
Memdata *lastbad;
Memimage *lastbadsrc, *lastbaddst;

#ifndef SDFSDF
int
hwdraw(Memdrawparam *par)
{
	VGAscr *scr;
	Memimage *dst, *src;

	dst = par->dst;
	scr = &vgascreen[0];
	if(dst == nil || dst->data == nil)
		return 0;

	if(dst->data->bdata != gscreendata.bdata)
		return 0;

//	if(dst->data != &gscreendata){
//		lastbad = dst->data;
//		lastbadi = dst;
//		return 0;
//	}

	if(scr->fill==nil && scr->scroll==nil)
		return 0;

	/*
	 * If we have an opaque mask and source is one opaque pixel we can convert to the
	 * destination format and just replicate with memset.
	 */
	if(scr->fill && (par->state&(Simplemask|Simplesrc|Fullmask))==(Simplemask|Simplesrc|Fullmask))
		return scr->fill(scr, par->r, par->sdval);

	/*
	 * If no source alpha, an opaque mask, we can just copy the
	 * source onto the destination.  If the channels are the same and
	 * the source is not replicated, memmove suffices.
	 */
	src = par->src;
	if(scr->scroll && src->data->bdata==dst->data->bdata && !(src->flags&Falpha)
	&& (par->state&(Simplemask|Fullmask))==(Simplemask|Fullmask)){
		if(src->zero != dst->zero){
			lastbadsrc = src;
			lastbaddst = dst;
			iprint("#");
		}
		return scr->scroll(scr, par->r, par->sr);
	}

	return 0;	
}
#endif

M pc/screen.h => pc/screen.h +20 -2
@@ 66,6 66,9 @@ struct VGAdev {
	void	(*disable)(VGAscr*);
	void	(*page)(VGAscr*, int);
	ulong	(*linear)(VGAscr*, int*, int*);
	void	(*drawinit)(VGAscr*);
	int	(*fill)(VGAscr*, Rectangle, ulong);

};

struct VGAcur {


@@ 75,6 78,8 @@ struct VGAcur {
	void	(*disable)(VGAscr*);
	void	(*load)(VGAscr*, Cursor*);
	int	(*move)(VGAscr*, Point);

	int	doespanning;
};

/*


@@ 96,15 101,22 @@ struct VGAscr {
	ulong	io;				/* device specific registers */

	ulong	colormap[Pcolours][3];
	int	palettedepth;

	ulong	*mmio;
	Memimage* gscreen;
	Memdata* gscreendata;
	Memsubfont* memdefont;

	int	(*fill)(VGAscr*, Rectangle, ulong);
	int	(*scroll)(VGAscr*, Rectangle, Rectangle);
	ulong	id;	/* internal identifier for driver use */
};

extern VGAscr vgascreen[];

enum {
	Backgnd		= Pwhite,
	Backgnd		= 0,	/* black */
};

/* mouse.c */


@@ 115,8 127,14 @@ extern void	flushmemscreen(Rectangle);
extern int	cursoron(int);
extern void	cursoroff(int);
extern void	setcursor(Cursor*);
extern int	screensize(int, int, int);
extern int	screensize(int, int, int, ulong);
extern int	screenaperture(int, int);
extern Rectangle physgscreenr;	/* actual monitor size */
extern void	deletescreenimage(void);

/* vga.c */
extern void	vgascreenwin(VGAscr*);
extern void	vgaimageinit(ulong);
extern ulong	vgapcilinear(VGAscr*, int*, int*, int, int);

extern void	drawblankscreen(int);

M pc/vga.c => pc/vga.c +34 -36
@@ 11,42 11,37 @@
#include <cursor.h>
#include "screen.h"

static ulong backbits = (Backgnd<<24)|(Backgnd<<16)|(Backgnd<<8)|Backgnd;
static Memdata backdata = {
	nil,
	&backbits
};
static Memimage xback = {
	{ 0, 0, 1, 1 },
	{ -100000, -100000, 100000, 100000 },
	3,
	1,
	&backdata,
	0,
	1
};
static Memimage* back = &xback;

static ulong consbits = 0;
static Memdata consdata = {
	nil,
	&consbits
};
static Memimage conscol = {
	{ 0, 0, 1, 1 },
	{ -100000, -100000, 100000, 100000 },
	3,
	1,
	&consdata,
	0,
	1
};
static Memimage* back;
static Memimage *conscol;

static Point curpos;
static Rectangle window;
static int *xp;
static int xbuf[256];
static Lock vgascreenlock;
int drawdebug;

void
vgaimageinit(ulong chan)
{
	if(back == nil){
		back = allocmemimage(Rect(0,0,1,1), chan);	/* RSC BUG */
		if(back == nil)
			panic("back alloc");		/* RSC BUG */
		back->flags |= Frepl;
		back->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
		memfillcolor(back, DBlack);
	}

	if(conscol == nil){
		conscol = allocmemimage(Rect(0,0,1,1), chan);	/* RSC BUG */
		if(conscol == nil)
			panic("conscol alloc");	/* RSC BUG */
		conscol->flags |= Frepl;
		conscol->clipr = Rect(-0x3FFFFFF, -0x3FFFFFF, 0x3FFFFFF, 0x3FFFFFF);
		memfillcolor(conscol, DWhite);
	}
}

static void
vgascroll(VGAscr* scr)


@@ 59,9 54,9 @@ vgascroll(VGAscr* scr)
	o = 8*h;
	r = Rpt(window.min, Pt(window.max.x, window.max.y-o));
	p = Pt(window.min.x, window.min.y+o);
	memimagedraw(scr->gscreen, r, scr->gscreen, p, memones, p);
	memimagedraw(scr->gscreen, r, scr->gscreen, p, nil, p);
	r = Rpt(Pt(window.min.x, window.max.y-o), window.max);
	memimagedraw(scr->gscreen, r, back, ZP, memones, ZP);
	memimagedraw(scr->gscreen, r, back, ZP, nil, ZP);

	curpos.y -= o;
}


@@ 73,6 68,7 @@ vgascreenputc(VGAscr* scr, char* buf, Rectangle *flushr)
	int h, w, pos;
	Rectangle r;

//	drawdebug = 1;
	if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)])
		xp = xbuf;



@@ 100,7 96,7 @@ vgascreenputc(VGAscr* scr, char* buf, Rectangle *flushr)
		pos = (curpos.x-window.min.x)/w;
		pos = 4-(pos%4);
		r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y+h);
		memimagedraw(scr->gscreen, r, back, back->r.min, memones, back->r.min);
		memimagedraw(scr->gscreen, r, back, back->r.min, nil, ZP);
		bbox(flushr, r);
		curpos.x += pos*w;
		break;


@@ 110,7 106,7 @@ vgascreenputc(VGAscr* scr, char* buf, Rectangle *flushr)
			break;
		xp--;
		r = Rect(*xp, curpos.y, curpos.x, curpos.y+h);
		memimagedraw(scr->gscreen, r, back, back->r.min, memones, back->r.min);
		memimagedraw(scr->gscreen, r, back, back->r.min, nil, ZP);
		bbox(flushr, r);
		curpos.x = *xp;
		break;


@@ 124,11 120,12 @@ vgascreenputc(VGAscr* scr, char* buf, Rectangle *flushr)

		*xp++ = curpos.x;
		r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h);
		memimagedraw(scr->gscreen, r, back, back->r.min, memones, back->r.min);
		memimagestring(scr->gscreen, curpos, &conscol, scr->memdefont, buf);
		memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min);
		memimagestring(scr->gscreen, curpos, conscol, scr->memdefont, buf);
		bbox(flushr, r);
		curpos.x += w;
	}
//	drawdebug = 0;
}

static void


@@ 192,3 189,4 @@ vgascreenwin(VGAscr* scr)

	screenputs = vgascreenputs;
}


M pc/vgaark2000pv.c => pc/vgaark2000pv.c +6 -6
@@ 55,12 55,12 @@ ark2000pvenable(VGAscr* scr)
	vgaxo(Seqx, 0x20, seq20);
	seq20 |= 0x18;

	vgaxo(Seqx, 0x26, Pwhite);
	vgaxo(Seqx, 0x27, Pwhite);
	vgaxo(Seqx, 0x28, Pwhite);
	vgaxo(Seqx, 0x29, Pblack);
	vgaxo(Seqx, 0x2A, Pblack);
	vgaxo(Seqx, 0x2B, Pblack);
	vgaxo(Seqx, 0x26, 0x00);
	vgaxo(Seqx, 0x27, 0x00);
	vgaxo(Seqx, 0x28, 0x00);
	vgaxo(Seqx, 0x29, 0xFF);
	vgaxo(Seqx, 0x2A, 0xFF);
	vgaxo(Seqx, 0x2B, 0xFF);

	/*
	 * Cursor storage is a 256 byte or 1Kb block located in the last

M pc/vgacyber938x.c => pc/vgacyber938x.c +2 -2
@@ 167,9 167,9 @@ cyber938xcurenable(VGAscr* scr)
	 * Cursor colours.
	 */
	for(i = 0x48; i < 0x4C; i++)
		vgaxo(Crtx, i, Pwhite);
		vgaxo(Crtx, i, 0x00);
	for(i = 0x4C; i < 0x50; i++)
		vgaxo(Crtx, i, Pblack);
		vgaxo(Crtx, i, 0xFF);

	/*
	 * Find a place for the cursor data in display memory.

M pc/vgamach64xx.c => pc/vgamach64xx.c +397 -6
@@ 13,7 13,7 @@
#include "screen.h"

/*
 * ATI Mach64(CT|ET|G*|VT|VU).
 * ATI Mach64(CT|ET|G*|VT|VU|LP).
 */
static ushort mach64xxdid[] = {
	('C'<<8)|'T',


@@ 25,13 25,16 @@ static ushort mach64xxdid[] = {
	('G'<<8)|'Q',
	('G'<<8)|'T',
	('G'<<8)|'U',
	('G'<<8)|'V',
	('G'<<8)|'Z',
	('V'<<8)|'T',
	('V'<<8)|'U',
	('L'<<8)|'P',
	0,
};

static int hwfill(VGAscr*, Rectangle, ulong);
static int hwscroll(VGAscr*, Rectangle, Rectangle);
static void initengine(VGAscr*);

static Pcidev*
mach64xxpci(void)
{


@@ 66,6 69,7 @@ mach64xxenable(VGAscr* scr)
		 * this will do for now.
		 */
		scr->io = p->mem[1].bar & ~0x03;

		if(scr->io == 0 && p->did == ('C'<<8)|'T')
			scr->io = 0x2EC;
	}


@@ 74,10 78,12 @@ mach64xxenable(VGAscr* scr)
static ulong
mach64xxlinear(VGAscr* scr, int* size, int* align)
{
	ulong aperture, oaperture;
	ulong aperture, osize, oaperture;
	int oapsize, wasupamem;
	Pcidev *p;
	Physseg seg;

	osize = *size;
	oaperture = scr->aperture;
	oapsize = scr->apsize;
	wasupamem = scr->isupamem;


@@ 100,27 106,126 @@ mach64xxlinear(VGAscr* scr, int* size, int* align)
	else
		scr->isupamem = 1;

	scr->mmio = (ulong*)(aperture+osize-0x400);
	if(oaperture)
		print("warning (BUG): redefinition of aperture does not change mach64mmio segment\n");
	memset(&seg, 0, sizeof(seg));
	seg.attr = SG_PHYSICAL;
	seg.name = smalloc(NAMELEN);
	snprint(seg.name, NAMELEN, "mach64mmio");
	seg.pa = aperture+osize - BY2PG;
	seg.size = BY2PG;
	addphysseg(&seg);

	seg.name = smalloc(NAMELEN);
	snprint(seg.name, NAMELEN, "mach64screen");
	seg.pa = aperture;
	seg.size = osize;
	addphysseg(&seg);

	return aperture;
}

enum {
	CrtcOffPitch	= 0x05,
	CrtcGenCtl	= 0x07,
	CurClr0		= 0x0B,		/* I/O Select */
	CurClr1		= 0x0C,
	CurOffset	= 0x0D,
	CurHVposn	= 0x0E,
	CurHVoff	= 0x0F,

	BusCntl	= 0x13,
	GenTestCntl	= 0x19,

	ContextMask	= 0x100,	/* not accessible via I/O */
	FifoStat,
	GuiStat,
	DpFrgdClr,
	DpBkgdClr,
	DpWriteMask,
	DpMix,
	DpPixWidth,
	DpSrc,
	ClrCmpCntl,
	GuiTrajCntl,
	ScLeftRight,
	ScTopBottom,
	DstOffPitch,
	DstYX,
	DstHeightWidth,
	DstCntl,
	DstHeight,
	DstBresErr,
	DstBresInc,
	DstBresDec,
	SrcCntl,
	SrcHeight1Width1,
	SrcHeight2Width2,
	SrcYX,
	SrcWidth1,
	SrcYXstart,
	HostCntl,
	PatReg0,
	PatReg1,
	PatCntl,
	ScBottom,
	ScLeft,
	ScRight,
	ScTop,
	ClrCmpClr,
	ClrCmpMask,
	DpChainMask,
	SrcOffPitch,	
};

static uchar mmoffset[] = {
	[CrtcOffPitch]	0x05,
	[CurClr0]	0x18,
	[CurClr1]	0x19,
	[CurOffset]	0x1A,
	[CurHVposn]	0x1B,
	[CurHVoff]	0x1C,

	[BusCntl]		0x28,
	[GenTestCntl]	0x34,
	[DstOffPitch]	0x40,
	[DstYX]		0x43,
	[DstHeight]	0x45,
	[DstHeightWidth]	0x46,
	[DstBresErr]	0x49,
	[DstBresInc]	0x4A,
	[DstBresDec]	0x4B,
	[DstCntl]		0x4C,
	[SrcOffPitch]	0x60,
	[SrcYX]	0x63,
	[SrcWidth1]	0x64,
	[SrcYXstart]	0x69,
	[SrcHeight1Width1]	0x66,
	[SrcHeight2Width2]	0x6C,
	[SrcCntl]		0x6D,
	[HostCntl]	0x90,
	[PatReg0]	0xA0,
	[PatReg1]	0xA1,
	[PatCntl]	0xA2,
	[ScLeft]	0xA8,
	[ScRight]	0xA9,
	[ScLeftRight]	0xAA,
	[ScTop]	0xAB,
	[ScBottom] 0xAC,
	[ScTopBottom]	0xAD,
	[DpBkgdClr]	0xB0,
	[DpFrgdClr]	0xB1,
	[DpWriteMask]	0xB2,
	[DpChainMask]	0xB3,
	[DpPixWidth]	0xB4,
	[DpMix]		0xB5,
	[DpSrc]		0xB6,
	[ClrCmpClr]	0xC0,
	[ClrCmpMask]	0xC1,
	[ClrCmpCntl]	0xC2,
	[FifoStat]		0xC4,
	[ContextMask]	0xC8,
	[GuiTrajCntl]	0xCC,
	[GuiStat]		0xCE,
};

static ulong


@@ 128,6 233,8 @@ ior32(VGAscr* scr, int r)
{
	if(scr->io == 0x2EC || scr->io == 0x1C8)
		return inl((r<<10)+scr->io);
	if(r >= 0x100 && scr->mmio != nil)
		return scr->mmio[mmoffset[r]];
	return inl((mmoffset[r]<<2)+scr->io);
}



@@ 136,6 243,8 @@ iow32(VGAscr* scr, int r, ulong l)
{
	if(scr->io == 0x2EC || scr->io == 0x1C8)
		outl(((r)<<10)+scr->io, l);
	else if(r >= 0x100 && scr->mmio != nil)
		scr->mmio[mmoffset[r]] = l;
	else
		outl((mmoffset[r]<<2)+scr->io, l);
}


@@ 208,9 317,75 @@ mach64xxcurload(VGAscr* scr, Cursor* curs)
}

static int
ptalmostinrect(Point p, Rectangle r)
{
	return p.x>=r.min.x && p.x<=r.max.x &&
	       p.y>=r.min.y && p.y<=r.max.y;
}

/*
 * If necessary, translate the rectangle physr
 * some multiple of [dx dy] so that it includes p.
 * Return 1 if the rectangle changed.
 */
static int
screenpan(Point p, Rectangle *physr, int dx, int dy)
{
	int d;

	if(ptalmostinrect(p, *physr))
		return 0;

	if(p.y < physr->min.y){
		d = physr->min.y - (p.y&~(dy-1));
		physr->min.y -= d;
		physr->max.y -= d;
	}
	if(p.y > physr->max.y){
		d = ((p.y+dy-1)&~(dy-1)) - physr->max.y;
		physr->min.y += d;
		physr->max.y += d;
	}

	if(p.x < physr->min.x){
		d = physr->min.x - (p.x&~(dx-1));
		physr->min.x -= d;
		physr->max.x -= d;
	}
	if(p.x > physr->max.x){
		d = ((p.x+dx-1)&~(dx-1)) - physr->max.x;
		physr->min.x += d;
		physr->max.x += d;
	}
	return 1;
}

static int
mach64xxcurmove(VGAscr* scr, Point p)
{
	int x, xo, y, yo;
	int dx;
	ulong off, pitch;

	/*
	 * If the point we want to display is outside the current
	 * screen rectangle, pan the screen to display it.
	 *
	 * We have to move in 64-bit chunks.
	 */
	if(scr->gscreen->depth == 24)
		dx = (64*3)/24;
	else
		dx = 64 / scr->gscreen->depth;

	if(screenpan(p, &physgscreenr, dx, 1)){
		off = (physgscreenr.min.y*Dx(scr->gscreen->r)+physgscreenr.min.x)/dx;
		pitch = Dx(scr->gscreen->r)/8;
		iow32(scr, CrtcOffPitch, (pitch<<22)|off);
	}

	p.x -= physgscreenr.min.x;
	p.y -= physgscreenr.min.y;

	/*
	 * Mustn't position the cursor offscreen even partially,


@@ 276,6 451,219 @@ mach64xxcurenable(VGAscr* scr)
	iow32(scr, GenTestCntl, 0x80|r);
}

static void
waitforfifo(VGAscr *scr, int entries)
{
	int x;
	x = 0;
	while((ior32(scr, FifoStat)&0xFF) > (0x8000>>entries) && x++ < 1000000)
		;
	if(x >= 1000000)
		iprint("fifo %d stat %.8lux %.8lux scrio %.8lux mmio %p scr %p pc %luX\n", entries, ior32(scr, FifoStat), scr->mmio[mmoffset[FifoStat]], scr->io, scr->mmio, scr, getcallerpc(&scr));
}

static void
waitforidle(VGAscr *scr)
{
	int x;
	waitforfifo(scr, 16);
	x = 0;
	while((ior32(scr, GuiStat)&1) && x++ < 1000000)
		;
	if(x >= 1000000)
		iprint("idle stat %.8lux %.8lux scrio %.8lux mmio %p scr %p pc %luX\n", ior32(scr, GuiStat), scr->mmio[mmoffset[GuiStat]], scr->io, scr->mmio, scr, getcallerpc(&scr));
}

static void
resetengine(VGAscr *scr)
{
	ulong x;
	x = ior32(scr, GenTestCntl);
	iow32(scr, GenTestCntl, x&~0x100);
	iow32(scr, GenTestCntl, x|0x100);
	iow32(scr, BusCntl, ior32(scr, BusCntl)|0x00A00000);
}

static void
initengine(VGAscr *scr)
{
	ulong pitch;

	pitch = Dx(scr->gscreen->r)/8;
	if(scr->gscreen->depth == 24)
		pitch *= 3;

	resetengine(scr);
	waitforfifo(scr, 14);
	iow32(scr, ContextMask, ~0);
	iow32(scr, DstOffPitch, pitch<<22);
	iow32(scr, DstYX, 0);
	iow32(scr, DstHeight, 0);
	iow32(scr, DstBresErr, 0);
	iow32(scr, DstBresInc, 0);
	iow32(scr, DstBresDec, 0);
	iow32(scr, DstCntl, 0x23);
	iow32(scr, SrcOffPitch, pitch<<22);
	iow32(scr, SrcYX, 0);
	iow32(scr, SrcHeight1Width1, 1);
	iow32(scr, SrcYXstart, 0);
	iow32(scr, SrcHeight2Width2, 1);
	iow32(scr, SrcCntl, 0x01);

	waitforfifo(scr, 13);
	iow32(scr, HostCntl, 0);
	iow32(scr, PatReg0, 0);
	iow32(scr, PatReg1, 0);
	iow32(scr, PatCntl, 0);
	iow32(scr, ScLeft, 0);
	iow32(scr, ScTop, 0);
	iow32(scr, ScBottom, 0xFFFF);
	iow32(scr, ScRight, 0xFFFF);
	iow32(scr, DpBkgdClr, 0);
	iow32(scr, DpFrgdClr, ~0);
	iow32(scr, DpWriteMask, ~0);
	iow32(scr, DpMix, 0x70003);
	iow32(scr, DpSrc, 0x00010100);

	waitforfifo(scr, 3);
	iow32(scr, ClrCmpClr, 0);
	iow32(scr, ClrCmpMask, ~0);
	iow32(scr, ClrCmpCntl, 0);

	waitforfifo(scr, 2);
	switch(scr->gscreen->depth){
	case 8:
	case 24:	/* [sic] */
		iow32(scr, DpPixWidth, 0x00020202);
		iow32(scr, DpChainMask, 0x8080);
		break;
	case 16:
		iow32(scr, DpPixWidth, 0x00040404);
		iow32(scr, DpChainMask, 0x8410);
		break;
	case 32:
		iow32(scr, DpPixWidth, 0x00060606);
		iow32(scr, DpChainMask, 0x8080);
		break;
	}

	waitforidle(scr);
}

static int
mach64hwfill(VGAscr *scr, Rectangle r, ulong sval)
{
	ulong pitch;
	ulong ctl;

if(drawdebug)
	iprint("hwfill %R val %lux...\n", r, sval);

	/* shouldn't happen */
	if(scr->io == 0x2EC || scr->io == 0x1C8 || scr->io == 0)
		return 0;

	pitch = Dx(scr->gscreen->r)/8;
	ctl = 1|2;	/* left-to-right, top-to-bottom */
	if(scr->gscreen->depth == 24){
		r.min.x *= 3;
		r.max.x *= 3;
		pitch *= 3;
		ctl |= (1<<7)|(((r.min.x/4)%6)<<8);
	}

	waitforfifo(scr, 11);
	iow32(scr, DpFrgdClr, sval);
	iow32(scr, DpWriteMask, 0xFFFFFFFF);
	iow32(scr, DpMix, 0x00070003);
	iow32(scr, DpSrc, 0x00000111);
	iow32(scr, ClrCmpCntl, 0x00000000);
	iow32(scr, ScLeftRight, 0x1FFF0000);
	iow32(scr, ScTopBottom, 0x1FFF0000);
	iow32(scr, DstOffPitch, pitch<<22);
	iow32(scr, DstCntl, ctl);
	iow32(scr, DstYX, (r.min.x<<16)|r.min.y);
	iow32(scr, DstHeightWidth, (Dx(r)<<16)|Dy(r));

	waitforidle(scr);
	return 1;
}

static int
mach64hwscroll(VGAscr *scr, Rectangle r, Rectangle sr)
{
	ulong pitch;
	Point dp, sp;
	ulong ctl;
	int dx, dy;

	dx = Dx(r);
	dy = Dy(r);
	pitch = Dx(scr->gscreen->r)/8;
	if(scr->gscreen->depth == 24){
		dx *= 3;
		pitch *= 3;
		r.min.x *= 3;
		sr.min.x *= 3;
	}

	ctl = 0;
	if(r.min.x <= sr.min.x){
		ctl |= 1;
		dp.x = r.min.x;
		sp.x = sr.min.x;
	}else{
		dp.x = r.min.x+dx-1;
		sp.x = sr.min.x+dx-1;
	}

	if(r.min.y <= sr.min.y){
		ctl |= 2;
		dp.y = r.min.y;
		sp.y = sr.min.y;
	}else{
		dp.y = r.min.y+dy-1;
		sp.y = sr.min.y+dy-1;
	}

	if(scr->gscreen->depth == 24)
		ctl |= (1<<7)|(((dp.x/4)%6)<<8);

	waitforfifo(scr, 6);
	iow32(scr, ScLeftRight, 0x1FFF0000);
	iow32(scr, ScTopBottom, 0x1FFF0000);
	iow32(scr, DpWriteMask, 0xFFFFFFFF);
	iow32(scr, DpMix, 0x00070003);
	iow32(scr, DpSrc, 0x00000300);
	iow32(scr, ClrCmpCntl, 0x00000000);

	waitforfifo(scr, 8);
	iow32(scr, SrcOffPitch, pitch<<22);
	iow32(scr, SrcCntl, 0x00000000);
	iow32(scr, SrcYX, (sp.x<<16)|sp.y);
	iow32(scr, SrcWidth1, dx);
	iow32(scr, DstOffPitch, pitch<<22);
	iow32(scr, DstCntl, ctl);

	iow32(scr, DstYX, (dp.x<<16)|dp.y);
	iow32(scr, DstHeightWidth, (dx<<16)|dy);

	waitforidle(scr);

	return 1;
}

static void
mach64xxdrawinit(VGAscr *scr)
{
	if(scr->io > 0x2FF){
		initengine(scr);
		scr->fill = mach64hwfill;
		scr->scroll = mach64hwscroll;
	}
}


VGAdev vgamach64xxdev = {
	"mach64xx",



@@ 283,6 671,7 @@ VGAdev vgamach64xxdev = {
	0,				/* disable */
	0,				/* page */
	mach64xxlinear,			/* linear */
	mach64xxdrawinit,	/* drawinit */
};

VGAcur vgamach64xxcur = {


@@ 292,4 681,6 @@ VGAcur vgamach64xxcur = {
	mach64xxcurdisable,		/* disable */
	mach64xxcurload,		/* load */
	mach64xxcurmove,		/* move */

	1					/* doespanning */
};

M pc/vgas3.c => pc/vgas3.c +196 -26
@@ 19,7 19,7 @@ s3pageset(VGAscr* scr, int page)
	int opage;

	crt35 = vgaxi(Crtx, 0x35);
	if(scr->gscreen->ldepth == 3){
	if(scr->gscreen->depth >= 8){
		/*
		 * The S3 registers need to be unlocked for this.
		 * Let's hope they are already:


@@ 64,14 64,14 @@ s3page(VGAscr* scr, int page)
static ulong
s3linear(VGAscr* scr, int* size, int* align)
{
	ulong aperture, oaperture, osize;
	int oapsize, wasupamem;
	ulong aperture, oaperture;
	int osize, oapsize, wasupamem;
	Pcidev *p;
	Physseg seg;

	osize = *size;
	oaperture = scr->aperture;
	oapsize = scr->apsize;
	osize = *size;
	wasupamem = scr->isupamem;
	if(wasupamem)
		upafree(oaperture, oapsize);


@@ 92,6 92,8 @@ s3linear(VGAscr* scr, int* size, int* align)
	else
		scr->isupamem = 1;

	if(oaperture)
		print("warning (BUG): redefinition of aperture does not change s3screen segment\n");
	memset(&seg, 0, sizeof(seg));
	seg.attr = SG_PHYSICAL;
	seg.name = smalloc(NAMELEN);


@@ 99,6 101,7 @@ s3linear(VGAscr* scr, int* size, int* align)
	seg.pa = aperture;
	seg.size = osize;
	addphysseg(&seg);

	return aperture;
}



@@ 130,7 133,7 @@ s3disable(VGAscr*)
static void
s3enable(VGAscr* scr)
{
	int i, id;
	int i;
	ulong storage;

	s3disable(scr);


@@ 138,30 141,16 @@ s3enable(VGAscr* scr)
	/*
	 * Cursor colours. Set both the CR0[EF] and the colour
	 * stack in case we are using a 16-bit RAMDAC.
	 * This stuff is just a mystery for the ViRGE/GX2.
	 */
	vgaxo(Crtx, 0x0E, Pwhite);
	vgaxo(Crtx, 0x0F, Pblack);
	vgaxi(Crtx, 0x45);
	id = (vgaxi(Crtx, 0x30)<<8)|vgaxi(Crtx, 0x2E);
	switch(id){

	case 0xE110:				/* ViRGE/GX2 */
		for(i = 0; i < 3; i++)
			vgaxo(Crtx, 0x4A, Pblack);
		vgaxi(Crtx, 0x45);
		for(i = 0; i < 3; i++)
			vgaxo(Crtx, 0x4B, Pwhite);
		break;

	default:
		for(i = 0; i < 3; i++)
			vgaxo(Crtx, 0x4A, Pwhite);
		vgaxi(Crtx, 0x45);
		for(i = 0; i < 3; i++)
			vgaxo(Crtx, 0x4B, Pblack);
		break;
	}
	for(i = 0; i < 3; i++)
		vgaxo(Crtx, 0x4A, Pblack);
	vgaxi(Crtx, 0x45);
	for(i = 0; i < 3; i++)
		vgaxo(Crtx, 0x4B, Pwhite);

	/*
	 * Find a place for the cursor data in display memory.


@@ 198,8 187,10 @@ s3load(VGAscr* scr, Cursor* curs)
	id = (vgaxi(Crtx, 0x30)<<8)|vgaxi(Crtx, 0x2E);
	switch(id){

	case 0xE110:				/* ViRGE/GX2 */
	case 0xE131:				/* ViRGE */
	case 0xE18A:				/* ViRGE/[DG]X */
	case 0xE110:				/* ViRGE/GX2 */
	case 0xE13D:				/* ViRGE/VX */
		p += scr->storage;
		break;



@@ 242,8 233,10 @@ s3load(VGAscr* scr, Cursor* curs)

	switch(id){

	case 0xE110:				/* ViRGE/GX2 */
	case 0xE131:				/* ViRGE */
	case 0xE18A:				/* ViRGE/[DG]X */
	case 0xE110:				/* ViRGE/GX2 */
	case 0xE13D:				/* ViRGE/VX */
		break;

	default:


@@ 297,6 290,182 @@ s3move(VGAscr* scr, Point p)
	return 0;
}

/*
 * The manual gives byte offsets, but we want ulong offsets, hence /4.
 */
enum {
	SrcBase = 0xA4D4/4,
	DstBase = 0xA4D8/4,
	Stride = 0xA4E4/4,
	FgrdData = 0xA4F4/4,
	WidthHeight = 0xA504/4,
	SrcXY = 0xA508/4,
	DestXY = 0xA50C/4,
	Command = 0xA500/4,
	SubStat = 0x8504/4,
	FifoStat = 0x850C/4,
};

/*
 * Wait for writes to VGA memory via linear aperture to flush.
 */
enum {Maxloop = 1<<24};
struct {
	ulong linear;
	ulong fifo;
	ulong idle;
} waitcount;

static void
waitforlinearfifo(VGAscr *scr)
{
	ulong *mmio;
	long x;
	static ulong nwaitforlinearfifo;
	ulong mask, val;

	switch(scr->id){
	default:
		panic("unknown scr->id in s3 waitforlinearfifo");
	case 0xE131:	/* ViRGE */
	case 0xE13D:	/* ViRGE/VX */
		mask = 0x0F<<6;
		val = 0x08<<6;
		break;
	case 0xE110:	/* ViRGE/GX2 */
		mask = 0x1F<<6;
		val = 0x10<<6;
		break;
	}
	mmio = scr->mmio;
	x = 0;
	while((mmio[FifoStat]&mask) != val && x++ < Maxloop)
		waitcount.linear++;
}

static void
waitforfifo(VGAscr *scr, int entries)
{
	ulong *mmio;
	long x;
	static ulong nwaitforfifo;

	mmio = scr->mmio;
	x = 0;
	while((mmio[SubStat]&0x1F00) < ((entries+2)<<8) && x++ < Maxloop)
		waitcount.fifo++;
}

static void
waitforidle(VGAscr *scr)
{
	ulong *mmio;
	long x;

	mmio = scr->mmio;
	x = 0;
	while((mmio[SubStat]&0x3F00) != 0x3000 && x++ < Maxloop)
		waitcount.idle++;
}

static int
hwscroll(VGAscr *scr, Rectangle r, Rectangle sr)
{
	enum { Bitbltop = 0xCC };	/* copy source */
	ulong *mmio;
	ulong cmd, stride;
	Point dp, sp;
	int did, d;

	d = scr->gscreen->depth;
	did = (d-8)/8;
	cmd = 0x00000020|(Bitbltop<<17)|(did<<2);
	stride = Dx(scr->gscreen->r)*d/8;

	if(r.min.x <= sr.min.x){
		cmd |= 1<<25;
		dp.x = r.min.x;
		sp.x = sr.min.x;
	}else{
		dp.x = r.max.x-1;
		sp.x = sr.max.x-1;
	}

	if(r.min.y <= sr.min.y){
		cmd |= 1<<26;
		dp.y = r.min.y;
		sp.y = sr.min.y;
	}else{
		dp.y = r.max.y-1;
		sp.y = sr.max.y-1;
	}

	mmio = scr->mmio;
	waitforlinearfifo(scr);
	waitforfifo(scr, 7);
	mmio[SrcBase] = scr->aperture;
	mmio[DstBase] = scr->aperture;
	mmio[Stride] = (stride<<16)|stride;
	mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r);
	mmio[SrcXY] = (sp.x<<16)|sp.y;
	mmio[DestXY] = (dp.x<<16)|dp.y;
	mmio[Command] = cmd;
	waitforidle(scr);
	return 1;
}

static int
hwfill(VGAscr *scr, Rectangle r, ulong sval)
{
	enum { Bitbltop = 0xCC };	/* copy source */
	ulong *mmio;
	ulong cmd, stride;
	int did, d;

	d = scr->gscreen->depth;
	did = (d-8)/8;
	cmd = 0x16000120|(Bitbltop<<17)|(did<<2);
	stride = Dx(scr->gscreen->r)*d/8;
	mmio = scr->mmio;
	waitforlinearfifo(scr);
	waitforfifo(scr, 8);
	mmio[SrcBase] = scr->aperture;
	mmio[DstBase] = scr->aperture;
	mmio[DstBase] = scr->aperture;
	mmio[Stride] = (stride<<16)|stride;
	mmio[FgrdData] = sval;
	mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r);
	mmio[DestXY] = (r.min.x<<16)|r.min.y;
	mmio[Command] = cmd;
	waitforidle(scr);
	return 1;
}

static void
s3drawinit(VGAscr *scr)
{
	ulong id;

	id = (vgaxi(Crtx, 0x30)<<8)|vgaxi(Crtx, 0x2E);
	scr->id = id;

	/*
	 * It's highly likely that other ViRGEs will work without
	 * change to the driver, with the exception of the size of
	 * the linear aperture memory write FIFO.  Since we don't
	 * know that size, I'm not turning them on.  See waitforlinearfifo
	 * above.
	 */
	switch(id){
	case 0xE131:				/* ViRGE */
	case 0xE13D:				/* ViRGE/VX */
	case 0xE110:				/* ViRGE/GX2 */
		scr->mmio = (ulong*)(scr->aperture+0x1000000);
		scr->fill = hwfill;
		scr->scroll = hwscroll;
	}
}

VGAdev vgas3dev = {
	"s3",



@@ 304,6 473,7 @@ VGAdev vgas3dev = {
	0,
	s3page,
	s3linear,
	s3drawinit,
};

VGAcur vgas3cur = {

M port/devaudio.c => port/devaudio.c +48 -6
@@ 18,6 18,7 @@ enum
	Qdir		= 0,
	Qaudio,
	Qvolume,
	Qstatus,

	Fmono		= 1,
	Fin		= 2,


@@ 47,6 48,7 @@ audiodir[] =
{
	"audio",	{Qaudio},		0,	0666,
	"volume",	{Qvolume},		0,	0666,
	"audiostat",{Qstatus},		0,	0444,
};

struct	Buf


@@ 76,6 78,8 @@ static	struct
	int	lovol[Nvol];
	int	major;		/* SB16 major version number (sb 4) */
	int	minor;		/* SB16 minor version number */
	ulong	totcount;	/* how many bytes processed since open */
	vlong	tottime;	/* time at which totcount bytes were processed */

	Buf	buf[Nbuf];	/* buffers and queues */
	AQueue	empty;


@@ 399,6 403,7 @@ sb16startdma(void)
	sbcmd(count>>8);

	audio.active = 1;
	audio.tottime = todget(nil);
	contindma();
	iunlock(&blaster);
}


@@ 490,6 495,7 @@ ess1688startdma(void)
	ess1688w(0xB8, x|0x05);

	audio.active = 1;
	audio.tottime = todget(nil);
	contindma();
	iunlock(&blaster);
}


@@ 516,6 522,8 @@ sb16intr(void)
		if(stat & 2) {
			ilock(&blaster);
			dummy = inb(blaster.clri16);
			audio.totcount += Bufsize;
			audio.tottime = todget(nil);
			contindma();
			iunlock(&blaster);
			audio.intr = 1;


@@ 538,6 546,8 @@ ess1688intr(void)

	if(audio.active){
		ilock(&blaster);
		audio.totcount += Bufsize;
		audio.tottime = todget(nil);
		contindma();
		dummy = inb(blaster.clri8);
		iunlock(&blaster);


@@ 625,6 635,8 @@ setempty(void)
	audio.filling = 0;
	for(i=0; i<Nbuf; i++)
		putbuf(&audio.empty, &audio.buf[i]);
	audio.totcount = 0;
	audio.tottime = 0LL;
	iunlock(&blaster);
}



@@ 707,7 719,8 @@ static void
audioinit(void)
{
	ISAConf sbconf;
	int i;
	int i, x;
	static int irq[] = {2,5,7,10};

	sbconf.port = 0x220;
	sbconf.dma = Dma;


@@ 752,9 765,6 @@ audioinit(void)
	blaster.startdma = sb16startdma;
	blaster.intr = sb16intr;

	seteisadma(blaster.dma, audiodmaintr);
	setvec(Int0vec+sbconf.irq, pcaudiosbintr, 0);

	audio.amode = Aclosed;
	resetlevel();



@@ 789,7 799,13 @@ audioinit(void)
	mxvolume();

	/*
	 * set up irq/dma chans
	 * Attempt to set IRQ/DMA channels.
	 * On old ISA boards, these registers are writable.
	 * On Plug-n-Play boards, these are read-only.
	 *
	 * To accomodate both, we write to the registers,
	 * but then use the contents in case the write is
	 * disallowed.
	 */
	mxcmd(0x80,			/* irq */
		(sbconf.irq==2)? 1:


@@ 798,6 814,23 @@ audioinit(void)
		(sbconf.irq==10)? 8:
		0);
	mxcmd(0x81, 1<<blaster.dma);	/* dma */

	x = mxread(0x81);
	for(i=5; i<=7; i++)
		if(x & (1<<i)){
			blaster.dma = i;
			break;
		}

	x = mxread(0x80);
	for(i=0; i<=3; i++)
		if(x & (1<<i)){
			sbconf.irq = irq[i];
			break;
		}

	seteisadma(blaster.dma, audiodmaintr);
	setvec(Int0vec+sbconf.irq, pcaudiosbintr, 0);
}

static Chan*


@@ 831,6 864,9 @@ audioopen(Chan *c, int omode)
		error(Eperm);
		break;

	case Qstatus:
		if((omode&7) != OREAD)
			error(Eperm);
	case Qvolume:
	case Qdir:
		break;


@@ 875,6 911,7 @@ audioclose(Chan *c)

	case Qdir:
	case Qvolume:
	case Qstatus:
		break;

	case Qaudio:


@@ 965,6 1002,12 @@ audioread(Chan *c, void *v, long n, vlong off)
		qunlock(&audio);
		break;

	case Qstatus:
		buf[0] = 0;
		snprint(buf, sizeof(buf), "bytes %lud\ntime %lld\n",
			audio.totcount, audio.tottime);
		return readstr(offset, a, n, buf);

	case Qvolume:
		j = 0;
		buf[0] = 0;


@@ 1004,7 1047,6 @@ audioread(Chan *c, void *v, long n, vlong off)
			}
			j += snprint(buf+j, sizeof(buf)-j, "\n");
		}

		return readstr(offset, a, n, buf);
	}
	return n0-n;

M port/devcons.c => port/devcons.c +13 -5
@@ 5,7 5,7 @@
#include	"fns.h"
#include	"../port/error.h"

void	(*consdebug)(void);
void	(*consdebug)(void) = rdb;

Queue*	kbdq;			/* unprocessed console input */
Queue*	lineq;			/* processed console input */


@@ 141,6 141,8 @@ sprint(char *s, char *fmt, ...)
	return n;
}

int noprint;

int
print(char *fmt, ...)
{


@@ 148,6 150,9 @@ print(char *fmt, ...)
	va_list arg;
	char buf[PRINTSIZE];

	if(noprint)
		return -1;

	va_start(arg, fmt);
	n = doprint(buf, buf+sizeof(buf), fmt, arg) - buf;
	va_end(arg);


@@ 168,6 173,7 @@ iprint(char *fmt, ...)
	n = doprint(buf, buf+sizeof(buf), fmt, arg) - buf;
	va_end(arg);
	serialputs(buf, n);
//	screenputs(buf, n);
	splx(s);

	return n;


@@ 180,11 186,14 @@ panic(char *fmt, ...)
	va_list arg;
	char buf[PRINTSIZE];

	splhi();
	strcpy(buf, "panic: ");
	va_start(arg, fmt);
	n = doprint(buf+strlen(buf), buf+sizeof(buf), fmt, arg) - buf;
	va_end(arg);
	buf[n] = '\n';
	serialputs(buf, n+1);
	rdb();
	putstrn(buf, n+1);
	spllo();
	prflush();


@@ 207,7 216,7 @@ pprint(char *fmt, ...)
	va_list arg;
	char buf[2*PRINTSIZE];

	if(up->fgrp == 0)
	if(up == nil || up->fgrp == nil)
		return 0;

	c = up->fgrp->fd[2];


@@ 267,9 276,6 @@ echo(Rune r, char *buf, int n)
			if(consdebug != nil)
				consdebug();
			return;
		case 'D':
			consdebug = rdb;
			return;
		case 'p':
			x = spllo();
			procdump();


@@ 378,6 384,7 @@ enum{
	Qkey,
	Qhostdomain,
	Qhostowner,
	Qnewkernel,
	Qnull,
	Qpgrpid,
	Qpid,


@@ 409,6 416,7 @@ static Dirtab consdir[]={
	"hostdomain",	{Qhostdomain},	DOMLEN,		0664,
	"hostowner",	{Qhostowner},	NAMELEN,	0664,
	"key",		{Qkey},		DESKEYLEN,	0622,
	"newkernel",	{Qnewkernel},	0,	0000,	/* stupid hack -rsc */
	"null",		{Qnull},	0,		0666,
	"pgrpid",	{Qpgrpid},	NUMSIZE,	0444,
	"pid",		{Qpid},		NUMSIZE,	0444,

M port/devdraw.c => port/devdraw.c +197 -60
@@ 1,21 1,24 @@
#include	"u.h"
#include	"lib.h"
#include	"../port/lib.h"
#include	"mem.h"
#include	"dat.h"
#include	"fns.h"
#include	"error.h"
#include	"../port/error.h"
#include	<pool.h>

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

enum
{
	Qtopdir		= 0,
	Q2nd,
	Qnew,
	Q3rd,
	Q2nd,
	Qcolormap,
	Qctl,
	Qdata,


@@ 150,7 153,7 @@ struct DScreen
};

static	Draw		sdraw;
static	Memimage	screenimage;
static	Memimage	*screenimage;
static	Memdata	screendata;
static	Rectangle	flushrect;
static	int		waste;


@@ 172,7 175,7 @@ static	char Ewriteoutside[] =	"writeimage outside image";
static	char Enotfont[] =	"image not a font";
static	char Eindex[] =		"character index out of range";
static	char Enoclient[] =	"no such draw client";
static	char Eldepth[] =	"image has bad ldepth";
static	char Edepth[] =	"image has bad depth";
static	char Enameused[] =	"image name in use";
static	char Enoname[] =	"no image with that name";
static	char Eoldname[] =	"named image no longer valid";


@@ 302,7 305,7 @@ addflush(Rectangle r)
	int abb, ar, anbb;
	Rectangle nbb;

	if(sdraw.softscreen==0 || !rectclip(&r, screenimage.r))
	if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r))
		return;

	if(flushrect.min.x >= flushrect.max.x){


@@ 353,7 356,7 @@ dstflush(int dstid, Memimage *dst, Rectangle r)
	if(l == nil)
		return;
	do{
		if(l->screen->image->data != screenimage.data)
		if(l->screen->image->data != screenimage->data)
			return;
		r = rectaddpt(r, l->delta);
		l = l->screen->image->layer;


@@ 414,15 417,18 @@ drawlookup(Client *client, int id, int checkname)
{
	DImage *d;

//	print("drawlookup %d", id);
	d = client->dimage[id&HASHMASK];
	while(d){
		if(d->id == id){
			if(checkname && !drawgoodname(d))
				error(Eoldname);
//			print(" %p...", d);
			return d;
		}
		d = d->next;
	}
//	print(" nil...");
	return 0;
}



@@ 475,6 481,7 @@ drawinstall(Client *client, int id, Memimage *i, DScreen *dscreen)
	d->dscreen = dscreen;
	d->next = client->dimage[id&HASHMASK];
	client->dimage[id&HASHMASK] = d;
//	print("install %d %p...", id, d);
	return i;
}



@@ 485,6 492,9 @@ drawinstallscreen(Client *client, DScreen *d, int id, DImage *dimage, DImage *df
	CScreen *c;

	c = malloc(sizeof(CScreen));
	if(dimage && dimage->image && dimage->image->chan == 0)
		panic("bad image %p in drawinstallscreen", dimage->image);

	if(c == 0)
		return 0;
	if(d == 0){


@@ 590,12 600,12 @@ drawfreedimage(DImage *dimage)
			i++;
	if(dimage->vers != 0)	/* acquired by name; owned by someone else*/
		goto Return;
	if(dimage->image == &screenimage)	/* don't free the display */
	if(dimage->image == screenimage)	/* don't free the display */
		goto Return;
	ds = dimage->dscreen;
	if(ds){
		l = dimage->image;
		if(l->data == screenimage.data)
		if(l->data == screenimage->data)
			addflush(l->layer->screenr);
		if(l->layer->refreshfn == drawrefresh)	/* else true owner will clean up */
			free(l->layer->refreshptr);


@@ 790,25 800,41 @@ drawchar(Memimage *dst, Point p, Memimage *src, Point *sp, DImage *font, int ind
static int
initscreenimage(void)
{
	int width;
	int width, depth;
	ulong chan;
	Rectangle r;

	if(screendata.data != nil)
	if(screenimage != nil)
		return 1;

	screendata.base = nil;
	screendata.data = attachscreen(&screenimage.r, &screenimage.ldepth, &width, &sdraw.softscreen);

	if(screendata.data == nil)
	screendata.bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen);
	if(screendata.bdata == nil)
		return 0;

	screendata.ref = 1;
	screenimage.data = &screendata;
	screenimage.width = width;
	screenimage.clipr  = screenimage.r;

	screenimage = allocmemimaged(r, chan, &screendata);
	if(screenimage == nil){
		/* RSC: BUG: detach screen */
		return 0;
	}

	screenimage->width = width;
	screenimage->clipr  = screenimage->r;
	return 1;
}

void
deletescreenimage(void)
{
	qlock(&sdraw);
	/* RSC: BUG: detach screen */
	if(screenimage)
		freememimage(screenimage);
	screenimage = nil;
	qunlock(&sdraw);
}

Chan*
drawattach(char *spec)
{


@@ 824,7 850,7 @@ drawattach(char *spec)
int
drawwalk(Chan *c, char *name)
{
	if(screendata.data == nil)
	if(screendata.bdata == nil)
		error("no frame buffer");
	if(strcmp(name, "..") == 0){
		switch(QID(c->qid)){


@@ 885,7 911,7 @@ drawopen(Chan *c, int omode)
			error(Einuse);
		cl->busy = 1;
		flushrect = Rect(10000, 10000, -10000, -10000);
		drawinstall(cl, 0, &screenimage, 0);
		drawinstall(cl, 0, screenimage, 0);
		incref(&cl->r);
		break;
	case Qcolormap:


@@ 963,6 989,7 @@ drawread(Chan *c, void *a, long n, vlong off)
	DImage *di;
	Memimage *i;
	ulong offset = off;
	char buf[16];

	USED(offset);
	if(c->qid.path & CHDIR)


@@ 979,16 1006,18 @@ drawread(Chan *c, void *a, long n, vlong off)
			error(Eshortread);
		if(cl->infoid < 0)
			error(Enodrawimage);
		if(cl->infoid == 0)
			i = &screenimage;
		else{
		if(cl->infoid == 0){
			i = screenimage;
			if(i == nil)
				error(Enodrawimage);
		}else{
			di = drawlookup(cl, cl->infoid, 1);
			if(di == nil)
				error(Enodrawimage);
			i = di->image;
		}
		n = sprint(a, "%11d %11d %11d %11d %11d %11d %11d %11d %11d %11d %11d %11d ",
			cl->clientid, cl->infoid, i->ldepth, i->repl,
		n = sprint(a, "%11d %11d %11s %11d %11d %11d %11d %11d %11d %11d %11d %11d ",
			cl->clientid, cl->infoid, chantostr(buf, i->chan), (i->flags&Frepl)==Frepl,
			i->r.min.x, i->r.min.y, i->r.max.x, i->r.max.y,
			i->clipr.min.x, i->clipr.min.y, i->clipr.max.x, i->clipr.max.y);
		cl->infoid = -1;


@@ 1110,15 1139,15 @@ drawwrite(Chan *c, void *a, long n, vlong off)
			m -= i;
			*q = 0;
			if(parsefields(buf, fields, nelem(fields), " ") != 4)
				error(Ebadarg);
				error("eb1"); //error(Ebadarg);
			i = strtoul(fields[0], 0, 0);
			red = strtoul(fields[1], 0, 0);
			green = strtoul(fields[2], 0, 0);
			blue = strtoul(fields[3], &q, 0);
			if(fields[3] == q)
				error(Ebadarg);
				error("eb2"); //error(Ebadarg);
			if(red>255 || green>255 || blue>255 || i<0 || i>255)
				error(Ebadarg);
				error("eb3"); //error(Ebadarg);
			red |= red<<8;
			red |= red<<16;
			green |= green<<8;


@@ 1167,11 1196,63 @@ drawcoord(uchar *p, uchar *maxp, int oldx, int *newx)
	return p;
}

static void
printmesg(char *fmt, uchar *a, int itwouldmakemehappyifyouprintthis)
{
	char buf[256];
	char *p, *q;
	int s;

	if(1|| itwouldmakemehappyifyouprintthis==0){
		SET(s,q,p);
		USED(fmt, a, buf, p, q, s);
		return;
	}
	q = buf;
	*q++ = *a++;
	for(p=fmt; *p; p++){
		switch(*p){
		case 'l':
			q += sprint(q, " %ld", (long)BGLONG(a));
			a += 4;
			break;
		case 'L':
			q += sprint(q, " %.8lux", (ulong)BGLONG(a));
			a += 4;
			break;
		case 'R':
			q += sprint(q, " [%d %d %d %d]", BGLONG(a), BGLONG(a+4), BGLONG(a+8), BGLONG(a+12));
			a += 16;
			break;
		case 'P':
			q += sprint(q, " [%d %d]", BGLONG(a), BGLONG(a+4));
			a += 8;
			break;
		case 'b':
			q += sprint(q, " %d", *a++);
			break;
		case 's':
			q += sprint(q, " %d", BGSHORT(a));
			a += 2;
			break;
		case 'S':
			q += sprint(q, " %.4ux", BGSHORT(a));
			a += 2;
			break;
		}
	}
	*q++ = '\n';
	*q = 0;
	iprint("%.*s", (int)(q-buf), buf);
}

void
drawmesg(Client *client, void *av, int n)
{
	int c, ldepth, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, ox, oy, esize, doflush;
	uchar *u, *a;
	int c, repl, m, y, dstid, scrnid, ni, ci, j, nw, e0, e1, ox, oy, esize, doflush;
	uchar *u, *a, refresh;
	char *fmt;
	ulong value, chan;
	Rectangle r, clipr;
	Point p, q, *pp, sp;
	Memimage *i, *dst, *src, *mask;


@@ 1187,30 1268,41 @@ drawmesg(Client *client, void *av, int n)

	a = av;
	m = 0;
	fmt = nil;
	if(waserror()){
		if(fmt) printmesg(fmt, a, 1);
	//	iprint("error: %s\n", up->error);
		nexterror();
	}
	while((n-=m) > 0){
		USED(fmt);
		a += m;
		switch(*a){
		default:
			error("bad draw command");
		/* allocate: 'a' id[4] screenid[4] refresh[1] ldepth[2] repl[1] R[4*4] clipR[4*4] value[1] */
		case 'a':
			m = 1+4+4+1+2+1+4*4+4*4+1;
		/* new allocate: 'b' id[4] screenid[4] refresh[1] chan[4] repl[1] R[4*4] clipR[4*4] rrggbbαα[4] */
		case 'b':
			printmesg(fmt="LLbLbRRL", a, 0);
			m = 1+4+4+1+4+1+4*4+4*4+4;
			if(n < m)
				error(Eshortdraw);
			dstid = BGLONG(a+1);
			scrnid = BGSHORT(a+5);
			refresh = a[9];
			chan = BGLONG(a+10);
			repl = a[14];
			drawrectangle(&r, a+15);
			drawrectangle(&clipr, a+31);
			value = BGLONG(a+47);
			if(drawlookup(client, dstid, 0))
				error(Eimageexists);
			ldepth = BGSHORT(a+10);
			repl = a[12];
			drawrectangle(&r, a+13);
			scrnid = BGSHORT(a+5);
			if(scrnid){
				dscrn = drawlookupscreen(client, scrnid, &cs);
				scrn = dscrn->screen;
				if(repl || ldepth!=scrn->image->ldepth)
				if(repl || chan!=scrn->image->chan)
					error("image parameters incompatible with screen");
				reffn = nil;
				switch(a[9]){
				switch(refresh){
				case Refbackup:
					break;
				case Refnone:


@@ 1222,11 1314,11 @@ drawmesg(Client *client, void *av, int n)
				default:
					error("unknown refresh method");
				}
				l = memlalloc(scrn, r, reffn, 0, a[45]);
				l = memlalloc(scrn, r, reffn, 0, value);
				if(l == 0)
					error(Edrawmem);
				addflush(l->layer->screenr);
				drawrectangle(&l->clipr, a+29);
				l->clipr = clipr;
				rectclip(&l->clipr, r);
				if(drawinstall(client, dstid, l, dscrn) == 0){
					memldelete(l);


@@ 1248,22 1340,26 @@ drawmesg(Client *client, void *av, int n)
				}
				continue;
			}
			i = allocmemimage(r, ldepth);
			if(i == 0)
			i = allocmemimage(r, chan);
			if(i == 0){
				iprint("chan %lux\n", chan);
				error(Edrawmem);
			i->repl = repl;
			drawrectangle(&i->clipr, a+29);
			}
			if(repl)
				i->flags |= Frepl;
			i->clipr = clipr;
			if(!repl)
				rectclip(&i->clipr, r);
			if(drawinstall(client, dstid, i, 0) == 0){
				freememimage(i);
				error(Edrawmem);
			}
			memfillcolor(i, a[45]);
			memfillcolor(i, value);
			continue;

		/* allocate screen: 'A' id[4] imageid[4] fillid[4] public[1] */
		case 'A':
			printmesg(fmt="LLLb", a, 1);
			m = 1+4+4+4+1;
			if(n < m)
				error(Eshortdraw);


@@ 1282,6 1378,7 @@ drawmesg(Client *client, void *av, int n)

		/* set repl and clip: 'c' dstid[4] repl[1] clipR[4*4] */
		case 'c':
			printmesg(fmt="LbR", a, 0);
			m = 1+4+1+4*4;
			if(n < m)
				error(Eshortdraw);


@@ 1291,12 1388,14 @@ drawmesg(Client *client, void *av, int n)
			if(ddst->name)
				error("can't change repl/clipr of shared image");
			dst = ddst->image;
			dst->repl = a[5];
			if(a[5])
				dst->flags |= Frepl;
			drawrectangle(&dst->clipr, a+6);
			continue;

		/* draw: 'd' dstid[4] srcid[4] maskid[4] R[4*4] P[2*4] P[2*4] */
		case 'd':
			printmesg(fmt="LLLRPP", a, 0);
			m = 1+4+4+4+4*4+2*4+2*4;
			if(n < m)
				error(Eshortdraw);


@@ 1311,9 1410,19 @@ drawmesg(Client *client, void *av, int n)
			dstflush(dstid, dst, r);
			continue;

		/* toggle debugging: 'D' val[1] */
		case 'D':
			printmesg(fmt="b", a, 0);
			m = 1+1;
			if(n < m)
				error(Eshortdraw);
			drawdebug = a[1];
			continue;

		/* ellipse: 'e' dstid[4] srcid[4] center[2*4] a[4] b[4] thick[4] sp[2*4] alpha[4] phi[4]*/
		case 'e':
		case 'E':
			printmesg(fmt="LLPlllPll", a, 0);
			m = 1+4+4+2*4+4+4+4+2*4+2*4;
			if(n < m)
				error(Eshortdraw);


@@ 1346,6 1455,7 @@ drawmesg(Client *client, void *av, int n)

		/* free: 'f' id[4] */
		case 'f':
			printmesg(fmt="L", a, 1);
			m = 1+4;
			if(n < m)
				error(Eshortdraw);


@@ 1357,6 1467,7 @@ drawmesg(Client *client, void *av, int n)

		/* free screen: 'F' id[4] */
		case 'F':
			printmesg(fmt="L", a, 1);
			m = 1+4;
			if(n < m)
				error(Eshortdraw);


@@ 1366,6 1477,7 @@ drawmesg(Client *client, void *av, int n)

		/* initialize font: 'i' fontid[4] nchars[4] ascent[1] */
		case 'i':
			printmesg(fmt="Llb", a, 1);
			m = 1+4+4+1;
			if(n < m)
				error(Eshortdraw);


@@ 1389,6 1501,7 @@ drawmesg(Client *client, void *av, int n)

		/* load character: 'l' fontid[4] srcid[4] index[2] R[4*4] P[2*4] left[1] width[1] */
		case 'l':
			printmesg(fmt="LLSRPbb", a, 0);
			m = 1+4+4+2+4*4+2*4+1+1;
			if(n < m)
				error(Eshortdraw);


@@ 1403,7 1516,7 @@ drawmesg(Client *client, void *av, int n)
				error(Eindex);
			drawrectangle(&r, a+11);
			drawpoint(&p, a+27);
			memdraw(font->image, r, src, p, memones, p);
			memdraw(font->image, r, src, p, memopaque, p);
			fc = &font->fchar[ci];
			fc->minx = r.min.x;
			fc->maxx = r.max.x;


@@ 1415,6 1528,7 @@ drawmesg(Client *client, void *av, int n)

		/* draw line: 'L' dstid[4] p0[2*4] p1[2*4] end0[4] end1[4] radius[4] srcid[4] sp[2*4] */
		case 'L':
			printmesg(fmt="LPPlllLP", a, 0);
			m = 1+4+2*4+2*4+4+4+4+4+2*4;
			if(n < m)
				error(Eshortdraw);


@@ 1438,8 1552,17 @@ drawmesg(Client *client, void *av, int n)
			}
			continue;

		/* create image mask: 'm' newid[4] id[4] */
		case 'm':
			printmesg("LL", a, 0);
			m = 4+4;
			if(n < m)
				error(Eshortdraw);
			break;

		/* attach to a named image: 'n' dstid[4] j[1] name[j] */
		case 'n':
			printmesg(fmt="Lz", a, 0);
			m = 1+4+1;
			if(n < m)
				error(Eshortdraw);


@@ 1469,6 1592,7 @@ drawmesg(Client *client, void *av, int n)

		/* name an image: 'N' dstid[4] in[1] j[1] name[j] */
		case 'N':
			printmesg(fmt="Lbz", a, 0);
			m = 1+4+1+1;
			if(n < m)
				error(Eshortdraw);


@@ 1498,6 1622,7 @@ drawmesg(Client *client, void *av, int n)

		/* position window: 'o' id[4] r.min [2*4] screenr.min [2*4] */
		case 'o':
			printmesg(fmt="LPP", a, 0);
			m = 1+4+2*4+2*4;
			if(n < m)
				error(Eshortdraw);


@@ 1523,6 1648,7 @@ drawmesg(Client *client, void *av, int n)
		/* polygon: 'p' dstid[4] n[2] end0[4] end1[4] radius[4] srcid[4] sp[2*4] p0[2*4] dp[2*2*n] */
		case 'p':
		case 'P':
			printmesg(fmt="LslllLPP", a, 0);
			m = 1+4+2+4+4+4+4+2*4;
			if(n < m)
				error(Eshortdraw);


@@ 1547,7 1673,7 @@ drawmesg(Client *client, void *av, int n)
			if(pp == nil)
				error(Enomem);
			doflush = 0;
			if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage.data))
			if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage->data))
				doflush = 1;	/* simplify test in loop */
			ox = oy = 0;
			u = a+m;


@@ 1588,6 1714,7 @@ drawmesg(Client *client, void *av, int n)

		/* read: 'r' id[4] R[4*4] */
		case 'r':
			printmesg(fmt="LR", a, 0);
			m = 1+4+4*4;
			if(n < m)
				error(Eshortdraw);


@@ 1597,7 1724,7 @@ drawmesg(Client *client, void *av, int n)
			drawrectangle(&r, a+5);
			if(!rectinrect(r, i->r))
				error(Ereadoutside);
			c = bytesperline(r, i->ldepth);
			c = bytesperline(r, i->depth);
			c *= Dy(r);
			free(client->readdata);
			client->readdata = mallocz(c, 0);


@@ 1615,6 1742,7 @@ drawmesg(Client *client, void *av, int n)
		/* stringbg: 'x' dstid[4] srcid[4] fontid[4] P[2*4] clipr[4*4] sp[2*4] ni[2] bgid[4] bgpt[2*4] ni*(index[2]) */
		case 's':
		case 'x':
			printmesg(fmt="LLLPRPs", a, 0);
			m = 1+4+4+4+2*4+4*4+2*4+2;
			if(*a == 'x')
				m += 4+2*4;


@@ 1657,7 1785,7 @@ drawmesg(Client *client, void *av, int n)
					r.max.x += font->fchar[ci].width;
					u += 2;
				}
				memdraw(dst, r, l, q, memones, ZP);
				memdraw(dst, r, l, q, memopaque, ZP);
				u -= 2*ni;
			}
			q = p;


@@ 1677,6 1805,7 @@ drawmesg(Client *client, void *av, int n)

		/* use public screen: 'S' id[4] ldepth[4] */
		case 'S':
			printmesg(fmt="Ll", a, 0);
			m = 1+4+4;
			if(n < m)
				error(Eshortdraw);


@@ 1686,14 1815,15 @@ drawmesg(Client *client, void *av, int n)
			dscrn = drawlookupdscreen(dstid);
			if(dscrn==0 || (dscrn->public==0 && dscrn->owner!=client))
				error(Enodrawscreen);
			if(dscrn->screen->image->ldepth != BGLONG(a+5))
				error("inconsistent ldepth");
			if(dscrn->screen->image->depth != (1<<BGLONG(a+5)))
				error("inconsistent depth");
			if(drawinstallscreen(client, dscrn, 0, 0, 0, 0) == 0)
				error(Edrawmem);
			continue;

		/* top or bottom windows: 't' top[1] nw[2] n*id[4] */
		case 't':
			printmesg(fmt="bsL", a, 0);
			m = 1+1+2;
			if(n < m)
				error(Eshortdraw);


@@ 1723,7 1853,7 @@ drawmesg(Client *client, void *av, int n)
				memltofrontn(lp, nw);
			else
				memltorearn(lp, nw);
			if(lp[0]->layer->screen->image->data == screenimage.data)
			if(lp[0]->layer->screen->image->data == screenimage->data)
				for(j=0; j<nw; j++)
					addflush(lp[j]->layer->screenr);
			ll = drawlookup(client, BGLONG(a+1+1+2), 1);


@@ 1735,14 1865,19 @@ drawmesg(Client *client, void *av, int n)

		/* visible: 'v' */
		case 'v':
			printmesg(fmt="", a, 0);
			m = 1;
			drawflush();
			continue;

		/* write: 'w' id[4] R[4*4] data[x*1] */
		/* write from compressed data: 'W' id[4] R[4*4] data[x*1] */
		case 'w':
		case 'W':
		/* old write: 'w' id[4] R[4*4] data[x*1] */
		/* old write from compressed data: 'W' id[4] R[4*4] data[x*1] */
		/* new write: 'y' id[4] R[4*4] data[x*1] */
		/* new write from compressed data: 'Y' id[4] R[4*4] data[x*1] */
		case 'y':
		case 'Y':
			printmesg(fmt="LR", a, 0);
		//	iprint("load %c\n", *a);
			m = 1+4+4*4;
			if(n < m)
				error(Eshortdraw);


@@ 1751,7 1886,7 @@ drawmesg(Client *client, void *av, int n)
			drawrectangle(&r, a+5);
			if(!rectinrect(r, dst->r))
				error(Ewriteoutside);
			y = memload(dst, r, a+m, n-m, *a=='W');
			y = memload(dst, r, a+m, n-m, *a=='Y');
			if(y < 0)
				error("bad writeimage call");
			dstflush(dstid, dst, r);


@@ 1759,6 1894,7 @@ drawmesg(Client *client, void *av, int n)
			continue;
		}
	}
	poperror();
}

Dev drawdevtab = {


@@ 1829,7 1965,8 @@ drawblankscreen(int blank)
		return;
	}
	p = sdraw.savemap;
	nc = 1<<(1<<screenimage.ldepth);
	nc = screenimage->depth > 8 ? 256 : 1<<screenimage->depth;

	if(blank == 0)	/* turn screen on */
		for(i=0; i<nc; i++, p+=3)
			setcolor(i, p[0], p[1], p[2]);

M port/devmouse.c => port/devmouse.c +13 -10
@@ 58,7 58,7 @@ static uchar buttonmap[8] = {
};
static int mouseswap;

extern	Memimage	gscreen;
extern	Memimage*	gscreen;

static void
mousereset(void)


@@ 323,7 323,7 @@ mousewrite(Chan *c, void *va, long n, vlong)
			error(Eshort);
		pt.y = strtoul(p, 0, 0);
		qlock(&mouse);
		if(ptinrect(pt, gscreen.r)){
		if(ptinrect(pt, gscreen->r)){
			mouse.xy = pt;
			mouse.redraw = 1;
			mouse.track = 1;


@@ 426,20 426,23 @@ mousetrack(int b, int dx, int dy)
{
	int x, y;

	if(gscreen==nil)
		return;

	if(mouse.acceleration){
		dx = scale(dx);
		dy = scale(dy);
	}
	x = mouse.xy.x + dx;
	if(x < gscreen.r.min.x)
		x = gscreen.r.min.x;
	if(x >= gscreen.r.max.x)
		x = gscreen.r.max.x;
	if(x < gscreen->r.min.x)
		x = gscreen->r.min.x;
	if(x >= gscreen->r.max.x)
		x = gscreen->r.max.x;
	y = mouse.xy.y + dy;
	if(y < gscreen.r.min.y)
		y = gscreen.r.min.y;
	if(y >= gscreen.r.max.y)
		y = gscreen.r.max.y;
	if(y < gscreen->r.min.y)
		y = gscreen->r.min.y;
	if(y >= gscreen->r.max.y)
		y = gscreen->r.max.y;
	mouse.counter++;
	mouse.xy = Pt(x, y);
	mouse.buttons = b;

M port/portfns.h => port/portfns.h +2 -1
@@ 8,6 8,7 @@ Block*		allocb(int);
int		anyhigher(void);
int		anyready(void);
#define	assert(x)	if(x);else panic("assert(x) failed");
void		_assert(char*);
Image*		attachimage(int, Chan*, ulong, ulong);
long		authcheck(Chan*, char*, int);
long		authcheckread(Chan*, char*, int n);


@@ 146,7 147,7 @@ void		kproc(char*, void(*)(void*), void*);
void		kprocchild(Proc*, void (*)(void*), void*);
void		(*kproftimer)(ulong);
void		ksetenv(char*, char*);
long		latin1(uchar*, int);
long		latin1(Rune*, int);
void		lock(Lock*);
void		lockinit(void);
void		logopen(Log*);