git: 9front

Download patch

ref: f64cf6fb2c810a1c319ae60ddf60f30930c48dd2
parent: ae61407f5db078f3c9415d9302073a70c48750a4
author: cinap_lenrek <cinap_lenrek@localhost>
date: Mon Apr 11 16:56:59 EDT 2011

vesa-changes

--- a/sys/src/9/pc/devvga.c
+++ b/sys/src/9/pc/devvga.c
@@ -368,11 +368,6 @@
 		if(screenaperture(size, align) < 0)
 			error("not enough free address space");
 		return;
-/*	
-	case CMmemset:
-		memset((void*)strtoul(cb->f[1], 0, 0), atoi(cb->f[2]), atoi(cb->f[3]));
-		return;
-*/
 
 	case CMblank:
 		drawblankscreen(1);
--- a/sys/src/9/pc/screen.c
+++ b/sys/src/9/pc/screen.c
@@ -19,7 +19,6 @@
 
 Rectangle physgscreenr;
 
-Memdata gscreendata;
 Memimage *gscreen;
 
 VGAscr vgascreen[1];
@@ -40,14 +39,19 @@
 
 int didswcursorinit;
 
-static void *softscreen;
-
 int
-screensize(int x, int y, int z, ulong chan)
+screensize(int x, int y, int, ulong chan)
 {
 	VGAscr *scr;
-	void *oldsoft;
 
+	qlock(&drawlock);
+	if(waserror()){
+		qunlock(&drawlock);
+		nexterror();
+	}
+
+	memimageinit();
+
 	lock(&vgascreenlock);
 	if(waserror()){
 		unlock(&vgascreenlock);
@@ -54,54 +58,51 @@
 		nexterror();
 	}
 
-	memimageinit();
 	scr = &vgascreen[0];
-	oldsoft = softscreen;
+	scr->gscreendata = nil;
+	scr->gscreen = nil;
+	if(gscreen){
+		freememimage(gscreen);
+		gscreen = nil;
+	}
 
 	if(scr->paddr == 0){
-		int width = (x*z)/BI2WD;
-		void *p;
-
-		p = xalloc(width*BY2WD*y);
-		if(p == nil)
-			error("no memory for vga soft screen");
-		gscreendata.bdata = softscreen = p;
+		gscreen = allocmemimage(Rect(0,0,x,y), chan);
 		if(scr->dev && scr->dev->page){
 			scr->vaddr = KADDR(VGAMEM());
 			scr->apsize = 1<<16;
 		}
 		scr->useflush = 1;
-	}
-	else{
-		gscreendata.bdata = scr->vaddr;
+	}else{
+		static Memdata md;
+
+		md.ref = 1;
+		md.bdata = scr->vaddr;
+		gscreen = allocmemimaged(Rect(0,0,x,y), chan, &md);
 		scr->useflush = scr->dev && scr->dev->flush;
 	}
-
-	scr->gscreen = nil;
-	if(gscreen)
-		freememimage(gscreen);
-	gscreen = allocmemimaged(Rect(0,0,x,y), chan, &gscreendata);
 	if(gscreen == nil)
 		error("no memory for vga memimage");
-	vgaimageinit(chan);
 
 	scr->palettedepth = 6;	/* default */
-	scr->gscreendata = &gscreendata;
 	scr->memdefont = getmemdefont();
 	scr->gscreen = gscreen;
+	scr->gscreendata = gscreen->data;
 
 	physgscreenr = gscreen->r;
+
+	vgaimageinit(chan);
+
 	unlock(&vgascreenlock);
 	poperror();
-	if(oldsoft)
-		xfree(oldsoft);
 
-	memimagedraw(gscreen, gscreen->r, memblack, ZP, nil, ZP, S);
-	flushmemscreen(gscreen->r);
-
+	drawcmap();
 	if(didswcursorinit)
 		swcursorinit();
-	drawcmap();
+
+	qunlock(&drawlock);
+	poperror();
+
 	return 0;
 }
 
@@ -152,8 +153,17 @@
 	*chan = scr->gscreen->chan;
 	*d = scr->gscreen->depth;
 	*width = scr->gscreen->width;
-	*softscreen = scr->useflush;
-
+	if(scr->gscreendata->allocd){
+		/*
+		 * we use a memimage as softscreen. devdraw will create its own
+		 * screen image on the backing store of that image. when our gscreen
+		 * and devdraws screenimage gets freed, the imagedata will
+		 * be released.
+		 */
+		*softscreen = 0xa110c;
+		scr->gscreendata->ref++;
+	} else
+		*softscreen = scr->useflush ? 1 : 0;
 	return scr->gscreendata->bdata;
 }
 
@@ -168,12 +178,12 @@
 	int y, len, incs, off, page;
 
 	scr = &vgascreen[0];
+	if(scr->gscreen == nil || scr->useflush == 0)
+		return;
 	if(scr->dev && scr->dev->flush){
 		scr->dev->flush(scr, r);
 		return;
 	}
-	if(scr->gscreen == nil || scr->useflush == 0)
-		return;
 	if(scr->dev == nil || scr->dev->page == nil)
 		return;
 
@@ -185,7 +195,6 @@
 	switch(scr->gscreen->depth){
 	default:
 		len = 0;
-		panic("flushmemscreen: depth\n");
 		break;
 	case 8:
 		len = Dx(r);
@@ -358,36 +367,31 @@
 {
 	VGAscr *scr;
 	Memimage *dst, *src, *mask;
+	Memdata *scrd;
 	int m;
 
-	if(hwaccel == 0)
-		return 0;
-
 	scr = &vgascreen[0];
-	if((dst=par->dst) == nil || dst->data == nil)
+	scrd = scr->gscreendata;
+	if(scr->gscreen == nil || scrd == nil)
 		return 0;
-	if((src=par->src) == nil || src->data == nil)
+	if((dst = par->dst) == nil || dst->data == nil)
 		return 0;
-	if((mask=par->mask) == nil || mask->data == nil)
-		return 0;
-
+	if((src = par->src) && src->data == nil)
+		src = nil;
+	if((mask = par->mask) && mask->data == nil)
+		mask = nil;
 	if(scr->cur == &swcursor){
-		/*
-		 * always calling swcursorhide here doesn't cure
-		 * leaving cursor tracks nor failing to refresh menus
-		 * with the latest libmemdraw/draw.c.
-		 */
-		if(dst->data->bdata == gscreendata.bdata)
+		if(dst->data->bdata == scrd->bdata)
 			swcursoravoid(par->r);
-		if(src->data->bdata == gscreendata.bdata)
+		if(src && src->data->bdata == scrd->bdata)
 			swcursoravoid(par->sr);
-		if(mask->data->bdata == gscreendata.bdata)
+		if(mask && mask->data->bdata == scrd->bdata)
 			swcursoravoid(par->mr);
 	}
-	
-	if(dst->data->bdata != gscreendata.bdata)
+	if(hwaccel == 0)
 		return 0;
-
+	if(dst->data->bdata != scrd->bdata || src == nil || mask == nil)
+		return 0;
 	if(scr->fill==nil && scr->scroll==nil)
 		return 0;
 
@@ -607,30 +611,7 @@
 	swvisible = 1;
 }
 
-/*
- * Need to lock drawlock for ourselves.
- */
 void
-swenable(VGAscr*)
-{
-	swenabled = 1;
-	if(canqlock(&drawlock)){
-		swcursordraw();
-		qunlock(&drawlock);
-	}
-}
-
-void
-swdisable(VGAscr*)
-{
-	swenabled = 0;
-	if(canqlock(&drawlock)){
-		swcursorhide();
-		qunlock(&drawlock);
-	}
-}
-
-void
 swload(VGAscr*, Cursor *curs)
 {
 	uchar *ip, *mp;
@@ -693,7 +674,7 @@
 void
 swcursorinit(void)
 {
-	static int init, warned;
+	static int init;
 	VGAscr *scr;
 
 	didswcursorinit = 1;
@@ -701,40 +682,54 @@
 		init = 1;
 		addclock0link(swcursorclock, 10);
 	}
+
 	scr = &vgascreen[0];
-	if(scr==nil || scr->gscreen==nil)
+	if(scr->gscreen==nil)
 		return;
 
-	if(scr->dev == nil || scr->dev->linear == nil){
-		if(!warned){
-			print("cannot use software cursor on non-linear vga screen\n");
-			warned = 1;
-		}
-		return;
-	}
-
 	if(swback){
 		freememimage(swback);
 		freememimage(swmask);
 		freememimage(swmask1);
 		freememimage(swimg);
-		freememimage(swimg1);
+		freememimage(swimg1); 
 	}
-
 	swback = allocmemimage(Rect(0,0,32,32), gscreen->chan);
 	swmask = allocmemimage(Rect(0,0,16,16), GREY8);
 	swmask1 = allocmemimage(Rect(0,0,16,16), GREY1);
 	swimg = allocmemimage(Rect(0,0,16,16), GREY8);
 	swimg1 = allocmemimage(Rect(0,0,16,16), GREY1);
-	if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil){
+	if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil)
 		print("software cursor: allocmemimage fails");
-		return;
-	}
-
+	memfillcolor(swback, DTransparent);
 	memfillcolor(swmask, DOpaque);
 	memfillcolor(swmask1, DOpaque);
 	memfillcolor(swimg, DBlack);
 	memfillcolor(swimg1, DBlack);
+}
+
+/*
+ * Need to lock drawlock for ourselves.
+ */
+void
+swenable(VGAscr *scr)
+{
+	swenabled = 1;
+	if(canqlock(&drawlock)){
+		swload(scr, &arrow);
+		swcursordraw();
+		qunlock(&drawlock);
+	}
+}
+
+void
+swdisable(VGAscr*)
+{
+	swenabled = 0;
+	if(canqlock(&drawlock)){
+		swcursorhide();
+		qunlock(&drawlock);
+	}
 }
 
 VGAcur swcursor =
--- a/sys/src/9/pc/vga.c
+++ b/sys/src/9/pc/vga.c
@@ -12,8 +12,8 @@
 #include <cursor.h>
 #include "screen.h"
 
-static Memimage* back;
 static Memimage *conscol;
+static Memimage *back;
 
 static Point curpos;
 static Rectangle window;
@@ -23,25 +23,10 @@
 int drawdebug;
 
 void
-vgaimageinit(ulong chan)
+vgaimageinit(ulong)
 {
-	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);
-	}
+	conscol = memblack;
+	back = memwhite;
 }
 
 static void
@@ -69,13 +54,11 @@
 	int h, w, pos;
 	Rectangle r;
 
-//	drawdebug = 1;
 	if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)])
 		xp = xbuf;
 
 	h = scr->memdefont->height;
 	switch(buf[0]){
-
 	case '\n':
 		if(curpos.y+h >= window.max.y){
 			vgascroll(scr);
@@ -100,7 +83,7 @@
 		pos = 4-(pos%4);
 		*xp++ = curpos.x;
 		r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y + h);
-		memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S);
+		memimagedraw(scr->gscreen, r, back, ZP, nil, ZP, S);
 		curpos.x += pos*w;
 		break;
 
@@ -109,7 +92,7 @@
 			break;
 		xp--;
 		r = Rect(*xp, curpos.y, curpos.x, curpos.y+h);
-		memimagedraw(scr->gscreen, r, back, back->r.min, nil, ZP, S);
+		memimagedraw(scr->gscreen, r, back, r.min, nil, ZP, S);
 		combinerect(flushr, r);
 		curpos.x = *xp;
 		break;
@@ -126,12 +109,11 @@
 
 		*xp++ = curpos.x;
 		r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h);
-		memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S);
+		memimagedraw(scr->gscreen, r, back, r.min, nil, ZP, S);
 		memimagestring(scr->gscreen, curpos, conscol, ZP, scr->memdefont, buf);
 		combinerect(flushr, r);
 		curpos.x += w;
 	}
-//	drawdebug = 0;
 }
 
 static void
@@ -184,19 +166,61 @@
 	unlock(&vgascreenlock);
 }
 
+static Memimage*
+mkcolor(Memimage *screen, ulong color)
+{
+	Memimage *i;
+
+	if(i = allocmemimage(Rect(0,0,1,1), screen->chan)){
+		i->flags |= Frepl;
+		i->clipr = screen->r;
+		memfillcolor(i, color);
+	}
+	return i;
+}
+
 void
 vgascreenwin(VGAscr* scr)
 {
-	int h, w;
+	Memimage *i;
+	Rectangle r;
+	Point p;
+	int h;
 
+	qlock(&drawlock);
+	
 	h = scr->memdefont->height;
-	w = scr->memdefont->info[' '].width;
+	r = scr->gscreen->r;
 
-	window = insetrect(scr->gscreen->r, 48);
-	window.max.x = window.min.x+((window.max.x-window.min.x)/w)*w;
-	window.max.y = window.min.y+((window.max.y-window.min.y)/h)*h;
+	if(i = mkcolor(scr->gscreen, 0x444488FF)){
+		memimagedraw(scr->gscreen, r, i, ZP, nil, ZP, S);
+		freememimage(i);
+	}
+
+	window = insetrect(r, 20);
+	memimagedraw(scr->gscreen, window, conscol, ZP, memopaque, ZP, S);
+	window = insetrect(window, 4);
+	memimagedraw(scr->gscreen, window, back, ZP, memopaque, ZP, S);
+
+	if(i = mkcolor(scr->gscreen, 0xAAAAAAFF)){
+		memimagedraw(scr->gscreen, Rect(window.min.x, window.min.y,
+			window.max.x, window.min.y+h+5+6), i, ZP, nil, ZP, S);
+		freememimage(i);
+
+		window = insetrect(window, 5);
+		p = addpt(window.min, Pt(10, 0));
+		memimagestring(scr->gscreen, p, memblack, ZP, scr->memdefont, " Plan 9 Console ");
+		window.min.y += h+6;
+	} else
+		window = insetrect(window, 5);
+
+	window.max.y = window.min.y+(Dy(window)/h)*h;
 	curpos = window.min;
 
+	flushmemscreen(r);
+
+	qunlock(&drawlock);
+
 	screenputs = vgascreenputs;
 }
 
@@ -239,25 +263,4 @@
 	seg.pa = pa;
 	seg.size = size;
 	addphysseg(&seg);
-}
-
-void
-cornerstring(char *s)
-{
-	int h, w;
-	VGAscr *scr;
-	Rectangle r;
-	Point p;
-
-	scr = &vgascreen[0];
-	if(scr->vaddr == nil || screenputs != vgascreenputs)
-		return;
-	p = memsubfontwidth(scr->memdefont, s);
-	w = p.x;
-	h = scr->memdefont->height;
-
-	r = Rect(0, 0, w, h);
-	memimagedraw(scr->gscreen, r, back, back->r.min, nil, back->r.min, S);
-	memimagestring(scr->gscreen, r.min, conscol, ZP, scr->memdefont, s);
-//	flushmemscreen(r);
 }
--- a/sys/src/9/pc/vgavesa.c
+++ b/sys/src/9/pc/vgavesa.c
@@ -62,16 +62,20 @@
 		nexterror();
 	}
 	pa = PADDR(RMBUF);
-	/* TODO: check read and write return values */
-	devtab[cmem->type]->write(cmem, modebuf, sizeof modebuf, pa);
-	u->trap = 0x10;
-	devtab[creg->type]->write(creg, u, sizeof *u, 0);
+	if(devtab[cmem->type]->write(cmem, modebuf, sizeof(modebuf), pa) != sizeof(modebuf))
+		error("write modebuf");
 
-	devtab[creg->type]->read(creg, u, sizeof *u, 0);
+	u->trap = 0x10;
+	if(devtab[creg->type]->write(creg, u, sizeof(*u), 0) != sizeof(*u))
+		error("write ureg");
+	if(devtab[creg->type]->read(creg, u, sizeof(*u), 0) != sizeof(*u))
+		error("read ureg");
 	if((u->ax&0xFFFF) != 0x004F)
 		error("vesa bios error");
-	devtab[cmem->type]->read(cmem, modebuf, sizeof modebuf, pa);
 
+	if(devtab[cmem->type]->read(cmem, modebuf, sizeof(modebuf), pa) != sizeof(modebuf))
+		error("read modebuf");
+
 	poperror();
 	cclose(creg);
 	poperror();
@@ -124,7 +128,7 @@
 	Pcidev *pci;
 
 	if(hardscreen) {
-		scr->vaddr = 0;
+		scr->vaddr = hardscreen;
 		scr->paddr = scr->apsize = 0;
 		return;
 	}
@@ -180,7 +184,6 @@
 
 	if(Usesoftscreen){
 		hardscreen = scr->vaddr;
-		scr->vaddr = 0;
 		scr->paddr = scr->apsize = 0;
 	}
 }
--- a/sys/src/9/port/devdraw.c
+++ b/sys/src/9/port/devdraw.c
@@ -365,9 +365,8 @@
 	int abb, ar, anbb;
 	Rectangle nbb;
 
-	if(sdraw.softscreen==0 || !rectclip(&r, screenimage->r))
+	if(sdraw.softscreen==0 || screenimage == nil || !rectclip(&r, screenimage->r))
 		return;
-
 	if(flushrect.min.x >= flushrect.max.x){
 		flushrect = r;
 		waste = 0;
@@ -413,14 +412,8 @@
 		combinerect(&flushrect, r);
 		return;
 	}
-	/* how can this happen? -rsc, dec 12 2002 */
-	if(dst == 0){
-		print("nil dstflush\n");
+	if(screenimage == nil || dst == nil || (l = dst->layer) == nil)
 		return;
-	}
-	l = dst->layer;
-	if(l == nil)
-		return;
 	do{
 		if(l->screen->image->data != screenimage->data)
 			return;
@@ -433,7 +426,7 @@
 void
 drawflush(void)
 {
-	if(flushrect.min.x < flushrect.max.x)
+	if(screenimage && flushrect.min.x < flushrect.max.x)
 		flushmemscreen(flushrect);
 	flushrect = Rect(10000, 10000, -10000, -10000);
 }
@@ -676,12 +669,10 @@
 		drawfreedimage(dimage->fromname);
 		goto Return;
 	}
-//	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(screenimage && l->data == screenimage->data)
 			addflush(l->layer->screenr);
 		if(l->layer->refreshfn == drawrefresh)	/* else true owner will clean up */
 			free(l->layer->refreshptr);
@@ -929,26 +920,36 @@
 	Memdata *md;
 	Memimage *i;
 	Rectangle r;
+	uchar *data;
 
-	md = malloc(sizeof *md);
-	if(md == nil)
+	if((data = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen)) == nil)
 		return nil;
-	md->allocd = 1;
-	md->base = nil;
-	md->bdata = attachscreen(&r, &chan, &depth, &width, &sdraw.softscreen);
-	if(md->bdata == nil){
-		free(md);
-		return nil;
+	if(sdraw.softscreen == 0xa110c){
+		/* hack: softscreen is memimage. */
+		md = *((Memdata**)(data - sizeof(ulong) - sizeof(Memdata*)));
+
+		assert(md->bdata == data);
+		assert(md->ref > 1);
+		assert(md->allocd);
+
+		if((i = allocmemimaged(r, chan, md)) == nil){
+			md->ref--;
+			return nil;
+		}
+	}else{
+		if((md = malloc(sizeof *md)) == nil)
+			return nil;
+		md->allocd = 1;
+		md->base = nil;
+		md->bdata = data;
+		md->ref = 1;
+		if((i = allocmemimaged(r, chan, md)) == nil){
+			free(md);
+			return nil;
+		}
 	}
-	md->ref = 1;
-	i = allocmemimaged(r, chan, md);
-	if(i == nil){
-		free(md);
-		return nil;
-	}
 	i->width = width;
 	i->clipr = r;
-
 	di = allocdimage(i);
 	if(di == nil){
 		freememimage(i);	/* frees md */
@@ -1859,7 +1860,7 @@
 			if(pp == nil)
 				error(Enomem);
 			doflush = 0;
-			if(dstid==0 || (dst->layer && dst->layer->screen->image->data == screenimage->data))
+			if(dstid==0 || (screenimage && dst->layer && dst->layer->screen->image->data == screenimage->data))
 				doflush = 1;	/* simplify test in loop */
 			ox = oy = 0;
 			esize = 0;
@@ -2048,7 +2049,7 @@
 				memltofrontn(lp, nw);
 			else
 				memltorearn(lp, nw);
-			if(lp[0]->layer->screen->image->data == screenimage->data)
+			if(screenimage && 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);
--- a/sys/src/cmd/aux/vga/main.c
+++ b/sys/src/cmd/aux/vga/main.c
@@ -158,7 +158,7 @@
 {
 	char *bios, buf[256], sizeb[256], *p, *vsize, *psize;
 	char *type, *vtype;
-	int fd, virtual, len;
+	int virtual, len;
 	Ctlr *ctlr;
 	Vga *vga;
 
@@ -454,12 +454,6 @@
 				vgactlw("hwgc", "soft");
 			else
 				vgactlw("hwgc", vga->hwgc->name);
-
-			/* might as well initialize the cursor */
-			if((fd = open("/dev/cursor", OWRITE)) >= 0){
-				write(fd, buf, 0);
-				close(fd);
-			}
 
 			if(vga->virtx != vga->mode->x || vga->virty != vga->mode->y){
 				sprint(buf, "%dx%d", vga->mode->x, vga->mode->y);
--- a/sys/src/cmd/aux/vga/vesa.c
+++ b/sys/src/cmd/aux/vga/vesa.c
@@ -31,8 +31,8 @@
 
 struct Vmode
 {
-	char	name[32];
-	char	chan[32];
+	char	size[Namelen+1];
+	char	chan[Namelen+1];
 	int	id;
 	int	attr;	/* flags */
 	int	bpl;
@@ -89,8 +89,10 @@
 #define PLONG(p, v) (p)[0] = (v); (p)[1] = (v)>>8; (p)[2] = (v)>>16; (p)[3] = (v)>>24
 
 static Vbe *vbe;
-static Edid edid;
+static Edid *edid;
 
+extern Mode *vesamodes[];
+
 Vbe *mkvbe(void);
 int vbecheck(Vbe*);
 uchar *vbemodes(Vbe*);
@@ -116,6 +118,7 @@
 		fprint(2, "dbvesa: %r\n");
 		return 0;
 	}
+
 	vga->link = alloc(sizeof(Ctlr));
 	*vga->link = vesa;
 	vga->vesa = vga->link;
@@ -129,33 +132,39 @@
 }
 
 Mode*
-dbvesamode(char *mode)
+dbvesamode(char *size)
 {
-	int i;
+	int i, width;
 	uchar *p, *ep;
+	Attr *a;
 	Vmode vm;
 	Mode *m;
+	Modelist *l;
 	
 	if(vbe == nil)
 		return nil;
 
-	p = vbemodes(vbe);
-	if(p == nil)
-		return nil;
-	for(ep=p+1024; (p[0]!=0xFF || p[1]!=0xFF) && p<ep; p+=2){
-		if(vbemodeinfo(vbe, WORD(p), &vm) < 0)
-			continue;
-		if(strcmp(vm.name, mode) == 0)
+	if(strncmp(size, "0x", 2) == 0){
+		if(vbemodeinfo(vbe, strtol(size+2, nil, 16), &vm) == 0)
 			goto havemode;
-	}
-	if(1){
-		fprint(2, "warning: scanning for unoffered vesa modes\n");
-		for(i=0x100; i<0x200; i++){
-			if(vbemodeinfo(vbe, i, &vm) < 0)
-				continue;
-			if(strcmp(vm.name, mode) == 0)
-				goto havemode;
+	}else{
+		if(p = vbemodes(vbe)){
+			for(ep=p+1024; (p[0]!=0xFF || p[1]!=0xFF) && p<ep; p+=2){
+				if(vbemodeinfo(vbe, WORD(p), &vm) < 0)
+					continue;
+				if(strcmp(vm.size, size) == 0)
+					goto havemode;
+			}
 		}
+		if(1){
+			fprint(2, "warning: scanning for unoffered vesa modes\n");
+			for(i=0x100; i<0x200; i++){
+				if(vbemodeinfo(vbe, i, &vm) < 0)
+					continue;
+				if(strcmp(vm.size, size) == 0)
+					goto havemode;
+			}
+		}
 	}
 	werrstr("no such vesa mode");
 	return nil;
@@ -162,13 +171,8 @@
 
 havemode:
 	m = alloc(sizeof(Mode));
-	strcpy(m->type, "vesa");
-	strcpy(m->size, vm.name);
-	strcpy(m->chan, vm.chan);
-	m->frequency = 100;
 	m->x = vm.dx;
 	m->y = vm.dy;
-	m->z = vm.depth;
 	m->ht = m->x;
 	m->shb = m->x;
 	m->ehb = m->x;
@@ -177,11 +181,49 @@
 	m->vt = m->y;
 	m->vrs = m->y;
 	m->vre = m->y;
+	m->frequency = m->ht * m->vt * 60;
 
-	m->attr = alloc(sizeof(Attr));
-	m->attr->attr = "id";
-	m->attr->val = alloc(32);
-	sprint(m->attr->val, "0x%x", vm.id);
+	/* get default monitor timing */
+	for(i=0; vesamodes[i]; i++){
+		if(vesamodes[i]->x != vm.dx || vesamodes[i]->y != vm.dy)
+			continue;
+		*m = *vesamodes[i];
+		break;
+	}
+	if(edid){
+		for(l = edid->modelist; l; l = l->next){
+			if(l->x != vm.dx || l->y != vm.dy)
+				continue;
+			*m = *((Mode*)l);
+			break;
+		}
+	}
+
+	strcpy(m->type, "vesa");
+	strcpy(m->size, vm.size);
+	strcpy(m->chan, vm.chan);
+	m->z = vm.depth;
+
+	a = alloc(sizeof(Attr));
+	a->attr = "id";
+	a->val = alloc(32);
+	sprint(a->val, "0x%x", vm.id);
+
+	a->next = nil;
+	m->attr = a;
+
+	/* account for framebuffer stride */
+	width = vm.bpl * 8 / m->z;
+	if(width > m->x){
+		a = alloc(sizeof(Attr));
+		a->attr = "virtx";
+		a->val = alloc(32);
+		sprint(a->val, "%d", width);
+
+		a->next = m->attr;
+		m->attr = a;
+	}
+
 	return m;
 }
 
@@ -194,10 +236,23 @@
 		vga->vesa = ctlr;
 	vbesnarf(vbe, vga);
 	vga->linear = 1;
-	ctlr->flag |= Hlinear|Ulinear;
+	ctlr->flag |= Hlinear|Ulinear|Fsnarf;
 }
 
 static void
+options(Vga *vga, Ctlr *ctlr)
+{
+	char *v;
+
+	if(v = dbattr(vga->mode->attr, "virtx")){
+		vga->virtx = atoi(v);
+		vga->virty = vga->mode->y;
+		vga->panning = 0;
+	}
+	ctlr->flag |= Foptions;
+}
+
+static void
 load(Vga* vga, Ctlr* ctlr)
 {
 	if(vbe == nil)
@@ -233,18 +288,14 @@
 	for(i=0x100; i<0x1FF; i++)
 		if(!did[i])
 			vbeprintmodeinfo(vbe, i, " (unoffered)");
-				
-	
-	if(vbeddcedid(vbe, &edid) < 0)
-		fprint(2, "warning: reading edid: %r\n");
-	else
-		printedid(&edid);
+	if(edid)
+		printedid(edid);
 }
 
 Ctlr vesa = {
 	"vesa",			/* name */
 	snarf,				/* snarf */
-	0,			/* options */
+	options,			/* options */
 	0,				/* init */
 	load,				/* load */
 	dump,				/* dump */
@@ -273,19 +324,34 @@
 	0
 };
 
+enum {
+	AttrSupported	= 1<<0,
+	AttrTTY		= 1<<2,
+	AttrColor	= 1<<3,
+	AttrGraphics	= 1<<4,
+	AttrNotVGA	= 1<<5,
+	AttrNotWinVGA	= 1<<6,
+	AttrLinear	= 1<<7,
+	AttrDoublescan	= 1<<8,
+	AttrInterlace	= 1<<9,
+	AttrTriplebuf	= 1<<10,
+	AttrStereo	= 1<<11,
+	AttrDualAddr	= 1<<12,
+};
+
 static Flag modeattributesflags[] = {
-	1<<0, "supported",
-	1<<2, "tty",
-	1<<3, "color",
-	1<<4, "graphics",
-	1<<5, "not-vga",
-	1<<6, "no-windowed-vga",
-	1<<7, "linear",
-	1<<8, "double-scan",
-	1<<9, "interlace",
-	1<<10, "triple-buffer",
-	1<<11, "stereoscopic",
-	1<<12, "dual-start-addr",
+	AttrSupported,	"supported",
+	AttrTTY,	"tty",
+	AttrColor,	"color",
+	AttrGraphics,	"graphics",
+	AttrNotVGA,	"not-vga",
+	AttrNotWinVGA,	"no-windowed-vga",
+	AttrLinear,	"linear",
+	AttrDoublescan,	"double-scan",
+	AttrInterlace,	"interlace",
+	AttrTriplebuf,	"triple-buffer",
+	AttrStereo,	"stereoscopic",
+	AttrDualAddr,	"dual-start-addr",
 	0
 };
 
@@ -302,10 +368,29 @@
 	0
 };
 
+enum {
+	ModText = 0,
+	ModCGA,
+	ModHercules,
+	ModPlanar,
+	ModPacked,
+	ModNonChain4,
+	ModDirect,
+	ModYUV,
+};
+
 static char *modelstr[] = {
-	"text", "cga", "hercules", "planar", "packed", "non-chain4", "direct", "YUV"
+	[ModText]	"text",
+	[ModCGA]	"cga",
+	[ModHercules]	"hercules",
+	[ModPlanar]	"planar",
+	[ModPacked]	"packed",
+	[ModNonChain4]	"non-chain4",
+	[ModDirect]	"direct",
+	[ModYUV]	"YUV",
 };
 
+
 static void
 printflags(Flag *f, int b)
 {
@@ -402,10 +487,14 @@
 	strcpy((char*)p, "VBE2");
 	if(vbecall(vbe, &u) < 0)
 		return -1;
-	if(memcmp(p, "VESA", 4) != 0 || p[5] < 2){
-		werrstr("invalid vesa signature %.4H %.4H\n", p, p+4);
+	if(memcmp(p, "VESA", 4)){
+		werrstr("invalid vesa signature %.4H\n", p);
 		return -1;
 	}
+	if(p[5] < 2){
+		werrstr("invalid vesa version: %.4H\n", p+4);
+		return -1;
+	}
 	return 0;
 }
 
@@ -422,6 +511,13 @@
 	if(memcmp(p, "VESA", 4) != 0 || p[5] < 2)
 		return -1;
 	vga->apz = WORD(p+18)*0x10000UL;
+	if(edid == nil){
+		edid = alloc(sizeof(Edid));
+		if(vbeddcedid(vbe, edid) < 0){
+			free(edid);
+			edid = nil;
+		}
+	}
 	return 0;
 }
 
@@ -474,11 +570,9 @@
 int
 vbemodeinfo(Vbe *vbe, int id, Vmode *m)
 {
-	int o;
-	ulong d, c, x;
 	uchar *p;
-	char tmp[sizeof m->chan];
 	Ureg u;
+	int mod;
 
 	p = vbesetup(vbe, &u, 0x4F01);
 	u.cx = id;
@@ -487,11 +581,31 @@
 
 	m->id = id;
 	m->attr = WORD(p);
+	if(!(m->attr & AttrSupported))
+		goto Unsupported;
+	if(!(m->attr & AttrGraphics))
+		goto Unsupported;
+	if(!(m->attr & AttrLinear))
+		goto Unsupported;
 	m->bpl = WORD(p+16);
 	m->dx = WORD(p+18);
 	m->dy = WORD(p+20);
 	m->depth = p[25];
-	m->model = p[27] < nelem(modelstr) ? modelstr[p[27]] : "unknown";
+	if((m->dx * m->dy * m->depth) <= 0)
+		goto Unsupported;
+	mod = p[27];
+	switch(mod){
+	default:
+	Unsupported:
+		werrstr("mode unsupported");
+		return -1;
+	case ModCGA:
+	case ModHercules:
+	case ModPacked:
+	case ModDirect:
+		m->model = modelstr[mod];
+		break;
+	}
 	m->r = p[31];
 	m->g = p[33];
 	m->b = p[35];
@@ -502,44 +616,49 @@
 	m->xo = p[38];
 	m->directcolor = p[39];
 	m->paddr = LONG(p+40);
-	snprint(m->name, sizeof m->name, "%dx%dx%d",
-		m->dx, m->dy, m->depth);
-	if(m->depth <= 8) {
-		snprint(m->chan, sizeof m->chan, "m%d", m->depth);
-		return 0;
-	}
 
-	m->xo = m->x = 0;
-	d = 1 << (m->depth - 1);
-	d |= d - 1;
-	c  = ((1<<m->r)-1) << m->ro;
-	c |= ((1<<m->g)-1) << m->go;
-	c |= ((1<<m->b)-1) << m->bo;
-	x = d ^ c;
-	if(x != 0){
-		for(; (x & 1) == 0; x >>= 1)
-			m->xo++;
-		for(; x & 1; x >>= 1)
-			m->x++;
-	}
+	snprint(m->size, sizeof m->size, "%dx%dx%d", m->dx, m->dy, m->depth);
+	if(m->depth <= 8)
+		snprint(m->chan, sizeof m->chan, "%c%d", 
+			(m->attr & AttrColor) ? 'm' : 'k', m->depth);
+	else {
+		int o;
+		ulong d, c, x;
 
-	m->chan[0] = o = 0;
-	while(o < m->depth){
-		if(m->r && m->ro == o){
-			snprint(tmp, sizeof tmp, "r%d%s", m->r, m->chan);
-			o += m->r;
-		}else if(m->g && m->go == o){
-			snprint(tmp, sizeof tmp, "g%d%s", m->g, m->chan);
-			o += m->g;
-		}else if(m->b && m->bo == o){
-			snprint(tmp, sizeof tmp, "b%d%s", m->b, m->chan);
-			o += m->b;
-		}else if(m->x && m->xo == o){
-			snprint(tmp, sizeof tmp, "x%d%s", m->x, m->chan);
-			o += m->x;
-		}else
-			break;
-		strncpy(m->chan, tmp, sizeof m->chan);
+		m->xo = m->x = 0;
+		d = 1<<m->depth-1;
+		d |= d-1;
+		c = ((1<<m->r)-1) << m->ro;
+		c |= ((1<<m->g)-1) << m->go;
+		c |= ((1<<m->b)-1) << m->bo;
+		if(x = d ^ c){
+			for(; (x & 1) == 0; x >>= 1)
+				m->xo++;
+			for(; (x & 1) == 1; x >>= 1)
+				m->x++;
+		}
+
+		o = 0;
+		m->chan[0] = 0;
+		while(o < m->depth){
+			char tmp[sizeof m->chan];
+
+			if(m->r && m->ro == o){
+				snprint(tmp, sizeof tmp, "r%d%s", m->r, m->chan);
+				o += m->r;
+			}else if(m->g && m->go == o){
+				snprint(tmp, sizeof tmp, "g%d%s", m->g, m->chan);
+				o += m->g;
+			}else if(m->b && m->bo == o){
+				snprint(tmp, sizeof tmp, "b%d%s", m->b, m->chan);
+				o += m->b;
+			}else if(m->x && m->xo == o){
+				snprint(tmp, sizeof tmp, "x%d%s", m->x, m->chan);
+				o += m->x;
+			}else
+				break;
+			strncpy(m->chan, tmp, sizeof m->chan);
+		}
 	}
 	return 0;
 }
@@ -549,13 +668,11 @@
 {
 	Vmode m;
 
-	if(vbemodeinfo(vbe, id, &m) < 0){
-	//	Bprint(&stdout, "vesa: cannot get mode 0x%ux: %r\n", id);
+	if(vbemodeinfo(vbe, id, &m) < 0)
 		return;
-	}
 	printitem("vesa", "mode");
 	Bprint(&stdout, "0x%ux %s %s %s%s\n",
-		m.id, m.name, m.chan, m.model, suffix);
+		m.id, m.size, m.chan, m.model, suffix);
 }
 
 int
@@ -572,33 +689,12 @@
 int
 vbesetmode(Vbe *vbe, int id)
 {
-	uchar *p;
 	Ureg u;
 
-	p = vbesetup(vbe, &u, 0x4F02);
-	if(id != 3)
-		id |= 3<<14;	/* graphics: use linear, do not clear */
+	vbesetup(vbe, &u, 0x4F02);
 	u.bx = id;
-	USED(p);
-	/*
-	 * can set mode specifics (ht hss hse vt vss vse 0 clockhz refreshhz):
-	 *
-		u.bx |= 1<<11;
-		n = atoi(argv[2]); PWORD(p, n); p+=2;
-		n = atoi(argv[3]); PWORD(p, n); p+=2;
-		n = atoi(argv[4]); PWORD(p, n); p+=2;
-		n = atoi(argv[5]); PWORD(p, n); p+=2;
-		n = atoi(argv[6]); PWORD(p, n); p+=2;
-		n = atoi(argv[7]); PWORD(p, n); p+=2;
-		*p++ = atoi(argv[8]);
-		n = atoi(argv[9]); PLONG(p, n); p += 4;
-		n = atoi(argv[10]); PWORD(p, n); p += 2;
-		if(p != vbe.buf+19){
-			fprint(2, "prog error\n");
-			return;
-		}
-	 *
-	 */
+	if(id != 3)
+		u.bx |= 3<<14;	/* graphics: use linear, do not clear */
 	return vbecall(vbe, &u);
 }
 
@@ -679,8 +775,16 @@
 	
 	for(l=e->modelist; l; l=l->next){
 		printitem("edid", l->name);
-		Bprint(&stdout, "\n\t\tclock=%g\n\t\tshb=%d ehb=%d ht=%d\n\t\tvrs=%d vre=%d vt=%d\n\t\thsync=%c vsync=%c %s\n",
-			l->frequency/1.e6, l->shb, l->ehb, l->ht, l->vrs, l->vre, l->vt, l->hsync?l->hsync:'?', l->vsync?l->vsync:'?', l->interlace?"interlace=v" : "");
+		Bprint(&stdout, "\n\t\tclock=%g\n"
+			"\t\tshb=%d ehb=%d ht=%d\n"
+			"\t\tvrs=%d vre=%d vt=%d\n"
+			"\t\thsync=%c vsync=%c %s\n",
+			l->frequency/1.e6, 
+			l->shb, l->ehb, l->ht,
+			l->vrs, l->vre, l->vt,
+			l->hsync?l->hsync:'?',
+			l->vsync?l->vsync:'?',
+			l->interlace?"interlace=v" : "");
 	}
 }
 
@@ -690,9 +794,8 @@
 	int rr;
 	Modelist **lp;
 
-//m.z = 8; // BUG
 	rr = (m.frequency+m.ht*m.vt/2)/(m.ht*m.vt);
-	snprint(m.name, sizeof m.name, "%dx%dx%d@%dHz", m.x, m.y, m.z, rr);
+	snprint(m.name, sizeof m.name, "%dx%d@%dHz", m.x, m.y, rr);
 
 	if(m.shs == 0)
 		m.shs = m.shb;
@@ -831,8 +934,6 @@
 
 	return 0;
 }
-
-extern Mode *vesamodes[];
 
 int
 vesalookup(Mode *m, char *name)
--- a/sys/src/cmd/aux/vga/vesadb.c
+++ b/sys/src/cmd/aux/vga/vesadb.c
@@ -1,4 +1,4 @@
-/* this file was automatically generated from vesa.txt */
+// DO NOT EDIT; this file is automatically generated from vesa.txt
 
 #include <u.h>
 #include <libc.h>
@@ -6,16 +6,14 @@
 #include "pci.h"
 #include "vga.h"
 
-/*
- * VESA Monitor Timing Standard mode definitions as per
- * VESA and Industry Standards and Guidelines for Computer
- * Display Monitor Timing, Version 1.0, Revision 0.8, 17 September 1998.
- *
- * See /public/doc/vesa/dmtv1r08.pdf.
- *
- * This might go back into vgadb at some point. It's here mainly
- * so that people don't change it, and so that we can run without vgadb.
- */
+// VESA Monitor Timing Standard mode definitions as per
+// VESA and Industry Standards and Guidelines for Computer
+// Display Monitor Timing, Version 1.0, Revision 0.8, 17 September 1998.
+//
+// See /lib/vesa/dmtv1r08.pdf.
+//
+// This might go back into vgadb at some point. It's here mainly
+// so that people don't change it, and so that we can run without vgadb.
 
 static Mode vesa640x480x60 = {
 	.name = "640x480@60Hz",
--- a/sys/src/libmemdraw/draw.c
+++ b/sys/src/libmemdraw/draw.c
@@ -12,23 +12,35 @@
 #define RGB2K(r,g,b)	((156763*(r)+307758*(g)+59769*(b))>>19)
 
 /*
- * for 0 ≤ x ≤ 255*255, (x*0x0101+0x100)>>16 is a perfect approximation.
- * for 0 ≤ x < (1<<16), x/255 = ((x+1)*0x0101)>>16 is a perfect approximation.
- * the last one is perfect for all up to 1<<16, avoids a multiply, but requires a rathole.
+ * For 16-bit values, x / 255 == (t = x+1, (t+(t>>8)) >> 8).
+ * We add another 127 to round to the nearest value rather
+ * than truncate.
+ *
+ * CALCxy does x bytewise calculations on y input images (x=1,4; y=1,2).
+ * CALC2x does two parallel 16-bit calculations on y input images (y=1,2).
  */
-/* #define DIV255(x) (((x)*257+256)>>16)  */
-#define DIV255(x) ((((x)+1)*257)>>16)
-/* #define DIV255(x) (tmp=(x)+1, (tmp+(tmp>>8))>>8) */
+#define CALC11(a, v, tmp) \
+	(tmp=(a)*(v)+128, (tmp+(tmp>>8))>>8)
 
-#define MUL(x, y, t)	(t = (x)*(y)+128, (t+(t>>8))>>8)
-#define MASK13	0xFF00FF00
-#define MASK02	0x00FF00FF
-#define MUL13(a, x, t)		(t = (a)*(((x)&MASK13)>>8)+128, ((t+((t>>8)&MASK02))>>8)&MASK02)
-#define MUL02(a, x, t)		(t = (a)*(((x)&MASK02)>>0)+128, ((t+((t>>8)&MASK02))>>8)&MASK02)
-#define MUL0123(a, x, s, t)	((MUL13(a, x, s)<<8)|MUL02(a, x, t))
+#define CALC12(a1, v1, a2, v2, tmp) \
+	(tmp=(a1)*(v1)+(a2)*(v2)+128, (tmp+(tmp>>8))>>8)
 
-#define MUL2(u, v, x, y)	(t = (u)*(v)+(x)*(y)+256, (t+(t>>8))>>8)
+#define MASK 0xFF00FF
 
+#define CALC21(a, vvuu, tmp) \
+	(tmp=(a)*(vvuu)+0x00800080, ((tmp+((tmp>>8)&MASK))>>8)&MASK)
+
+#define CALC41(a, rgba, tmp1, tmp2) \
+	(CALC21(a, rgba & MASK, tmp1) | \
+	 (CALC21(a, (rgba>>8)&MASK, tmp2)<<8))
+
+#define CALC22(a1, vvuu1, a2, vvuu2, tmp) \
+	(tmp=(a1)*(vvuu1)+(a2)*(vvuu2)+0x00800080, ((tmp+((tmp>>8)&MASK))>>8)&MASK)
+
+#define CALC42(a1, rgba1, a2, rgba2, tmp1, tmp2) \
+	(CALC22(a1, rgba1 & MASK, a2, rgba2 & MASK, tmp1) | \
+	 (CALC22(a1, (rgba1>>8) & MASK, a2, (rgba2>>8) & MASK, tmp2)<<8))
+
 static void mktables(void);
 typedef int Subdraw(Memdrawparam*);
 static Subdraw chardraw, alphadraw, memoptdraw;
@@ -803,6 +815,50 @@
 	return bdst;
 }
 
+/*
+ * Do the channels in the buffers match enough
+ * that we can do word-at-a-time operations
+ * on the pixels?
+ */
+static int
+chanmatch(Buffer *bdst, Buffer *bsrc)
+{
+	uchar *drgb, *srgb;
+	
+	/*
+	 * first, r, g, b must be in the same place
+	 * in the rgba word.
+	 */
+	drgb = (uchar*)bdst->rgba;
+	srgb = (uchar*)bsrc->rgba;
+	if(bdst->red - drgb != bsrc->red - srgb
+	|| bdst->blu - drgb != bsrc->blu - srgb
+	|| bdst->grn - drgb != bsrc->grn - srgb)
+		return 0;
+	
+	/*
+	 * that implies alpha is in the same place,
+	 * if it is there at all (it might be == &ones).
+	 * if the destination is &ones, we can scribble
+	 * over the rgba slot just fine.
+	 */
+	if(bdst->alpha == &ones)
+		return 1;
+	
+	/*
+	 * if the destination is not ones but the src is,
+	 * then the simultaneous calculation will use
+	 * bogus bytes from the src's rgba.  no good.
+	 */
+	if(bsrc->alpha == &ones)
+		return 0;
+	
+	/*
+	 * otherwise, alphas are in the same place.
+	 */
+	return 1;
+}
+
 static Buffer
 alphacalc14(Buffer bdst, Buffer bsrc, Buffer bmask, int dx, int grey, int op)
 {
@@ -809,26 +865,26 @@
 	Buffer obdst;
 	int fd, sadelta;
 	int i, sa, ma, q;
-	ulong s, t;
+	ulong t, t1;
 
 	obdst = bdst;
 	sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta;
-	q = bsrc.delta == 4 && bdst.delta == 4;
+	q = bsrc.delta == 4 && bdst.delta == 4 && chanmatch(&bdst, &bsrc);
 
 	for(i=0; i<dx; i++){
 		sa = *bsrc.alpha;
 		ma = *bmask.alpha;
-		fd = MUL(sa, ma, t);
+		fd = CALC11(sa, ma, t);
 		if(op == DoutS)
 			fd = 255-fd;
 
 		if(grey){
-			*bdst.grey = MUL(fd, *bdst.grey, t);
+			*bdst.grey = CALC11(fd, *bdst.grey, t);
 			bsrc.grey += bsrc.delta;
 			bdst.grey += bdst.delta;
 		}else{
 			if(q){
-				*bdst.rgba = MUL0123(fd, *bdst.rgba, s, t);
+				*bdst.rgba = CALC41(fd, *bdst.rgba, t, t1);
 				bsrc.rgba++;
 				bdst.rgba++;
 				bsrc.alpha += sadelta;
@@ -835,9 +891,9 @@
 				bmask.alpha += bmask.delta;
 				continue;
 			}
-			*bdst.red = MUL(fd, *bdst.red, t);
-			*bdst.grn = MUL(fd, *bdst.grn, t);
-			*bdst.blu = MUL(fd, *bdst.blu, t);
+			*bdst.red = CALC11(fd, *bdst.red, t);
+			*bdst.grn = CALC11(fd, *bdst.grn, t);
+			*bdst.blu = CALC11(fd, *bdst.blu, t);
 			bsrc.red += bsrc.delta;
 			bsrc.blu += bsrc.delta;
 			bsrc.grn += bsrc.delta;
@@ -846,7 +902,7 @@
 			bdst.grn += bdst.delta;
 		}
 		if(bdst.alpha != &ones){
-			*bdst.alpha = MUL(fd, *bdst.alpha, t);
+			*bdst.alpha = CALC11(fd, *bdst.alpha, t);
 			bdst.alpha += bdst.delta;
 		}
 		bmask.alpha += bmask.delta;
@@ -861,11 +917,11 @@
 	Buffer obdst;
 	int fs, sadelta;
 	int i, ma, da, q;
-	ulong s, t;
+	ulong t, t1;
 
 	obdst = bdst;
 	sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta;
-	q = bsrc.delta == 4 && bdst.delta == 4;
+	q = bsrc.delta == 4 && bdst.delta == 4 && chanmatch(&bdst, &bsrc);
 
 	for(i=0; i<dx; i++){
 		ma = *bmask.alpha;
@@ -874,15 +930,15 @@
 			da = 255-da;
 		fs = ma;
 		if(op != S)
-			fs = MUL(fs, da, t);
+			fs = CALC11(fs, da, t);
 
 		if(grey){
-			*bdst.grey = MUL(fs, *bsrc.grey, t);
+			*bdst.grey = CALC11(fs, *bsrc.grey, t);
 			bsrc.grey += bsrc.delta;
 			bdst.grey += bdst.delta;
 		}else{
 			if(q){
-				*bdst.rgba = MUL0123(fs, *bsrc.rgba, s, t);
+				*bdst.rgba = CALC41(fs, *bsrc.rgba, t, t1);
 				bsrc.rgba++;
 				bdst.rgba++;
 				bmask.alpha += bmask.delta;
@@ -889,9 +945,9 @@
 				bdst.alpha += bdst.delta;
 				continue;
 			}
-			*bdst.red = MUL(fs, *bsrc.red, t);
-			*bdst.grn = MUL(fs, *bsrc.grn, t);
-			*bdst.blu = MUL(fs, *bsrc.blu, t);
+			*bdst.red = CALC11(fs, *bsrc.red, t);
+			*bdst.grn = CALC11(fs, *bsrc.grn, t);
+			*bdst.blu = CALC11(fs, *bsrc.blu, t);
 			bsrc.red += bsrc.delta;
 			bsrc.blu += bsrc.delta;
 			bsrc.grn += bsrc.delta;
@@ -900,7 +956,7 @@
 			bdst.grn += bdst.delta;
 		}
 		if(bdst.alpha != &ones){
-			*bdst.alpha = MUL(fs, *bsrc.alpha, t);
+			*bdst.alpha = CALC11(fs, *bsrc.alpha, t);
 			bdst.alpha += bdst.delta;
 		}
 		bmask.alpha += bmask.delta;
@@ -915,11 +971,11 @@
 	Buffer obdst;
 	int fs, fd, sadelta;
 	int i, sa, ma, da, q;
-	ulong s, t, u, v;
+	ulong t, t1;
 
 	obdst = bdst;
 	sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta;
-	q = bsrc.delta == 4 && bdst.delta == 4;
+	q = bsrc.delta == 4 && bdst.delta == 4 && chanmatch(&bdst, &bsrc);
 
 	for(i=0; i<dx; i++){
 		sa = *bsrc.alpha;
@@ -926,24 +982,24 @@
 		ma = *bmask.alpha;
 		da = *bdst.alpha;
 		if(op == SatopD)
-			fs = MUL(ma, da, t);
+			fs = CALC11(ma, da, t);
 		else
-			fs = MUL(ma, 255-da, t);
+			fs = CALC11(ma, 255-da, t);
 		if(op == DoverS)
 			fd = 255;
 		else{
-			fd = MUL(sa, ma, t);
+			fd = CALC11(sa, ma, t);
 			if(op != DatopS)
 				fd = 255-fd;
 		}
 
 		if(grey){
-			*bdst.grey = MUL(fs, *bsrc.grey, s)+MUL(fd, *bdst.grey, t);
+			*bdst.grey = CALC12(fs, *bsrc.grey, fd, *bdst.grey, t);
 			bsrc.grey += bsrc.delta;
 			bdst.grey += bdst.delta;
 		}else{
 			if(q){
-				*bdst.rgba = MUL0123(fs, *bsrc.rgba, s, t)+MUL0123(fd, *bdst.rgba, u, v);
+				*bdst.rgba = CALC42(fs, *bsrc.rgba, fd, *bdst.rgba, t, t1);
 				bsrc.rgba++;
 				bdst.rgba++;
 				bsrc.alpha += sadelta;
@@ -951,9 +1007,9 @@
 				bdst.alpha += bdst.delta;
 				continue;
 			}
-			*bdst.red = MUL(fs, *bsrc.red, s)+MUL(fd, *bdst.red, t);
-			*bdst.grn = MUL(fs, *bsrc.grn, s)+MUL(fd, *bdst.grn, t);
-			*bdst.blu = MUL(fs, *bsrc.blu, s)+MUL(fd, *bdst.blu, t);
+			*bdst.red = CALC12(fs, *bsrc.red, fd, *bdst.red, t);
+			*bdst.grn = CALC12(fs, *bsrc.grn, fd, *bdst.grn, t);
+			*bdst.blu = CALC12(fs, *bsrc.blu, fd, *bdst.blu, t);
 			bsrc.red += bsrc.delta;
 			bsrc.blu += bsrc.delta;
 			bsrc.grn += bsrc.delta;
@@ -962,7 +1018,7 @@
 			bdst.grn += bdst.delta;
 		}
 		if(bdst.alpha != &ones){
-			*bdst.alpha = MUL(fs, sa, s)+MUL(fd, da, t);
+			*bdst.alpha = CALC12(fs, sa, fd, da, t);
 			bdst.alpha += bdst.delta;
 		}
 		bmask.alpha += bmask.delta;
@@ -988,25 +1044,25 @@
 	Buffer obdst;
 	int fd, sadelta;
 	int i, sa, ma, q;
-	ulong s, t, u, v;
+	ulong t, t1;
 
 	USED(op);
 	obdst = bdst;
 	sadelta = bsrc.alpha == &ones ? 0 : bsrc.delta;
-	q = bsrc.delta == 4 && bdst.delta == 4;
+	q = bsrc.delta == 4 && bdst.delta == 4 && chanmatch(&bdst, &bsrc);
 
 	for(i=0; i<dx; i++){
 		sa = *bsrc.alpha;
 		ma = *bmask.alpha;
-		fd = 255-MUL(sa, ma, t);
+		fd = 255-CALC11(sa, ma, t);
 
 		if(grey){
-			*bdst.grey = MUL(ma, *bsrc.grey, s)+MUL(fd, *bdst.grey, t);
+			*bdst.grey = CALC12(ma, *bsrc.grey, fd, *bdst.grey, t);
 			bsrc.grey += bsrc.delta;
 			bdst.grey += bdst.delta;
 		}else{
 			if(q){
-				*bdst.rgba = MUL0123(ma, *bsrc.rgba, s, t)+MUL0123(fd, *bdst.rgba, u, v);
+				*bdst.rgba = CALC42(ma, *bsrc.rgba, fd, *bdst.rgba, t, t1);
 				bsrc.rgba++;
 				bdst.rgba++;
 				bsrc.alpha += sadelta;
@@ -1013,9 +1069,9 @@
 				bmask.alpha += bmask.delta;
 				continue;
 			}
-			*bdst.red = MUL(ma, *bsrc.red, s)+MUL(fd, *bdst.red, t);
-			*bdst.grn = MUL(ma, *bsrc.grn, s)+MUL(fd, *bdst.grn, t);
-			*bdst.blu = MUL(ma, *bsrc.blu, s)+MUL(fd, *bdst.blu, t);
+			*bdst.red = CALC12(ma, *bsrc.red, fd, *bdst.red, t);
+			*bdst.grn = CALC12(ma, *bsrc.grn, fd, *bdst.grn, t);
+			*bdst.blu = CALC12(ma, *bsrc.blu, fd, *bdst.blu, t);
 			bsrc.red += bsrc.delta;
 			bsrc.blu += bsrc.delta;
 			bsrc.grn += bsrc.delta;
@@ -1024,7 +1080,7 @@
 			bdst.grn += bdst.delta;
 		}
 		if(bdst.alpha != &ones){
-			*bdst.alpha = MUL(ma, sa, s)+MUL(fd, *bdst.alpha, t);
+			*bdst.alpha = CALC12(ma, sa, fd, *bdst.alpha, t);
 			bdst.alpha += bdst.delta;
 		}
 		bmask.alpha += bmask.delta;
@@ -1080,7 +1136,7 @@
 	Buffer obdst;
 	int fd;
 	int i, ma;
-	ulong s, t;
+	ulong t;
 
 	USED(op);
 	obdst = bdst;
@@ -1090,13 +1146,13 @@
 		fd = 255-ma;
 
 		if(grey){
-			*bdst.grey = MUL(ma, *bsrc.grey, s)+MUL(fd, *bdst.grey, t);
+			*bdst.grey = CALC12(ma, *bsrc.grey, fd, *bdst.grey, t);
 			bsrc.grey += bsrc.delta;
 			bdst.grey += bdst.delta;
 		}else{
-			*bdst.red = MUL(ma, *bsrc.red, s)+MUL(fd, *bdst.red, t);
-			*bdst.grn = MUL(ma, *bsrc.grn, s)+MUL(fd, *bdst.grn, t);
-			*bdst.blu = MUL(ma, *bsrc.blu, s)+MUL(fd, *bdst.blu, t);
+			*bdst.red = CALC12(ma, *bsrc.red, fd, *bdst.red, t);
+			*bdst.grn = CALC12(ma, *bsrc.grn, fd, *bdst.grn, t);
+			*bdst.blu = CALC12(ma, *bsrc.blu, fd, *bdst.blu, t);
 			bsrc.red += bsrc.delta;
 			bsrc.blu += bsrc.delta;
 			bsrc.grn += bsrc.delta;
@@ -1105,7 +1161,7 @@
 			bdst.grn += bdst.delta;
 		}
 		if(bdst.alpha != &ones){
-			*bdst.alpha = ma+MUL(fd, *bdst.alpha, t);
+			*bdst.alpha = ma+CALC11(fd, *bdst.alpha, t);
 			bdst.alpha += bdst.delta;
 		}
 		bmask.alpha += bmask.delta;
@@ -1154,7 +1210,7 @@
 	Buffer obdst;
 	int fs, fd;
 	int i, ma, da, zero;
-	ulong s, t;
+	ulong t;
 
 	obdst = bdst;
 	zero = !(op&1);
@@ -1171,7 +1227,7 @@
 
 		if(grey){
 			if(ma)
-				*bdst.grey = MUL(fs, *bsrc.grey, s)+MUL(fd, *bdst.grey, t);
+				*bdst.grey = CALC12(fs, *bsrc.grey, fd, *bdst.grey, t);
 			else if(zero)
 				*bdst.grey = 0;
 			bsrc.grey += bsrc.delta;
@@ -1178,9 +1234,9 @@
 			bdst.grey += bdst.delta;
 		}else{
 			if(ma){
-				*bdst.red = MUL(fs, *bsrc.red, s)+MUL(fd, *bdst.red, t);
-				*bdst.grn = MUL(fs, *bsrc.grn, s)+MUL(fd, *bdst.grn, t);
-				*bdst.blu = MUL(fs, *bsrc.blu, s)+MUL(fd, *bdst.blu, t);
+				*bdst.red = CALC12(fs, *bsrc.red, fd, *bdst.red, t);
+				*bdst.grn = CALC12(fs, *bsrc.grn, fd, *bdst.grn, t);
+				*bdst.blu = CALC12(fs, *bsrc.blu, fd, *bdst.blu, t);
 			}
 			else if(zero)
 				*bdst.red = *bdst.grn = *bdst.blu = 0;
@@ -1194,7 +1250,7 @@
 		bmask.alpha += bmask.delta;
 		if(bdst.alpha != &ones){
 			if(ma)
-				*bdst.alpha = fs+MUL(fd, da, t);
+				*bdst.alpha = fs+CALC11(fd, da, t);
 			else if(zero)
 				*bdst.alpha = 0;
 			bdst.alpha += bdst.delta;
--- a/sys/src/libmemdraw/load.c
+++ b/sys/src/libmemdraw/load.c
@@ -7,10 +7,17 @@
 loadmemimage(Memimage *i, Rectangle r, uchar *data, int ndata)
 {
 	int y, l, lpart, rpart, mx, m, mr;
+	Memdrawparam par;
 	uchar *q;
 
 	if(!rectinrect(r, i->r))
 		return -1;
+
+	memset(&par, 0, sizeof par);
+	par.dst = i;
+	par.r = r;
+	hwdraw(&par);
+
 	l = bytesperline(r, i->depth);
 	if(ndata < l*Dy(r))
 		return -1;
--