shithub: riscv

Download patch

ref: 806353ec9eda162a3ff13cad1e5228c58cd67566
parent: 874e71c8dc489b820c9a6066d13c470a34d7f83f
author: cinap_lenrek <[email protected]>
date: Sun Dec 27 18:08:59 EST 2020

devvga: implement screen tilting, remove panning and overlays

Tilting allows using left/right rotated or invetrted display orientation.
This can be changed at runtime such as: echo tilt right > /dev/vgactl
This removes the old panning and vga overlays as they are only implemented
with some ancient vga controllers.

--- a/sys/man/3/vga
+++ b/sys/man/3/vga
@@ -6,8 +6,6 @@
 .B bind #v /dev
 
 .B /dev/vgactl
-.B /dev/vgaovl
-.B /dev/vgaovlctl
 .fi
 .SH DESCRIPTION
 The VGA device allows configuration of a graphics controller
@@ -48,9 +46,8 @@
 .I Y 
 pixels high.
 This message is optional;
-it is used to implement panning and to accommodate
-displays that require the in-memory screen image
-to have certain alignment properties.
+it is used to accommodate displays that require the
+in-memory screen image to have certain alignment properties.
 For example, a 1400x1050 screen with a 1408x1050 in-memory image
 will use 
 .B "size 1408x1050
@@ -57,22 +54,17 @@
 but
 .BR "actualsize 1400x1050" .
 .TP
-.BI panning " mode"
-Depending on whether
-.I mode
-is 
-.B on
-or
-.BR off ,
-enable or disable panning in a virtual screen.
-If panning is on and the screen's
-.B size
-is larger than its
-.BR actualsize ,
-the displayed portion of the screen will pan to follow the mouse.
-Setting the panning mode after the first attach of the
-.B #i
-driver has no effect.
+.BI tilt " value"
+Set the tilt of the screen,
+altering the screen's orientation.
+The
+.I value
+can be one of:
+.BR none ,
+.BR left ,
+.B inverted
+and
+.BR right .
 .TP
 .BI type " ctlr"
 Set the type of VGA controller being used.
@@ -206,46 +198,6 @@
 Reading
 .B vgactl
 returns the current settings, one per line.
-.PP
-Some VGA cards support overlay graphics.
-Writing strings to
-.B vgaovlctl
-configures such cards.
-The following are valid overlay control commands:
-.TP
-.BI openctl
-opens the overlay device.
-.TP
-.BI configure " w h format"
-allocates resources inside the driver to support an overlay area
-of width
-.I w
-and height
-.I h
-pixels.  Currently, the only supported
-.I format
-is
-.B YUYV
-packed.
-In
-.B YUYV
-two pixels are encoded by their separate Y values
-and their combined U and V values.
-The size of the two pixels is 32 bits.
-.TP
-.BI enable " x y w h"
-enables drawing data on the display through the overlay mode.  The data
-is drawn at position
-.IR x , y
-and has a width and height of
-.IR w , h
-respectively.
-.TP
-.BI closectl
-terminates overlay control.
-.PP
-Overlay data can be written to
-.BR vgaovl .
 .SH EXAMPLES
 The following disables hardware acceleration.
 .IP
--- a/sys/src/9/pc/devvga.c
+++ b/sys/src/9/pc/devvga.c
@@ -19,15 +19,11 @@
 enum {
 	Qdir,
 	Qvgactl,
-	Qvgaovl,
-	Qvgaovlctl,
 };
 
 static Dirtab vgadir[] = {
-	".",	{ Qdir, 0, QTDIR },		0,	0550,
-	"vgactl",		{ Qvgactl, 0 },		0,	0660,
-	"vgaovl",		{ Qvgaovl, 0 },		0,	0660,
-	"vgaovlctl",	{ Qvgaovlctl, 0 },	0, 	0660,
+	".",		{ Qdir, 0, QTDIR },	0,	0550,
+	"vgactl",	{ Qvgactl, 0 },		0,	0660,
 };
 
 enum {
@@ -38,12 +34,12 @@
 	CMhwgc,
 	CMlinear,
 	CMpalettedepth,
-	CMpanning,
 	CMsize,
 	CMtextmode,
 	CMtype,
 	CMsoftscreen,
 	CMpcidev,
+	CMtilt,
 };
 
 static Cmdtab vgactlmsg[] = {
@@ -54,12 +50,12 @@
 	CMhwgc,		"hwgc",		2,
 	CMlinear,	"linear",	0,
 	CMpalettedepth,	"palettedepth",	2,
-	CMpanning,	"panning",	2,
 	CMsize,		"size",		3,
 	CMtextmode,	"textmode",	1,
 	CMtype,		"type",		2,
 	CMsoftscreen,	"softscreen",	2,
 	CMpcidev,	"pcidev",	2,
+	CMtilt,		"tilt",		2,
 };
 
 static void
@@ -120,35 +116,12 @@
 static Chan*
 vgaopen(Chan* c, int omode)
 {
-	VGAscr *scr;
-	static char *openctl = "openctl\n";
-
-	scr = &vgascreen[0];
-	if ((ulong)c->qid.path == Qvgaovlctl) {
-		if (scr->dev && scr->dev->ovlctl)
-			scr->dev->ovlctl(scr, c, openctl, strlen(openctl));
-		else 
-			error(Enonexist);
-	}
 	return devopen(c, omode, vgadir, nelem(vgadir), devgen);
 }
 
 static void
-vgaclose(Chan* c)
+vgaclose(Chan*)
 {
-	VGAscr *scr;
-	static char *closectl = "closectl\n";
-
-	scr = &vgascreen[0];
-	if((ulong)c->qid.path == Qvgaovlctl)
-		if(scr->dev && scr->dev->ovlctl){
-			if(waserror()){
-				print("ovlctl error: %s\n", up->errstr);
-				return;
-			}
-			scr->dev->ovlctl(scr, c, closectl, strlen(closectl));
-			poperror();
-		}
 }
 
 static long
@@ -176,19 +149,20 @@
 		p = seprint(p, e, "type %s\n",
 			scr->dev != nil ? scr->dev->name : "cga");
 		if(scr->gscreen != nil) {
+			Rectangle r;
+
 			p = seprint(p, e, "size %dx%dx%d %s\n",
-				scr->gscreen->r.max.x, scr->gscreen->r.max.y,
-				scr->gscreen->depth, chantostr(chbuf, scr->gscreen->chan));
-			if(Dx(scr->gscreen->r) != Dx(physgscreenr) 
-			|| Dy(scr->gscreen->r) != Dy(physgscreenr))
-				p = seprint(p, e, "actualsize %dx%d\n",
-					physgscreenr.max.x, physgscreenr.max.y);
+				scr->width, scr->height, scr->gscreen->depth,
+				chantostr(chbuf, scr->gscreen->chan));
+			r = actualscreensize(scr);
+			if(scr->width != r.max.x || scr->height != r.max.y)
+				p = seprint(p, e, "actualsize %dx%d\n", r.max.x, r.max.y);
+			p = seprint(p, e, "tilt %s\n", tiltstr[scr->tilt]);
 		}
 		p = seprint(p, e, "hwgc %s\n",
 			scr->cur != nil ? scr->cur->name : "off");
 		p = seprint(p, e, "hwaccel %s\n", hwaccel ? "on" : "off");
 		p = seprint(p, e, "hwblank %s\n", hwblank ? "on" : "off");
-		p = seprint(p, e, "panning %s\n", panning ? "on" : "off");
 		p = seprint(p, e, "addr p 0x%.8llux v %#p size %#ux\n",
 			scr->paddr, scr->vaddr, scr->apsize);
 		p = seprint(p, e, "softscreen %s\n", scr->softscreen ? "on" : "off");
@@ -199,11 +173,6 @@
 
 		return n;
 
-	case Qvgaovl:
-	case Qvgaovlctl:
-		error(Ebadusefd);
-		break;
-
 	default:
 		error(Egreg);
 		break;
@@ -216,18 +185,16 @@
 static char Enoscreen[] = "set the screen size first";
 
 static void
-vgactl(Cmdbuf *cb)
+vgactl(VGAscr *scr, Cmdbuf *cb)
 {
-	int align, i, size, x, y, z;
+	int align, size, tilt, z, i;
 	Rectangle r;
 	char *chanstr, *p;
 	ulong chan;
 	Cmdtab *ct;
-	VGAscr *scr;
 	extern VGAdev *vgadev[];
 	extern VGAcur *vgacur[];
 
-	scr = &vgascreen[0];
 	ct = lookupcmd(cb, vgactlmsg, nelem(vgactlmsg));
 	switch(ct->index){
 	case CMhwgc:
@@ -298,14 +265,15 @@
 		return;
 
 	case CMsize:
-		x = strtoul(cb->f[1], &p, 0);
+		r.min = ZP;
+		r.max.x = strtoul(cb->f[1], &p, 0);
 		if(*p)
 			p++;
-		y = strtoul(p, &p, 0);
+		r.max.y = strtoul(p, &p, 0);
 		if(*p)
 			p++;
 		z = strtoul(p, &p, 0);
-		if(badrect(Rect(0,0,x,y)))
+		if(badrect(r))
 			error(Ebadarg);
 		chanstr = cb->f[2];
 		if((chan = strtochan(chanstr)) == 0)
@@ -313,32 +281,30 @@
 		if(chantodepth(chan) != z)
 			error("depth, channel do not match");
 		deletescreenimage();
-		if(screensize(x, y, z, chan))
-			error(Egreg);
-		bootscreenconf(scr);
+		setscreensize(scr, r.max.x, r.max.y, z, chan, scr->tilt);
 		return;
 
 	case CMactualsize:
 		if(scr->gscreen == nil)
 			error(Enoscreen);
-		x = strtoul(cb->f[1], &p, 0);
+		r.min = ZP;
+		r.max.x = strtoul(cb->f[1], &p, 0);
 		if(*p)
 			p++;
-		y = strtoul(p, nil, 0);
-		r = Rect(0,0,x,y);
+		r.max.y = strtoul(p, nil, 0);
 		if(badrect(r))
 			error(Ebadarg);
-		if(!rectinrect(r, scr->gscreen->r))
+		if(r.max.x > scr->width || r.max.y > scr->height)
 			error("physical screen bigger than virtual");
 		deletescreenimage();
-		physgscreenr = r;
+		setactualsize(scr, r);
 		goto Resized;
 	
 	case CMpalettedepth:
-		x = strtoul(cb->f[1], &p, 0);
-		if(x != 8 && x != 6)
+		z = strtoul(cb->f[1], &p, 0);
+		if(z != 8 && z != 6)
 			error(Ebadarg);
-		scr->palettedepth = x;
+		scr->palettedepth = z;
 		return;
 
 	case CMsoftscreen:
@@ -348,17 +314,22 @@
 			scr->softscreen = 0;
 		else
 			break;
+		if(0){
+	case CMtilt:
+			for(tilt = 0; tilt < nelem(tiltstr); tilt++)
+				if(strcmp(cb->f[1], tiltstr[tilt]) == 0)
+					break;
+		} else {
+			tilt = scr->tilt;
+		}
 		if(scr->gscreen == nil)
 			return;
-		r = physgscreenr;
-		x = scr->gscreen->r.max.x;
-		y = scr->gscreen->r.max.y;
-		z = scr->gscreen->depth;
+		r = actualscreensize(scr);
 		chan = scr->gscreen->chan;
+		z = scr->gscreen->depth;
 		deletescreenimage();
-		if(screensize(x, y, z, chan))
-			error(Egreg);
-		physgscreenr = r;
+		setscreensize(scr, scr->width, scr->height, z, chan, tilt);
+		setactualsize(scr, r);
 		/* no break */
 	case CMdrawinit:
 		if(scr->gscreen == nil)
@@ -368,7 +339,6 @@
 		hwblank = scr->blank != nil;
 		hwaccel = scr->fill != nil || scr->scroll != nil;
 	Resized:
-		scr->gscreen->clipr = panning ? scr->gscreen->r : physgscreenr;
 		vgascreenwin(scr);
 		resetscreenimage();
 		return;
@@ -381,27 +351,10 @@
 			align = 0;
 		else
 			align = strtoul(cb->f[2], 0, 0);
-		if(screenaperture(size, align) < 0)
+		if(screenaperture(scr, size, align) < 0)
 			error("not enough free address space");
 		return;
 
-	case CMpanning:
-		if(strcmp(cb->f[1], "on") == 0){
-			if(scr->cur == nil)
-				error("set cursor first");
-			if(!scr->cur->doespanning)
-				error("panning not supported");
-			panning = 1;
-		}
-		else if(strcmp(cb->f[1], "off") == 0){
-			panning = 0;
-		}else
-			break;
-		if(scr->gscreen == nil)
-			return;
-		deletescreenimage();
-		goto Resized;
-
 	case CMhwaccel:
 		if(strcmp(cb->f[1], "on") == 0)
 			hwaccel = 1;
@@ -424,14 +377,11 @@
 	cmderror(cb, "bad VGA control message");
 }
 
-char Enooverlay[] = "No overlay support";
-
 static long
 vgawrite(Chan* c, void* a, long n, vlong off)
 {
 	ulong offset = off;
 	Cmdbuf *cb;
-	VGAscr *scr;
 
 	switch((ulong)c->qid.path){
 
@@ -446,26 +396,9 @@
 			free(cb);
 			nexterror();
 		}
-		vgactl(cb);
+		vgactl(&vgascreen[0], cb);
 		poperror();
 		free(cb);
-		return n;
-
-	case Qvgaovl:
-		scr = &vgascreen[0];
-		if (scr->dev == nil || scr->dev->ovlwrite == nil) {
-			error(Enooverlay);
-			break;
-		}
-		return scr->dev->ovlwrite(scr, a, n, off);
-
-	case Qvgaovlctl:
-		scr = &vgascreen[0];
-		if (scr->dev == nil || scr->dev->ovlctl == nil) {
-			error(Enooverlay);
-			break;
-		}
-		scr->dev->ovlctl(scr, c, a, n);
 		return n;
 
 	default:
--- a/sys/src/9/pc/screen.c
+++ b/sys/src/9/pc/screen.c
@@ -16,40 +16,78 @@
 
 extern VGAcur vgasoftcur;
 
-Rectangle physgscreenr;
-
 Memimage *gscreen;
 
 VGAscr vgascreen[1];
 
-int
-screensize(int x, int y, int, ulong chan)
+char *tiltstr[4] = {
+	"none",
+	"left",
+	"inverted",
+	"right",
+};
+
+static Point
+tiltpt(int tilt, Point dim, Point p)
 {
-	VGAscr *scr;
+	switch(tilt&3){
+	case 1:	return Pt(dim.y-p.y-1, p.x);
+	case 2:	return Pt(dim.x-p.x-1, dim.y-p.y-1);
+	case 3:	return Pt(p.y, dim.x-p.x-1);
+	}
+	return p;
+}
 
+static Rectangle
+tiltrect(int tilt, Point dim, Rectangle r)
+{
+	switch(tilt&3){
+	case 1:	return Rect(dim.y-r.max.y, r.min.x, dim.y-r.min.y, r.max.x);
+	case 2:	return Rect(dim.x-r.max.x, dim.y-r.max.y, dim.x-r.min.x, dim.y-r.min.y);
+	case 3:	return Rect(r.min.y, dim.x-r.max.x, r.max.y, dim.x-r.min.x);
+	}
+	return r;
+}
+
+static Point
+tiltsize(int tilt, Point dim)
+{
+	return (tilt & 1) != 0 ? Pt(dim.y, dim.x) : dim;
+}
+
+Rectangle
+actualscreensize(VGAscr *scr)
+{
+	return Rpt(ZP, tiltsize(-scr->tilt, scr->gscreen->clipr.max));
+}
+
+void
+setactualsize(VGAscr *scr, Rectangle r)
+{
 	qlock(&drawlock);
-	if(waserror()){
+
+	r.min = ZP;
+	r.max = tiltsize(scr->tilt, r.max);
+	if(rectclip(&r, scr->gscreen->r) == 0){
 		qunlock(&drawlock);
-		nexterror();
+		return;
 	}
+	scr->gscreen->clipr = r;
 
-	if(memimageinit() < 0)
-		error("memimageinit failed");
+	qunlock(&drawlock);
+}
 
-	lock(&vgascreenlock);
-	if(waserror()){
-		unlock(&vgascreenlock);
-		nexterror();
-	}
+static char*
+setscreensize0(VGAscr *scr, int width, int height, int depth, ulong chan, int tilt)
+{
+	int bpp, pitch;
 
-	scr = &vgascreen[0];
 	scr->gscreendata = nil;
 	scr->gscreen = nil;
-	if(gscreen){
+	if(gscreen != nil){
 		freememimage(gscreen);
 		gscreen = nil;
 	}
-
 	if(scr->paddr == 0){
 		if(scr->dev && scr->dev->page){
 			scr->vaddr = KADDR(VGAMEM());
@@ -57,8 +95,15 @@
 		}
 		scr->softscreen = 1;
 	}
+
+	depth = chantodepth(chan);
+	bpp = (depth+7) / 8;
+	pitch = ((width * depth+31) & ~31) / 8;
+
+	if(tilt)
+		scr->softscreen = 1;
 	if(scr->softscreen){
-		gscreen = allocmemimage(Rect(0,0,x,y), chan);
+		gscreen = allocmemimage(Rpt(ZP, tiltsize(tilt, Pt(width, height))), chan);
 		scr->useflush = 1;
 	}else{
 		static Memdata md;
@@ -66,20 +111,52 @@
 		md.ref = 1;
 		if((md.bdata = scr->vaddr) == 0)
 			error("framebuffer not maped");
-		gscreen = allocmemimaged(Rect(0,0,x,y), chan, &md);
+		gscreen = allocmemimaged(Rpt(ZP, Pt(width, height)), chan, &md);
 		scr->useflush = scr->dev && scr->dev->flush;
 	}
 	if(gscreen == nil)
-		error("no memory for vga memimage");
+		return "no memory for vga memimage";
 
+	scr->bpp = bpp;
+	scr->pitch = pitch;
+	scr->width = width;
+	scr->height = height;
+	scr->tilt = tilt & 3;
+
 	scr->palettedepth = 6;	/* default */
 	scr->memdefont = getmemdefont();
 	scr->gscreen = gscreen;
 	scr->gscreendata = gscreen->data;
 
-	physgscreenr = gscreen->r;
+	return nil;
+}
 
+void
+setscreensize(VGAscr *scr, int x, int y, int z, ulong chan, int tilt)
+{
+	char *err;
+
+	qlock(&drawlock);
+	if(waserror()){
+		qunlock(&drawlock);
+		nexterror();
+	}
+
+	if(memimageinit() < 0)
+		error("memimageinit failed");
+
+	lock(&vgascreenlock);
+	if(waserror()){
+		unlock(&vgascreenlock);
+		nexterror();
+	}
+
+	err = setscreensize0(scr, x, y, z, chan, tilt);
+	if(err != nil)
+		error(err);
+
 	vgaimageinit(chan);
+	bootscreenconf(scr);
 
 	unlock(&vgascreenlock);
 	poperror();
@@ -86,26 +163,27 @@
 
 	drawcmap();
 
+	if(scr->cur && scr->cur != &vgasoftcur){
+		cursoroff();
+		setcursor(&cursor);
+		cursoron();
+	}
+
 	qunlock(&drawlock);
 	poperror();
-
-	return 0;
 }
 
 int
-screenaperture(int size, int align)
+screenaperture(VGAscr *scr, int size, int align)
 {
-	VGAscr *scr;
 	uvlong pa;
 
-	scr = &vgascreen[0];
+	if(size == 0)
+		return 0;
 
 	if(scr->paddr)	/* set up during enable */
 		return 0;
 
-	if(size == 0)
-		return 0;
-
 	if(scr->dev && scr->dev->linear){
 		scr->dev->linear(scr, size, align);
 		return 0;
@@ -152,19 +230,61 @@
 {
 	VGAscr *scr;
 	uchar *sp, *disp, *sdisp, *edisp;
-	int y, len, incs, off, page;
+	int x, y, len, incs, off, page;
 
 	scr = &vgascreen[0];
 	if(scr->gscreen == nil || scr->useflush == 0)
 		return;
-	if(rectclip(&r, scr->gscreen->r) == 0)
+	if(rectclip(&r, scr->gscreen->clipr) == 0)
 		return;
+
+	if(scr->tilt){
+		Point size;
+
+		/* only supported on linear framebuffer */
+		disp = scr->vaddr;
+		if(scr->paddr == 0 || disp == nil)
+			return;
+
+		size = scr->gscreen->clipr.max;
+		r = tiltrect(-scr->tilt, size, r);
+		size = tiltsize(-scr->tilt, size);
+		sp = byteaddr(scr->gscreen, tiltpt(scr->tilt, size, r.min));
+		incs = byteaddr(scr->gscreen, tiltpt(scr->tilt, size, Pt(r.min.x+1, r.min.y))) - sp;
+
+		for(;;){
+			sdisp = disp + r.min.y * scr->pitch;
+			for(x = r.min.x; x < r.max.x; x++, sp += incs){
+				switch(scr->bpp){
+				case 4:
+					((ulong*)sdisp)[x] = *(ulong*)sp;
+					break;
+				case 3:
+					sdisp[x*3+0] = sp[0];
+					sdisp[x*3+1] = sp[1];
+					sdisp[x*3+2] = sp[2];
+					break;
+				case 2:
+					((ushort*)sdisp)[x] = *(ushort*)sp;
+					break;
+				case 1:
+					sdisp[x] = sp[0];
+					break;
+				}
+			}
+			if(++r.min.y >= r.max.y)
+				break;
+			sp = byteaddr(scr->gscreen, tiltpt(scr->tilt, size, r.min));
+		}
+		return;
+	}
+
 	if(scr->dev && scr->dev->flush){
 		scr->dev->flush(scr, r);
 		return;
 	}
 	disp = scr->vaddr;
-	incs = scr->gscreen->width*sizeof(ulong);
+	incs = scr->pitch;
 	off = (r.min.x*scr->gscreen->depth) / 8;
 	len = (r.max.x*scr->gscreen->depth + 7) / 8;
 	len -= off;
@@ -299,16 +419,48 @@
 	return setpalette(p, r, g, b);
 }
 
+static void
+tiltcursor(int t, Cursor *src, Cursor *dst)
+{
+	static Point dim = {16, 16};
+	uint i, j, im, jm;
+	Point p;
+
+	for(i = 0; i < 16*16; i++){
+		p = tiltpt(t, dim, Pt(i&15,i>>4));
+		j = p.y<<4 | p.x;
+		im = 0x80>>(i&7);
+		jm = 0x80>>(j&7);
+		if(src->clr[i>>3] & im)
+			dst->clr[j>>3] |= jm;
+		else
+			dst->clr[j>>3] &= ~jm;
+		if(src->set[i>>3] & im)
+			dst->set[j>>3] |= jm;
+		else
+			dst->set[j>>3] &= ~jm;
+	}
+
+	p = Pt(-src->offset.x & 15, -src->offset.y & 15);
+	p = tiltpt(t, dim, p);
+	dst->offset = Pt(-p.x, -p.y);
+}
+
 void
 cursoron(void)
 {
 	VGAscr *scr;
 	VGAcur *cur;
+	Point p;
 
 	scr = &vgascreen[0];
 	cur = scr->cur;
-	if(cur && cur->move)
-		cur->move(scr, mousexy());
+	if(cur && cur->move){
+		p = mousexy();
+		if(scr->tilt && cur != &vgasoftcur)
+			p = tiltpt(-scr->tilt, scr->gscreen->clipr.max, p);
+		cur->move(scr, p);
+	}
 }
 
 void
@@ -324,13 +476,18 @@
 
 	scr = &vgascreen[0];
 	cur = scr->cur;
-	if(cur && cur->load)
+	if(cur && cur->load){
+		if(scr->tilt && cur != &vgasoftcur){
+			static Cursor tmp;
+			tiltcursor(-scr->tilt, curs, &tmp);
+			curs = &tmp;
+		}
 		cur->load(scr, curs);
+	}
 }
 
 int hwaccel = 0;
 int hwblank = 0;
-int panning = 0;
 
 int
 hwdraw(Memdrawparam *par)
@@ -358,7 +515,7 @@
 		if(mask && mask->data->bdata == scrd->bdata)
 			swcursoravoid(par->mr);
 	}
-	if(!hwaccel || scr->softscreen)
+	if(!hwaccel || scr->softscreen || scr->tilt)
 		return 0;
 	if(dst->data->bdata != scrd->bdata || src == nil || mask == nil)
 		return 0;
@@ -396,13 +553,13 @@
 {
 	VGAscr *scr;
 
+	if(!hwblank)
+		return;
 	scr = &vgascreen[0];
-	if(hwblank){
-		if(scr->blank)
-			scr->blank(scr, blank);
-		else
-			vgablank(scr, blank);
-	}
+	if(scr->blank)
+		scr->blank(scr, blank);
+	else
+		vgablank(scr, blank);
 }
 
 static char*
@@ -461,6 +618,8 @@
 	Pcidev *p;
 	
 	p = scr->pci;
+	if(p == nil)
+		return "no pci card";
 
 	/*
 	 * Scan for largest memory region on card.
@@ -575,7 +734,7 @@
 bootscreeninit(void)
 {
 	VGAscr *scr;
-	int x, y, z;
+	int x, y, z, tilt;
 	uvlong pa;
 	ulong chan, sz;
 	char *s, *p, *err;
@@ -608,35 +767,37 @@
 	pa = strtoull(p+1, &s, 0);
 	if(pa == 0)
 		return;
-	if(*s++ == ' ')
-		sz = strtoul(s, nil, 0);
+	if(*s == ' ')
+		sz = strtoul(s+1, nil, 0);
 	if(sz < x * y * (z+7)/8)
 		sz = x * y * (z+7)/8;
 
-	/* map framebuffer */
+	tilt = 0;
+	if((p = getconf("tiltscreen")) != nil){
+		for(; tilt < nelem(tiltstr); tilt++)
+			if(strcmp(p, tiltstr[tilt]) == 0)
+				break;
+		tilt &= 3;
+	}
+
 	scr = &vgascreen[0];
+	scr->dev = nil;
+	scr->softscreen = 1;
+
 	if((err = bootmapfb(scr, pa, sz)) != nil){
 		print("bootmapfb: %s\n", err);
 		return;
 	}
 
-	if(memimageinit() < 0)
+	if(memimageinit() < 0){
+		print("memimageinit failed\n");
 		return;
-
-	gscreen = allocmemimage(Rect(0,0,x,y), chan);
-	if(gscreen == nil)
+	}
+	if((err = setscreensize0(scr, x, y, z, chan, tilt)) != nil){
+		print("setscreensize0: %s\n", err);
 		return;
+	}
 
-	scr->palettedepth = 6;	/* default */
-	scr->memdefont = getmemdefont();
-	scr->gscreen = gscreen;
-	scr->gscreendata = gscreen->data;
-	scr->softscreen = 1;
-	scr->useflush = 1;
-	scr->dev = nil;
-
-	physgscreenr = gscreen->r;
-
 	vgaimageinit(chan);
 	vgascreenwin(scr);
 
@@ -659,10 +820,11 @@
 	char conf[100], chan[30];
 
 	conf[0] = '\0';
-	if(scr != nil && scr->paddr != 0 && scr->gscreen != nil)
+	if(scr != nil && scr->paddr != 0 && scr->gscreen != nil){
 		snprint(conf, sizeof(conf), "%dx%dx%d %s 0x%.8llux %d\n",
-			scr->gscreen->r.max.x, scr->gscreen->r.max.y,
-			scr->gscreen->depth, chantostr(chan, scr->gscreen->chan),
+			scr->width, scr->height, scr->gscreen->depth, chantostr(chan, scr->gscreen->chan),
 			scr->paddr, scr->apsize);
+		ksetenv("tiltscreen", tiltstr[scr->tilt], 1);
+	}
 	ksetenv("*bootscreen", conf, 1);
 }
--- a/sys/src/9/pc/screen.h
+++ b/sys/src/9/pc/screen.h
@@ -75,8 +75,6 @@
 	void	(*disable)(VGAscr*);
 	void	(*load)(VGAscr*, Cursor*);
 	int	(*move)(VGAscr*, Point);
-
-	int	doespanning;
 };
 
 /*
@@ -96,6 +94,12 @@
 	void*	vaddr;
 	int	apsize;
 
+	int	bpp;
+	int	pitch;
+
+	int	width;
+	int	height;
+
 	ulong	io;		/* device specific registers */
 	ulong	*mmio;
 	
@@ -110,8 +114,8 @@
 	int	(*scroll)(VGAscr*, Rectangle, Rectangle);
 	void	(*blank)(VGAscr*, int);
 	ulong	id;	/* internal identifier for driver use */
-	int	overlayinit;
 	int	softscreen;
+	int	tilt;
 };
 
 extern VGAscr vgascreen[];
@@ -128,7 +132,10 @@
 /* screen.c */
 extern int	hwaccel;	/* use hw acceleration */
 extern int	hwblank;	/* use hw blanking */
-extern int	panning;	/* use virtual screen panning */
+extern char	*tiltstr[4];
+extern Rectangle actualscreensize(VGAscr*);
+extern void	setactualsize(VGAscr*, Rectangle);
+extern void	setscreensize(VGAscr*, int, int, int, ulong, int);
 extern void	addvgaseg(char*, uvlong, ulong);
 extern Memdata*	attachscreen(Rectangle*, ulong*, int*, int*, int*);
 extern void	flushmemscreen(Rectangle);
@@ -135,9 +142,7 @@
 extern void	cursoron(void);
 extern void	cursoroff(void);
 extern void	setcursor(Cursor*);
-extern int	screensize(int, int, int, ulong);
-extern int	screenaperture(int, int);
-extern Rectangle physgscreenr;	/* actual monitor size */
+extern int	screenaperture(VGAscr*, int, int);
 extern void	blankscreen(int);
 extern char*	rgbmask2chan(char *buf, int depth, u32int rm, u32int gm, u32int bm);
 extern void	bootscreeninit(void);
--- a/sys/src/9/pc/vgact65545.c
+++ b/sys/src/9/pc/vgact65545.c
@@ -34,7 +34,7 @@
 	 * Find a place for the cursor data in display memory.
 	 * Must be on a 1024-byte boundary.
 	 */
-	storage = ROUND(scr->gscreen->width*sizeof(ulong)*scr->gscreen->r.max.y, 1024);
+	storage = ROUND(scr->pitch*scr->height, 1024);
 	outl(0xB3D0, storage);
 	scr->storage = storage;
 
--- a/sys/src/9/pc/vgacyber938x.c
+++ b/sys/src/9/pc/vgacyber938x.c
@@ -170,7 +170,7 @@
 	/*
 	 * Find a place for the cursor data in display memory.
 	 */
-	storage = ((scr->gscreen->width*sizeof(ulong)*scr->gscreen->r.max.y+1023)/1024);
+	storage = ((scr->pitch*scr->height+1023)/1024);
 	vgaxo(Crtx, 0x44, storage & 0xFF);
 	vgaxo(Crtx, 0x45, (storage>>8) & 0xFF);
 	storage *= 1024;
--- a/sys/src/9/pc/vgaet4000.c
+++ b/sys/src/9/pc/vgaet4000.c
@@ -79,7 +79,7 @@
 	 * 1024-byte boundary so that there's no danger of it
 	 * crossing a page.
 	 */
-	scr->storage = (scr->gscreen->width*sizeof(ulong)*scr->gscreen->r.max.y+1023)/1024;
+	scr->storage = (scr->pitch*scr->height+1023)/1024;
 	scr->storage *= 1024/4;
 	outb(0x217A, 0xE8);
 	outb(0x217B, scr->storage & 0xFF);
--- a/sys/src/9/pc/vgamach64xx.c
+++ b/sys/src/9/pc/vgamach64xx.c
@@ -80,12 +80,6 @@
 #define HOST_MEM_MODE_V             		0xC0000000
 #define HOST_MEM_MODE_NORMAL     		HOST_YUV_APERTURE_UPPER 
 
-static Chan *ovl_chan;		/* Channel of controlling process */
-static int ovl_width;		/* Width of input overlay buffer */
-static int ovl_height;		/* Height of input overlay buffer */
-static int ovl_format;		/* Overlay format */
-static ulong ovl_fib;		/* Frame in bytes */
-
 enum {
 	 VTGTB1S1	= 0x01, /* Asic description for VTB1S1 and GTB1S1. */
 	 VT4GTIIC	= 0x3A,		/* asic descr for VT4 and RAGE IIC */
@@ -117,7 +111,6 @@
 static ulong mach64refclock;
 static Mach64types *mach64type;
 static int mach64revb;			/* Revision B or greater? */
-static ulong mach64overlay;		/* Overlay buffer */
 
 static Mach64types mach64s[] = {
 	('C'<<8)|'T',	0,	1350000, /*?*/	0,	/* 4354: CT */
@@ -480,71 +473,12 @@
 	       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(panning && 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,
 	 * or it disappears. Therefore, if x or y is -ve, adjust the
 	 * cursor presets instead. If y is negative also have to
@@ -589,7 +523,7 @@
 	 * Find a place for the cursor data in display memory.
 	 * Must be 64-bit aligned.
 	 */
-	storage = (scr->gscreen->width*sizeof(ulong)*scr->gscreen->r.max.y+7)/8;
+	storage = (scr->pitch*scr->height+7)/8;
 	iow32(scr, CurOffset, storage);
 	scr->storage = storage*8;
 
@@ -644,56 +578,15 @@
 }
 
 static void
-init_overlayclock(VGAscr *scr)
-{
-	uchar *cc, save, pll_ref_div, pll_vclk_cntl, vclk_post_div, 
-			vclk_fb_div, ecp_div;
-	int i;
-	ulong dotclock;
-
-	/* Taken from GLX */
-	/* Get monitor dotclock, check for Overlay Scaler clock limit */
- 	cc = (uchar *)&scr->mmio[mmoffset[ClockCntl]];
-  	save = cc[1]; i = cc[0] & 3;
-  	cc[1] = 2<<2; pll_ref_div = cc[2];
-  	cc[1] = 5<<2; pll_vclk_cntl = cc[2];
-  	cc[1] = 6<<2; vclk_post_div = (cc[2]>>(i+i)) & 3;
-  	cc[1] = (7+i)<<2; vclk_fb_div = cc[2];
-
-	dotclock = 2 * mach64refclock * vclk_fb_div / 
-			(pll_ref_div * (1 << vclk_post_div));
-	/* ecp_div: 0=dotclock, 1=dotclock/2, 2=dotclock/4 */
-  	ecp_div = dotclock / mach64type->m64_ovlclock;
-  	if (ecp_div>2) ecp_div = 2;
-
-  	/* Force a scaler clock factor of 1 if refclock *
-   	  * is unknown (VCLK_SRC not PLLVCLK)  */
-  	if ((pll_vclk_cntl & 0x03) != 0x03) 
-		ecp_div = 0;
-  	if ((pll_vclk_cntl & 0x30) != ecp_div<<4) {
-    		cc[1] = (5<<2)|2;
-    		cc[2] = (pll_vclk_cntl&0xCF) | (ecp_div<<4);
-	}
-
-  	/* Restore PLL Register Index */
-  	cc[1] = save;
-}
-
-static void
 initengine(VGAscr *scr)
 {
-	ulong pitch;
 	uchar *bios;
 	ushort table;
 
-	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, DstOffPitch, scr->pitch<<22);
 	iow32(scr, DstYX, 0);
 	iow32(scr, DstHeight, 0);
 	iow32(scr, DstBresErr, 0);
@@ -700,7 +593,7 @@
 	iow32(scr, DstBresInc, 0);
 	iow32(scr, DstBresDec, 0);
 	iow32(scr, DstCntl, 0x23);
-	iow32(scr, SrcOffPitch, pitch<<22);
+	iow32(scr, SrcOffPitch, scr->pitch<<22);
 	iow32(scr, SrcYX, 0);
 	iow32(scr, SrcHeight1Width1, 1);
 	iow32(scr, SrcYXstart, 0);
@@ -793,7 +686,6 @@
 static int
 mach64hwfill(VGAscr *scr, Rectangle r, ulong sval)
 {
-	ulong pitch;
 	ulong ctl;
 
 	/* shouldn't happen */
@@ -800,12 +692,10 @@
 	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);
 	}
 
@@ -817,7 +707,7 @@
 	iow32(scr, ClrCmpCntl, 0x00000000);
 	iow32(scr, ScLeftRight, 0x1FFF0000);
 	iow32(scr, ScTopBottom, 0x1FFF0000);
-	iow32(scr, DstOffPitch, pitch<<22);
+	iow32(scr, DstOffPitch, scr->pitch<<22);
 	iow32(scr, DstCntl, ctl);
 	iow32(scr, DstYX, (r.min.x<<16)|r.min.y);
 	iow32(scr, DstHeightWidth, (Dx(r)<<16)|Dy(r));
@@ -829,7 +719,6 @@
 static int
 mach64hwscroll(VGAscr *scr, Rectangle r, Rectangle sr)
 {
-	ulong pitch;
 	Point dp, sp;
 	ulong ctl;
 	int dx, dy;
@@ -836,10 +725,8 @@
 
 	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;
 	}
@@ -875,11 +762,11 @@
 	iow32(scr, ClrCmpCntl, 0x00000000);
 
 	waitforfifo(scr, 8);
-	iow32(scr, SrcOffPitch, pitch<<22);
+	iow32(scr, SrcOffPitch, scr->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, DstOffPitch, scr->pitch<<22);
 	iow32(scr, DstCntl, ctl);
 
 	iow32(scr, DstYX, (dp.x<<16)|dp.y);
@@ -957,222 +844,6 @@
 	}
 }
 
-static void
-ovl_configure(VGAscr *scr, Chan *c, char **field)
-{
-	int w, h;
-	char *format;
-
-	w = (int)strtol(field[1], nil, 0);
-	h = (int)strtol(field[2], nil, 0);
-	format = field[3];
-
-	if (c != ovl_chan) 
-		error(Einuse);
-	if (strcmp(format, "YUYV"))
-		error(Eunsupportedformat);
-	
-	ovl_width  = w;
-	ovl_height = h;
-	ovl_fib       = w * h * sizeof(ushort);
-
-	waitforidle(scr);
-	scr->mmio[mmoffset[BusCntl]] |= 0x08000000;	/* Enable regblock 1 */
-	scr->mmio[mmoffset[OverlayScaleCntl]] = 
-		SCALE_ZERO_EXTEND|SCALE_RED_TEMP_6500K|
-		SCALE_HORZ_BLEND|SCALE_VERT_BLEND;
-	scr->mmio[mmoffset[!mach64revb? Buf0Pitch: ScalerBuf0Pitch]] = w;
-	scr->mmio[mmoffset[CaptureConfig]] = 
-		SCALER_FRAME_READ_MODE_FULL|
-		SCALER_BUF_MODE_SINGLE|
-		SCALER_BUF_NEXT_0;
-	scr->mmio[mmoffset[OverlayKeyCntl]] = !mach64revb?
-		OVERLAY_MIX_ALWAYS_V|(OVERLAY_EXCLUSIVE_NORMAL << 28): 
-		0x011;
-
-	if (mach64type->m64_pro) {
-		waitforfifo(scr, 6);
-
-		/* set the scaler co-efficient registers */
-		scr->mmio[mmoffset[ScalerColourCntl]] = 
-			(0x00) | (0x10 << 8) | (0x10 << 16);
-		scr->mmio[mmoffset[ScalerHCoef0]] = 
-			(0x00) | (0x20 << 8);
-		scr->mmio[mmoffset[ScalerHCoef1]] = 
-			(0x0D) | (0x20 << 8) | (0x06 << 16) | (0x0D << 24);
-		scr->mmio[mmoffset[ScalerHCoef2]] = 
-			(0x0D) | (0x1C << 8) | (0x0A << 16) | (0x0D << 24);
-		scr->mmio[mmoffset[ScalerHCoef3]] = 
-			(0x0C) | (0x1A << 8) | (0x0E << 16) | (0x0C << 24);
-		scr->mmio[mmoffset[ScalerHCoef4]] = 
-			(0x0C) | (0x14 << 8) | (0x14 << 16) | (0x0C << 24);
-	}
-	
-	waitforfifo(scr, 3);
-	scr->mmio[mmoffset[VideoFormat]] = SCALE_IN_YVYU422 |
-		(!mach64revb? 0xC: 0);
-
-	if (mach64overlay == 0)
-		mach64overlay = scr->storage + 64 * 64 * sizeof(uchar);
-	scr->mmio[mmoffset[!mach64revb? Buf0Offset: ScalerBuf0Offset]] = 
-		mach64overlay;
-}
-
-static void
-ovl_enable(VGAscr *scr, Chan *c, char **field)
-{
-	int x, y, w, h;
-	long h_inc, v_inc;
-
-	x = (int)strtol(field[1], nil, 0);
-	y = (int)strtol(field[2], nil, 0);
-	w = (int)strtol(field[3], nil, 0);
-	h = (int)strtol(field[4], nil, 0);
-
-	if (x < 0 || x + w > physgscreenr.max.x ||
-	     y < 0 || y + h > physgscreenr.max.y)
-		error(Ebadarg);
-
-	if (c != ovl_chan) 
-		error(Einuse);
-	if (scr->mmio[mmoffset[CrtcGenCntl]] & 1) {	/* double scan enable */
-		y *= 2;
-		h *= 2;
-	}
-
-	waitforfifo(scr, 2);
-	scr->mmio[mmoffset[OverlayYX]] = 
-			((x & 0xFFFF) << 16) | (y & 0xFFFF);
-	scr->mmio[mmoffset[OverlayYXEnd]] = 
-			(((x + w) & 0xFFFF) << 16) | ((y + h) & 0xFFFF);
-
-	h_inc = (ovl_width << 12) / (w >> 1);  /* ??? */
-	v_inc = (ovl_height << 12) / h;
-	waitforfifo(scr, 2);
-	scr->mmio[mmoffset[OverlayScaleInc]] = 
-			((h_inc & 0xFFFF) << 16) | (v_inc & 0xFFFF);
-	scr->mmio[mmoffset[ScalerHeightWidth]] = 
-			((ovl_width & 0xFFFF) << 16) | (ovl_height & 0xFFFF);
-	waitforidle(scr);
-	scr->mmio[mmoffset[OverlayScaleCntl]] |= 
-			(SCALE_ENABLE|OVERLAY_ENABLE);
-}
-
-static void
-ovl_status(VGAscr *scr, Chan *, char **field)
-{
-	pprint("%s: %s %.4uX, VT/GT %s, PRO %s, ovlclock %lud, rev B %s, refclock %ld\n",
-		   scr->dev->name, field[0], mach64type->m64_id,
-		   mach64type->m64_vtgt? "yes": "no",
-		   mach64type->m64_pro? "yes": "no",
-		   mach64type->m64_ovlclock,
-		   mach64revb? "yes": "no",
-		   mach64refclock);
-	pprint("%s: storage @%.8luX, aperture @%8.ullX, ovl buf @%.8ulX\n",
-		   scr->dev->name, scr->storage, scr->paddr,
-		   mach64overlay);
-}
-	
-static void
-ovl_openctl(VGAscr *, Chan *c, char **)
-{
-	if (ovl_chan) 
-		error(Einuse);
-	ovl_chan = c;
-}
-
-static void
-ovl_closectl(VGAscr *scr, Chan *c, char **)
-{
-	if (c != ovl_chan) return;
-
-	waitforidle(scr);
-	scr->mmio[mmoffset[OverlayScaleCntl]] &=
-			~(SCALE_ENABLE|OVERLAY_ENABLE);
-	ovl_chan = nil;
-	ovl_width = ovl_height = ovl_fib = 0;
-}
-
-enum
-{
-	CMclosectl,
-	CMconfigure,
-	CMenable,
-	CMopenctl,
-	CMstatus,
-};
-
-static void (*ovl_cmds[])(VGAscr *, Chan *, char **) =
-{
-	[CMclosectl]	ovl_closectl,
-	[CMconfigure]	ovl_configure,
-	[CMenable]	ovl_enable,
-	[CMopenctl]	ovl_openctl,
-	[CMstatus]	ovl_status,
-};
-
-static Cmdtab mach64xxcmd[] =
-{
-	CMclosectl,	"closectl",	1,
-	CMconfigure,	"configure",	4,
-	CMenable,	"enable",	5,
-	CMopenctl,	"openctl",	1,
-	CMstatus,	"status",	1,
-};
-
-static void
-mach64xxovlctl(VGAscr *scr, Chan *c, void *a, int n)
-{
-	Cmdbuf *cb;
-	Cmdtab *ct;
-
-	if(!mach64type->m64_vtgt) 
-		error(Enodev);
-
-	if(!scr->overlayinit){
-		scr->overlayinit = 1;
-		init_overlayclock(scr);
-	}
-	cb = parsecmd(a, n);
-	if(waserror()){
-		free(cb);
-		nexterror();
-	}
-
-	ct = lookupcmd(cb, mach64xxcmd, nelem(mach64xxcmd));
-
-	ovl_cmds[ct->index](scr, c, cb->f);
-
-	poperror();
-	free(cb);
-}
-
-static int
-mach64xxovlwrite(VGAscr *scr, void *a, int len, vlong offs)
-{
-	uchar *src;
-	int _len;
-
-	if (ovl_chan == nil) return len;	/* Acts as a /dev/null */
-	
-	/* Calculate the destination address */
-	_len = len;
-	src   = (uchar *)a;
-	while (len > 0) {
-		ulong _offs;
-		int nb;
-
-		_offs = (ulong)(offs % ovl_fib);
-		nb     = (_offs + len > ovl_fib)? ovl_fib - _offs: len;
-		memmove((uchar *)scr->vaddr + mach64overlay + _offs, 
-				  src, nb);
-		offs += nb;
-		src  += nb;
-		len  -= nb;
-	}
-	return _len;
-}
-
 VGAdev vgamach64xxdev = {
 	"mach64xx",
 
@@ -1180,10 +851,7 @@
 	0,				/* disable */
 	0,				/* page */
 	mach64xxlinear,			/* linear */
-	mach64xxdrawinit,	/* drawinit */
-	0,
-	mach64xxovlctl,	/* overlay control */
-	mach64xxovlwrite,	/* write the overlay */
+	mach64xxdrawinit,		/* drawinit */
 };
 
 VGAcur vgamach64xxcur = {
@@ -1193,7 +861,5 @@
 	mach64xxcurdisable,		/* disable */
 	mach64xxcurload,		/* load */
 	mach64xxcurmove,		/* move */
-
-	1					/* doespanning */
 };
 
--- a/sys/src/9/pc/vgamga4xx.c
+++ b/sys/src/9/pc/vgamga4xx.c
@@ -405,7 +405,6 @@
 mga4xxscroll(VGAscr *scr, Rectangle dr, Rectangle sr)
 {
 	uchar * mga;
-	int pitch;
  	int width, height;
 	ulong start, end, sgn;
 	Point sp, dp;
@@ -421,7 +420,6 @@
 	if(eqpt(sp, dp))
 		return 1;
 
-	pitch = Dx(scr->gscreen->r);
 	width = Dx(sr);
 	height = Dy(sr);
 	sgn = 0;
@@ -433,7 +431,7 @@
 	}
 
 	width--;
-	start = end = sp.x + (sp.y * pitch);
+	start = end = sp.x + (sp.y * scr->width);
 
 	if(dp.x > sp.x && dp.x < sp.x + width){
 		start += width;
@@ -445,7 +443,7 @@
 	mga_fifo(mga, 8);
 	mgawrite32(mga, DWGCTL, 0);
 	mgawrite32(mga, SGN, sgn);
-	mgawrite32(mga, AR5, sgn & SGN_UP ? -pitch : pitch);
+	mgawrite32(mga, AR5, sgn & SGN_UP ? -scr->width : scr->width);
 	mgawrite32(mga, AR0, end);
 	mgawrite32(mga, AR3, start);
 	mgawrite32(mga, FXBNDRY, ((dp.x + width) << 16) | dp.x);
--- a/sys/src/9/pc/vganeomagic.c
+++ b/sys/src/9/pc/vganeomagic.c
@@ -376,8 +376,8 @@
 		| NEO_BC3_SKIP_MAPPING
 		| GXcopy;
 	mmio[DstStartOff] = scr->paddr
-		+ r.min.y*scr->gscreen->width*sizeof(ulong)
-		+ r.min.x*scr->gscreen->depth/BI2BY;
+		+ r.min.y*scr->pitch
+		+ r.min.x*scr->bpp;
 	mmio[XYExt] = (Dy(r) << 16) | (Dx(r) & 0xffff);
 	waitforidle(scr);
 	return 1;
@@ -387,13 +387,9 @@
 neomagichwscroll(VGAscr *scr, Rectangle r, Rectangle sr)
 {
 	ulong *mmio;
-	int pitch, pixel;
 
 	mmio = scr->mmio;
 
-	pitch = scr->gscreen->width*sizeof(ulong);
-	pixel = scr->gscreen->depth/BI2BY;
-
 	waitforfifo(scr, 4);
 	if (r.min.y < sr.min.y || (r.min.y == sr.min.y && r.min.x < sr.min.x)) {
 		/* start from upper-left */
@@ -402,9 +398,9 @@
 			| NEO_BC3_SKIP_MAPPING
 			| GXcopy;
 		mmio[SrcStartOff] = scr->paddr
-			+ sr.min.y*pitch + sr.min.x*pixel;
+			+ sr.min.y*scr->pitch + sr.min.x*scr->bpp;
 		mmio[DstStartOff] = scr->paddr
-			+ r.min.y*pitch + r.min.x*pixel;
+			+ r.min.y*scr->pitch + r.min.x*scr->bpp;
 	} else {
 		/* start from lower-right */
 		mmio[BltCntl] = neomagicbltflags
@@ -415,9 +411,9 @@
 			| NEO_BC3_SKIP_MAPPING
 			| GXcopy;
 		mmio[SrcStartOff] = scr->paddr
-			+ (sr.max.y-1)*pitch + (sr.max.x-1)*pixel;
+			+ (sr.max.y-1)*scr->pitch + (sr.max.x-1)*scr->bpp;
 		mmio[DstStartOff] = scr->paddr
-			+ (r.max.y-1)*pitch + (r.max.x-1)*pixel;
+			+ (r.max.y-1)*scr->pitch + (r.max.x-1)*scr->bpp;
 	}
 	mmio[XYExt] = (Dy(r) << 16) | (Dx(r) & 0xffff);
 	waitforidle(scr);
@@ -428,12 +424,10 @@
 neomagicdrawinit(VGAscr *scr)
 {
 	ulong *mmio;
-	uint bltmode, pitch;
+	uint bltmode;
 
 	mmio = scr->mmio;
 
-	pitch = scr->gscreen->width*sizeof(ulong);
-
 	neomagicbltflags = bltmode = 0;
 
 	switch(scr->gscreen->depth) {
@@ -450,7 +444,7 @@
 		return;
 	}
 
-	switch(Dx(scr->gscreen->r)) {
+	switch(scr->width) {
 	case 320:
 		bltmode |= NEO_MODE1_X_320;
 		neomagicbltflags |= NEO_BC1_X_320;
@@ -486,7 +480,7 @@
 
 	waitforidle(scr);
 	mmio[BltStat] = bltmode << 16;
-	mmio[Pitch] = (pitch << 16) | (pitch & 0xffff);
+	mmio[Pitch] = (scr->pitch << 16) | (scr->pitch & 0xffff);
 
 	scr->fill = neomagichwfill;
 	scr->scroll = neomagichwscroll;
--- a/sys/src/9/pc/vganvidia.c
+++ b/sys/src/9/pc/vganvidia.c
@@ -343,13 +343,11 @@
 nvresetgraphics(VGAscr *scr)
 {
 	ulong	surfaceFormat, patternFormat, rectFormat, lineFormat;
-	int	pitch, i;
+	int	i;
 
 	if(scr->paddr == 0)
 		return -1;
 
-	pitch = scr->gscreen->width*sizeof(ulong);
-
 	/*
 	 * DMA is at the end of the virtual window,
 	 * but we might have cut it short when mapping it.
@@ -416,7 +414,7 @@
 
 	nvdmastart(scr, SURFACE_FORMAT, 4);
 	nvdmanext(surfaceFormat);
-	nvdmanext(pitch | (pitch << 16));
+	nvdmanext(scr->pitch | (scr->pitch << 16));
 	nvdmanext(0);
 	nvdmanext(0);
 
--- a/sys/src/9/pc/vgaradeon.c
+++ b/sys/src/9/pc/vgaradeon.c
@@ -350,7 +350,7 @@
 static void
 radeondrawinit(VGAscr*scr)
 {
-	ulong bpp, dtype, i, pitch, clock_cntl_index, mclk_cntl, rbbm_soft_reset;
+	ulong dtype, i, clock_cntl_index, mclk_cntl, rbbm_soft_reset;
 
 	if (scr->mmio == 0)
 		return;
@@ -359,19 +359,15 @@
 	case 6:
 	case 8:
 		dtype = 2;
-		bpp = 1;
 		break;
 	case 15:
 		dtype = 3;
-		bpp = 2;
 		break;
 	case 16:
 		dtype = 4;
-		bpp = 2;
 		break;
 	case 32:
 		dtype = 6;
-		bpp = 4;
 		break;
 	default:
 		return;
@@ -413,11 +409,10 @@
 	radeonwaitfifo(scr, 1);
 	OUTREG(scr->mmio, RB2D_DSTCACHE_MODE, 0);
 
-	pitch = Dx(scr->gscreen->r) * bpp;
 	radeonwaitfifo(scr, 4);
-	OUTREG(scr->mmio, DEFAULT_PITCH, pitch);
-	OUTREG(scr->mmio, DST_PITCH, pitch);
-	OUTREG(scr->mmio, SRC_PITCH, pitch);
+	OUTREG(scr->mmio, DEFAULT_PITCH, scr->pitch);
+	OUTREG(scr->mmio, DST_PITCH, scr->pitch);
+	OUTREG(scr->mmio, SRC_PITCH, scr->pitch);
 	OUTREG(scr->mmio, DST_PITCH_OFFSET_C, 0);
 
 	radeonwaitfifo(scr, 3);
@@ -501,5 +496,4 @@
 	radeoncurdisable,
 	radeoncurload,
 	radeoncurmove,
-	0				/* doespanning */
 };
--- a/sys/src/9/pc/vgas3.c
+++ b/sys/src/9/pc/vgas3.c
@@ -309,7 +309,7 @@
 	 * Find a place for the cursor data in display memory.
 	 * Must be on a 1024-byte boundary.
 	 */
-	storage = (scr->gscreen->width*sizeof(ulong)*scr->gscreen->r.max.y+1023)/1024;
+	storage = (scr->pitch*scr->height+1023)/1024;
 	vgaxo(Crtx, 0x4C, storage>>8);
 	vgaxo(Crtx, 0x4D, storage & 0xFF);
 	storage *= 1024;
@@ -420,14 +420,12 @@
 {
 	enum { Bitbltop = 0xCC };	/* copy source */
 	ulong *mmio;
-	ulong cmd, stride;
+	ulong cmd;
 	Point dp, sp;
-	int did, d;
+	int did;
 
-	d = scr->gscreen->depth;
-	did = (d-8)/8;
+	did = (scr->gscreen->depth-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;
@@ -452,7 +450,7 @@
 	waitforfifo(scr, 7);
 	mmio[SrcBase] = scr->paddr;
 	mmio[DstBase] = scr->paddr;
-	mmio[Stride] = (stride<<16)|stride;
+	mmio[Stride] = (scr->pitch<<16)|scr->pitch;
 	mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r);
 	mmio[SrcXY] = (sp.x<<16)|sp.y;
 	mmio[DestXY] = (dp.x<<16)|dp.y;
@@ -466,13 +464,11 @@
 {
 	enum { Bitbltop = 0xCC };	/* copy source */
 	ulong *mmio;
-	ulong cmd, stride;
-	int did, d;
+	ulong cmd;
+	int did;
 
-	d = scr->gscreen->depth;
-	did = (d-8)/8;
+	did = (scr->gscreen->depth-8)/8;
 	cmd = 0x16000120|(Bitbltop<<17)|(did<<2);
-	stride = Dx(scr->gscreen->r)*d/8;
 	mmio = scr->mmio;
 	waitforlinearfifo(scr);
 	waitforfifo(scr, 8);
@@ -479,7 +475,7 @@
 	mmio[SrcBase] = scr->paddr;
 	mmio[DstBase] = scr->paddr;
 	mmio[DstBase] = scr->paddr;
-	mmio[Stride] = (stride<<16)|stride;
+	mmio[Stride] = (scr->pitch<<16)|scr->pitch;
 	mmio[FgrdData] = sval;
 	mmio[WidthHeight] = ((Dx(r)-1)<<16)|Dy(r);
 	mmio[DestXY] = (r.min.x<<16)|r.min.y;
--- a/sys/src/9/pc/vgasavage.c
+++ b/sys/src/9/pc/vgasavage.c
@@ -540,7 +540,7 @@
 
 	/* set bitmap descriptors */
 	bd = (scr->gscreen->depth<<DepthShift) |
-		(Dx(scr->gscreen->r)<<StrideShift) | BlockWriteDis
+		(scr->width<<StrideShift) | BlockWriteDis
 		| BDS64;
 
 	*(ulong*)(mmio+GBD1) = 0;
--- a/sys/src/9/pc/vgat2r4.c
+++ b/sys/src/9/pc/vgat2r4.c
@@ -459,14 +459,9 @@
 static void
 t2r4drawinit(VGAscr *scr)
 {
-	ulong pitch;
-	int depth;
 	int fmt;
 	ulong *d;
 
-	pitch = Dx(scr->gscreen->r);
-	depth = scr->gscreen->depth;
-
 	switch(scr->gscreen->chan){
 	case RGB16:
 		fmt = 3;
@@ -488,8 +483,8 @@
 	d[BufCtl] = fmt<<24;
 	d[DeSorg] = 0;
 	d[DeDorg] = 0;
-	d[DeSptch] = (pitch*depth)/8;
-	d[DeDptch] = (pitch*depth)/8;
+	d[DeSptch] = scr->pitch;
+	d[DeDptch] = scr->pitch;
 	d[CmdClp] = 0;	/* 2 = inside rectangle */
 	d[Mask] = ~0;
 	d[DeKey] = 0;