~kris/9p

9hist

c1a1e323a49bfd6cdec33bfb90a0433962f785f1 — David du Colombier 28 years ago 0d1a353
Plan 9 from Bell Labs 1998-01-09
4 files changed, 382 insertions(+), 108 deletions(-)

M carrera/screen.c
M pc/devastar.c
M pc/screen.c
M port/devdraw.c
M carrera/screen.c => carrera/screen.c +18 -2
@@ 295,11 295,12 @@ scroll(void)
 * export screen to devdraw
 */
ulong*
attachscreen(Rectangle *r, int *ld, int *width)
attachscreen(Rectangle *r, int *ld, int *width, int *softscreen)
{
	*r = gscreen.r;
	*ld = gscreen.ldepth;
	*width = gscreen.width;
	*softscreen = 0;

	return gscreendata.data;
}


@@ 308,8 309,23 @@ attachscreen(Rectangle *r, int *ld, int *width)
 * update screen (no-op: frame buffer is direct-mapped)
 */
void
flushmemscreen(Rectangle)
flushmemscreen(Rectangle r)
{
	USED(r);
/*
	DEBUG version flashes the screen to indicate what's being painted
	Memimage *x;
	int i;

	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);
	for(i=1000000; --i>0; );
	memimagedraw(&gscreen, r, x, r.min, memones, r.min);
	freememimage(x);
*/
}

static void

M pc/devastar.c => pc/devastar.c +319 -100
@@ 13,7 13,7 @@
 *
 *  At the expense of performance, I've tried to be careful about
 *  endian-ness to make this convertable to other ISA bus machines.
 *  However, xchngus() is in assembler and will have to be translated.
 *  However, xchgw() is in assembler and will have to be translated.
 */
#define LENDIAN 1



@@ 49,11 49,22 @@ enum
	ISAstat1=	4,		/* board status (1 bit per channel) */
	ISAstat2=	5,		/* board status (1 bit per channel) */

	Maxcard=	8,
	Pramsize=	64*1024,	/* size of program ram */
	Pageshift=	14,		/* footprint of card mem in ISA space */
	Pagesize=	1<<Pageshift,
	Pagemask=	Pagesize-1,

	PCIrange=	0x00,
	PCIremap=	0x04,
	PCIregion=	0x18,
	PCImailbox=	0x40,
	PCIdoorbell0=	0x60,
	PCIdoorbell1=	0x64,
	PCIcontrol=	0x68,		/* write */
	PCIstatus=	0x68,		/* read */
	PCIcommand=	0x6C,

	Maxcard=	8,
	Pramsize=	64*1024,	/* size of program ram */
};

#define APAGE(x) ((x)>>Pageshift)


@@ 239,6 250,7 @@ struct Astar
	Rendez		r;		/* waiting for command completion */

	ISAConf;
	Pcidev*		pci;
	Lock		pagelock;	/* lock for setting page */
	int		page;		/* page currently mapped */
	int		id;		/* from plan9.ini */


@@ 247,6 259,7 @@ struct Astar
	int		ramsize;	/* 16k or 256k */
	int		memsize;	/* size of memory currently mapped */
	int		needpage;
	int		pagebase;	/* pci */
	GCB		*gcb;		/* global board comm area */
	uchar		*addr;		/* base of memory area */
	int		running;


@@ 304,6 317,11 @@ setpage(Astar *a, ulong offset)
{
	int i;

	if(a->pci){
		print("#G%d: setpage caller pc %uX\n", a->id, getcallerpc(a));
		return;
	}

	i = APAGE(offset);
	if(i == a->page)
		return;


@@ 389,9 407,11 @@ astargen(Chan *c, Dirtab *tab, int ntab, int i, Dir *db)
static void
astarreset(void)
{
	int i;
	int i, x;
	Astar *a;
	Pcidev *p;

	p = nil;
	for(i = 0; i < Maxcard; i++){
		a = astar[nastar] = xalloc(sizeof(Astar));
		if(isaconfig("serial", i, a) == 0){


@@ 400,39 420,84 @@ astarreset(void)
			continue;
		}

		a->ramsize = 0;
		/* check all possible names */
		if(strcmp(a->type, "a100i") == 0 || strcmp(a->type,"A100I") == 0)
		if(cistrcmp(a->type, "a100i") == 0 || cistrcmp(a->type,"A100I") == 0)
			a->ramsize = 16*1024;
 		else if(strcmp(a->type, "a200i") == 0 ||
		      strcmp(a->type,"A200I") == 0 ||
		      strcmp(a->type, "a16i") == 0)
 		else if(cistrcmp(a->type, "a200i") == 0 ||
		      cistrcmp(a->type,"A200I") == 0 ||
		      cistrcmp(a->type, "a16i") == 0)
			a->ramsize = 256*1024;
		else {
		else if(cistrcmp(a->type, "AvanstarXp") == 0){
			if(p = pcimatch(p, 0x114F, 0x6001)){
				a->pci = p;
				/*
				 * It's really 128KB, but split into
				 * two 64KB chunks.
				 */
				a->ramsize = 64*1024;
			}
		}

		if(a->ramsize == 0){
			xfree(a);
			astar[nastar] = 0;
			continue;
		}

		/* defaults */
		if(a->irq == 0)
			a->irq = 15;
		a->id = i;

		a->mem = umbmalloc(a->mem, Pagesize, Pagesize);
		if(a->mem == 0)
			panic("astarreset: %lux", a->mem);
		a->mem &= ~KZERO;

		if(astarsetup(a) < 0){
			xfree(a);
			astar[nastar] = 0;
			continue;
		if(a->pci){
			a->irq = p->intl;
			a->port = p->mem[1].bar & ~0x03;
			a->mem = upamalloc(p->mem[2].bar & ~0x0F, p->mem[2].size, 0);
			a->addr = (uchar*)a->mem;
			a->gcb = (GCB*)(a->mem+0x10000);

			/*
			 * Toggle the software reset and wait for
			 * the adapter local init status to indicate done.
			 */
			outl(a->port+PCIremap, 0xA0000001);
			x = inl(a->port+PCIcommand);
			outl(a->port+PCIcommand, 0x40000000|x);
			microdelay(1);
			outl(a->port+PCIcommand, x);
			delay(100);
			for(x = 0; x < 10000; x++){
				 if(inl(a->port+PCIcommand) & 0x80000000)
					break;
			}
			if(!(inl(a->port+PCIcommand) & 0x80000000))
				print("#G: didn't reset\n", a->id);

			/*
			 * So the memory can be read before any other
			 * initialisation takes place.
			 */
			a->memsize = a->ramsize;
		}
		print("serial%d avanstar port 0x%lux addr %lux irq %d\n", a->id, a->port,
			a->addr, a->irq);
		print("\tctl1 %ux ctl2 %ux maddr %ux stat1 %ux stat2 %ux\n", inb(a->port+ISActl1),
			inb(a->port+ISActl2), inb(a->port+ISAmaddr), inb(a->port+ISAstat1),
			inb(a->port+ISAstat2));
		else{
			/* defaults */
			if(a->irq == 0)
				a->irq = 15;
	
			a->mem = umbmalloc(a->mem, Pagesize, Pagesize);
			if(a->mem == 0)
				panic("astarreset: %lux", a->mem);
			a->mem = PADDR(a->mem);
	
			if(astarsetup(a) < 0){
				xfree(a);
				astar[nastar] = 0;
				continue;
			}
			print("\tctl1 %ux ctl2 %ux maddr %ux stat1 %ux stat2 %ux\n",
				inb(a->port+ISActl1), inb(a->port+ISActl2), inb(a->port+ISAmaddr),
				inb(a->port+ISAstat1), inb(a->port+ISAstat2));
		}

		print("#G%d: %s port 0x%luX addr 0x%luX irq %d\n",
			a->id, a->type, a->port, a->addr, a->irq);
		nastar++;
	}
}


@@ 475,20 540,20 @@ astarsetup(Astar *a)
	else
		found = astarprobe(a->port);
	if(!found){
		print("avanstar %d not found\n", a->id);
		print("#G%d: not found\n", a->id);
		return -1;
	}

	/* check interrupt level */
	if(isairqcode[a->irq] == -1){
		print("Avanstar %d bad irq %d\n", a->id, a->irq);
		print("#G%d: bad irq %d\n", a->id, a->irq);
		return -1;
	}

	/* set ISA memory address */
	outb(a->port+ISAmaddr, (a->mem>>12) & 0xfc);
	a->gcb = (GCB*)(KZERO | a->mem);
	a->addr = (uchar*)(KZERO | a->mem);
	a->gcb = KADDR(a->mem);
	a->addr = KADDR(a->mem);

	/* disable ISA memory response */
	outb(a->port+ISActl2, 0);


@@ 588,10 653,32 @@ astarclose(Chan *c)
}

/*
 *  read PCI mapped memory
 */
static long
pcimemread(Astar *a, uchar *to, long n, ulong offset)
{
	uchar *from;
	int rem;

	if(offset+n > a->memsize){
		if(offset >= a->memsize)
			return 0;
		n = a->memsize - offset;
	}

	from = a->addr+offset;
	for(rem = n; rem > 0; rem--)
		*to++ = *from++;

	return n;
}

/*
 *  read ISA mapped memory
 */
static long
memread(Astar *a, uchar *to, long n, ulong offset)
isamemread(Astar *a, uchar *to, long n, ulong offset)
{
	uchar *from, *e, *tp;
	int i, rem;


@@ 640,11 727,23 @@ bctlread(Astar *a, void *buf, long n, ulong offset)
{
	char s[128];

	sprint(s, "id %4.4ux ctl1 %2.2ux ctl2 %2.2ux maddr %2.2ux stat %4.4ux",
		(inb(a->port+ISAid)<<8)|inb(a->port+ISAid),
		inb(a->port+ISActl1), inb(a->port+ISActl2), 
		inb(a->port+ISAmaddr),
		(inb(a->port+ISAstat2)<<8)|inb(a->port+ISAstat1));
	if(a->pci)
		sprint(s, "range %uX remap %uX region %uX mailbox %uX doorbell0 %uX doorbell1 %uX control %uX command %uX",
			inl(a->port+PCIrange),
			inl(a->port+PCIremap),
			inl(a->port+PCIregion),
			inl(a->port+PCImailbox),
			inl(a->port+PCIdoorbell0),
			inl(a->port+PCIdoorbell1),
			inl(a->port+PCIcontrol),
			inl(a->port+PCIcommand));
	else
		sprint(s, "id %4.4ux ctl1 %2.2ux ctl2 %2.2ux maddr %2.2ux stat %4.4ux",
			(inb(a->port+ISAid)<<8)|inb(a->port+ISAid),
			inb(a->port+ISActl1), inb(a->port+ISActl2), 
			inb(a->port+ISAmaddr),
			(inb(a->port+ISAstat2)<<8)|inb(a->port+ISAstat1));

	return readstr(offset, buf, n, s);
}



@@ 693,7 792,10 @@ astarread(Chan *c, void *buf, long n, ulong offset)
		a = astar[BOARD(c->qid.path)];
		return statread(a->c + CHAN(c->qid.path), buf, n, offset);
	case Qmem:
		return memread(astar[BOARD(c->qid.path)], buf, n, offset);
		a = astar[BOARD(c->qid.path)];
		if(a->pci)
			return pcimemread(a, buf, n, offset);
		return isamemread(a, buf, n, offset);
	case Qbctl:
		return bctlread(astar[BOARD(c->qid.path)], buf, n, offset);
	case Qdata:


@@ 706,10 808,41 @@ astarread(Chan *c, void *buf, long n, ulong offset)
}

/*
 *  write PCI mapped memory
 */
static long
pcimemwrite(Astar *a, uchar *from, long n, ulong offset)
{
	uchar *to;
	int rem;
	ulong limit;

	/*
	 * Disallow writes above 0xD000 where the i960
	 * data structures live if writing in the lower bank.
	 */
	if(a->addr == (uchar*)a->mem)
		limit = 0xD000;
	else
		limit = a->memsize;
	if(offset+n > limit){
		if(offset >= limit)
			return 0;
		n = limit - offset;
	}

	to = a->addr+offset;
	for(rem = n; rem > 0; rem--)
		*to++ = *from++;

	return n;
}

/*
 *  write ISA mapped memory
 */
static long
memwrite(Astar *a, uchar *from, long n, ulong offset)
isamemwrite(Astar *a, uchar *from, long n, ulong offset)
{
	uchar *to, *e, *tp;
	int i, rem;


@@ 759,25 892,35 @@ downloadmode(Astar *a)
	int c, i;

	/* put board in download mode */
	c = inb(a->port+ISActl1);
	outb(a->port+ISActl1, c & ~ISAnotdl);
	a->memsize = Pramsize;
	a->needpage = 1;

	/* give it up to 5 seconds to reset */
	for(i = 0; i < 21; i++){
		if(!(inb(a->port+ISActl1) & ISAnotdl))
			break;
		tsleep(&a->r, return0, 0, 500);
	}
	if(inb(a->port+ISActl1) & ISAnotdl){
		print("astar%d did not reset\n", a->id);
		error(Eio);
	if(a->pci){
		/*
		 * Don't let the download write over the
		 * i960 data structures.
		 */
		a->memsize = 0xD000;
		a->addr = (uchar*)a->mem;
	}
	else{
		a->memsize = Pramsize;
		a->needpage = 1;
		c = inb(a->port+ISActl1);
		outb(a->port+ISActl1, c & ~ISAnotdl);

	/* enable ISA access to first 16k */
	a->page = -1;
	setpage(a, 0);
		/* give it up to 5 seconds to reset */
		for(i = 0; i < 21; i++){
			if(!(inb(a->port+ISActl1) & ISAnotdl))
				break;
			tsleep(&a->r, return0, 0, 500);
		}
		if(inb(a->port+ISActl1) & ISAnotdl){
			print("#G%d: did not reset\n", a->id);
			error(Eio);
		}

		/* enable ISA access to first 16k */
		a->page = -1;
		setpage(a, 0);
	}
}

/*


@@ 795,25 938,46 @@ startcp(Astar *a)
		error(Eio);

	/* take board out of download mode and enable IRQ */
	c = inb(a->port+ISActl1);
	outb(a->port+ISActl1, c|ISAien|ISAnotdl);
	if(a->pci){
		outl(a->port+PCImailbox, 1);

		/* wait for control program to signal life */
		delay(100);
		for(i = 0; i < 10; i++){
			if(inl(a->port+PCImailbox) & 0x80000000)
				break;
			tsleep(&a->r, return0, 0, 100);
		}
		if(!(inl(a->port+PCImailbox) & 0x80000000)){
			print("#G%d: program not ready\n", a->id);
			//error(Eio);
		}

		a->addr = (uchar*)(a->mem+0x10000);
	}
	else{
		c = inb(a->port+ISActl1);
		outb(a->port+ISActl1, c|ISAien|ISAnotdl);

		/* wait for control program to signal life */
		for(i = 0; i < 21; i++){
			if(inb(a->port+ISActl1) & ISApr)
				break;
			tsleep(&a->r, return0, 0, 500);
		}
		if((inb(a->port+ISActl1) & ISApr) == 0){
			print("#G%d: program not ready\n", a->id);
			error(Eio);
		}
	}

	a->memsize = a->ramsize;
	if(a->memsize <= Pagesize)
	if(a->pci || a->memsize <= Pagesize)
		a->needpage = 0;
	else
	else{
		a->page = -1;
		setpage(a, 0);
		a->needpage = 1;
	a->page = -1;
	setpage(a, 0);

	/* wait for control program to signal life */
	for(i = 0; i < 21; i++){
		if(inb(a->port+ISActl1) & ISApr)
			break;
		tsleep(&a->r, return0, 0, 500);
	}
	if((inb(a->port+ISActl1) & ISApr) == 0){
		print("astar%d program not ready\n", a->id);
		error(Eio);
	}

	if(waserror()){


@@ 825,29 989,30 @@ startcp(Astar *a)
	i = LEUS(a->gcb->type);
	switch(i){
	default:
		print("astar%d wrong board type %ux\n", a->id, i);
		print("#G%d: wrong board type %uX\n", a->id, i);
		error(Eio);
	case 0xc:
	case 0x0C:
	case 0x12:					/* AvanstarXp */
		break;
	}

	/* check assumptions */
	n = LEUS(a->gcb->ccbn);
	if(n != 8 && n != 16){
		print("astar%d had %d channels?\n", a->id, i);
		print("#G%d: has %d channels?\n", a->id, i);
		error(Eio);
	}
	x = a->addr + LEUS(a->gcb->ccboff);
	sz = LEUS(a->gcb->ccbsz);
	if(x+n*sz > a->addr+Pagesize){
		print("astar%d ccb's not in 1st page\n", a->id);
		print("#G%d: ccb's not in 1st page\n", a->id);
		error(Eio);
	}
	for(i = 0; i < n; i++){
		ccb = (CCB*)(x + i*sz);
		if(APAGE(LEUS(ccb->inbase)) != APAGE(LEUS(ccb->inlim)) ||
		   APAGE(LEUS(ccb->outbase)) != APAGE(LEUS(ccb->outlim))){
			print("astar%d chan buffer spans pages\n", a->id);
			print("#G%d: chan buffer spans pages\n", a->id);
			error(Eio);
		}
	}


@@ 871,12 1036,20 @@ startcp(Astar *a)
	}

	/* set up interrupt level, enable interrupts */
	c = inb(a->port+ISActl1);
	c &= ~ISAirq;
	c |= ISAien|isairqcode[a->irq];
	outb(a->port+ISActl1, c);

	intrenable(VectorPIC + a->irq, astarintr, a, BUSUNKNOWN);
	if(a->pci){
		/*
		 * Which bits in the interrupt control register should be set?
		 */
		outl(a->port+PCIcontrol, 0x00031F00);
		intrenable(VectorPIC + a->irq, astarintr, a, a->pci->tbdf);
	}
	else{
		c = inb(a->port+ISActl1);
		c &= ~ISAirq;
		c |= ISAien|isairqcode[a->irq];
		outb(a->port+ISActl1, c);
		intrenable(VectorPIC + a->irq, astarintr, a, BUSUNKNOWN);
	}

	/* enable control program interrupt generation */
	LOCKPAGE(a, 0);


@@ 887,8 1060,6 @@ startcp(Astar *a)
static void
bctlwrite(Astar *a, char *cmsg)
{
	uchar c;

	if(waserror()){
		qunlock(a);
		nexterror();


@@ 902,20 1073,55 @@ bctlwrite(Astar *a, char *cmsg)
		/* put board in download mode */
		downloadmode(a);

	} else if(strncmp(cmsg, "sharedmem", 9) == 0){
		/* map shared memory */
		c = inb(a->port+ISActl1);
		outb(a->port+ISActl1, c | ISAnotdl);
		a->memsize = a->ramsize;

		/* enable ISA access to first 16k */
		a->page = -1;
		setpage(a, 0);

	} else if(strncmp(cmsg, "run", 3) == 0){
		/* start up downloaded program */
		startcp(a);

	} else if(strncmp(cmsg, "test", 4) == 0){
		uchar *p;

#ifdef notdef
		p = a->addr;
		a->page = -1;
		setpage(a, 16*1024);
		*p = 'X';
		p = a->addr;
		print("page0: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p);
		setpage(a, 2*16*1024);
		print("page1: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p);
		p = a->addr;
		*p = 'Y';
		p = a->addr;
		print("page1: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p);
		setpage(a, 16*1024);
		p = a->addr;
		print("page0: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p);
#else
		p = a->addr;
		*p = 'A';
		print(" 0K: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p);

		p = a->addr+(16*1024);
		*p = 'B';
		print("16K: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p);

		p = a->addr;
		print(" 0K: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p);

		*p = 'C';
		print(" 0K: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p);

		p = a->addr+(64*1024);
		*p = 'D';
		print("64K: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p);

		p = a->addr;
		print(" 0K: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p);

		p = a->addr+(16*1024);
		print("16K: %8.8uX %2.2uX\n", inl(a->port+PCIremap), *p);
#endif /* notdef */

	} else
		error(Ebadarg);



@@ 959,11 1165,11 @@ chancmd(Astarchan *ac, int cmd)
	cmd = ccb->cmd;
	UNLOCKPAGE(ac->a);
	if(cmd){
		print("astar%d cmd didn't terminate\n", ac->a->id);
		print("#G%d: cmd didn't terminate\n", ac->a->id);
		error(Eio);
	}
	if(status){
		print("astar%d cmd status %ux\n", ac->a->id, status);
		print("#G%d: cmd status %ux\n", ac->a->id, status);
		error(Eio);
	}
}


@@ 1188,7 1394,9 @@ astarwrite(Chan *c, void *buf, long n, ulong offset)
	a = astar[BOARD(c->qid.path)];
	switch(TYPE(c->qid.path)){
	case Qmem:
		return memwrite(a, buf, n, offset);
		if(a->pci)
			return pcimemwrite(a, buf, n, offset);
		return isamemwrite(a, buf, n, offset);
	case Qbctl:
		if(n > sizeof cmsg)
			n = sizeof(cmsg) - 1;


@@ 1376,11 1584,12 @@ astarintr(Ureg *ur, void *arg)
	Astar *a = arg;
	Astarchan *ac;
	ulong globvec, vec, invec, outvec, errvec, mvec, cmdvec;
	int c;
	int c, status;


	USED(ur);
	if(a->running == 0)
		panic("astar interrupt but cp not running\n");
		panic("#G%d: interrupt but cp not running\n", a->id);

	lock(&a->pagelock);
	if(a->needpage)


@@ 1397,6 1606,16 @@ astarintr(Ureg *ur, void *arg)
	USED(mvec);

	/* reenable interrupts */
	if(a->pci){
		/*
		 * Only the PCI doorbell interrupt is expected.
		 */
		status = inl(a->port+PCIstatus);
		if((status & 0x0810E000) != 0x00002000)
			print("#G%d: unexpected interrupt %uX\n", a->id, status);
		if(status & 0x00002000)
			outl(a->port+PCIdoorbell1, 1);
	}
	a->gcb->cmd2 = LEUS(Gintack);

	/* service interrupts */

M pc/screen.c => pc/screen.c +2 -1
@@ 117,7 117,7 @@ screenaperture(int size, int align)
}

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



@@ 128,6 128,7 @@ attachscreen(Rectangle* r, int* ld, int* width)
	*r = scr->gscreen->r;
	*ld = scr->gscreen->ldepth;
	*width = scr->gscreen->width;
	*softscreen = scr->useflush;

	return scr->gscreendata->data;
}

M port/devdraw.c => port/devdraw.c +43 -5
@@ 57,6 57,7 @@ struct Draw
	int		nname;
	DName*	name;
	int		vers;
	int		softscreen;
	int		blanked;	/* screen turned off */
	ulong		blanktime;	/* time of last operation */
	ulong		savemap[3*256];


@@ 152,6 153,7 @@ static	Draw		sdraw;
static	Memimage	screenimage;
static	Memdata	screendata;
static	Rectangle	flushrect;
static	int		waste;
static	DScreen*	dscreen;
extern	void		flushmemscreen(Rectangle);
	void		drawmesg(Client*, void*, int);


@@ 294,11 296,47 @@ drawrefresh(Memimage *l, Rectangle r, void *v)
	}
}

static
void
static void
addflush(Rectangle r)
{
	bbox(&flushrect, r);
	int abb, ar, anbb;
	Rectangle nbb;

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

	if(flushrect.min.x >= flushrect.max.x){
		flushrect = r;
		waste = 0;
		return;
	}
	nbb = flushrect;
	bbox(&nbb, r);
	ar = Dx(r)*Dy(r);
	abb = Dx(flushrect)*Dy(flushrect);
	anbb = Dx(nbb)*Dy(nbb);
	/*
	 * Area of new waste is area of new bb minus area of old bb,
	 * less the area of the new segment, which we assume is not waste.
	 * This could be negative, but that's OK.
	 */
	waste += anbb-abb - ar;
	if(waste < 0)
		waste = 0;
	/*
	 * absorb if:
	 *	total area is small
	 *	waste is less than ½ total area
	 * 	rectangles touch
	 */
	if(anbb<=1024 || waste*2<anbb || rectXrect(flushrect, r)){
		flushrect = nbb;
		return;
	}
	/* emit current state */
	flushmemscreen(flushrect);
	flushrect = r;
	waste = 0;
}

static


@@ 320,7 358,7 @@ dstflush(int dstid, Memimage *dst, Rectangle r)
		r = rectaddpt(r, l->delta);
		l = l->screen->image->layer;
	}while(l);
	bbox(&flushrect, r);
	addflush(r);
}

static


@@ 756,7 794,7 @@ drawattach(char *spec)

	if(screendata.data == nil){
		screendata.base = nil;
		screendata.data = attachscreen(&screenimage.r, &screenimage.ldepth, &width);
		screendata.data = attachscreen(&screenimage.r, &screenimage.ldepth, &width, &sdraw.softscreen);
		if(screendata.data != nil){
			screendata.ref = 1;
			screenimage.data = &screendata;