xref: /inferno-os/os/mpc/screen.c (revision 4eb166cf184c1f102fb79e31b1465ea3e2021c39)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"io.h"
7 #include	"../port/error.h"
8 
9 #include	<draw.h>
10 #include	<memdraw.h>
11 #include	<cursor.h>
12 
13 #include	"screen.h"
14 
15 enum {
16 	Backgnd = 0xFF,	/* white */
17 	Foregnd =	0x00,	/* black */
18 };
19 
20 Cursor	arrow = {
21 	{ -1, -1 },
22 	{ 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
23 	  0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04,
24 	  0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04,
25 	  0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40,
26 	},
27 	{ 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
28 	  0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8,
29 	  0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8,
30 	  0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00,
31 	},
32 };
33 
34 static Memdata xgdata;
35 static Memimage xgscreen =
36 {
37 	{0, 0, 0, 0},	/* r */
38 	{0, 0, 0, 0},	/* clipr */
39 	8,			/* depth */
40 	1,			/* nchan */
41 	CMAP8,		/* chan */
42 	nil,			/* cmap */
43 	&xgdata,		/* data */
44 	0,			/* zero */
45 	0,			/* width */
46 	nil,			/* layer */
47 	0,			/* flags */
48 };
49 
50 int	novgascreen;	/* optionally set by configuration file */
51 static	int	lcdpdpar;	/* value to load into io->pdpar */
52 
53 Memimage *gscreen;
54 Memimage *conscol;
55 Memimage *back;
56 
57 static	Memsubfont *memdefont;
58 static	Lock	palettelock;			/* access to DAC registers */
59 static	Lock	screenlock;
60 static	int	h;
61 static	Point	curpos;
62 static	Rectangle window;
63 
64 typedef struct SWcursor SWcursor;
65 static SWcursor *swc = nil;
66 SWcursor* swcurs_create(ulong *, int, int, Rectangle, int);
67 void swcurs_destroy(SWcursor*);
68 void swcurs_enable(SWcursor*);
69 void swcurs_disable(SWcursor*);
70 void swcurs_hide(SWcursor*);
71 void swcurs_unhide(SWcursor*);
72 void swcurs_load(SWcursor*, Cursor*);
73 
74 static	void	screenputc(char*);
75 static	void	scroll(void);
76 static	void	setscreen(Mode*);
77 static	void	cursorlock(Rectangle);
78 static	void	cursorunlock(void);
79 static	void	lcdinit(Mode*);
80 static	void	lcdsetrgb(int, ulong, ulong, ulong);
81 
82 /*
83  *  Called by main().
84  */
85 void
86 screeninit(void)
87 {
88 	Mode m;
89 
90 novgascreen=1; return;
91 
92 	/* default size and parameters */
93 	memset(&m.lcd, 0, sizeof(m.lcd));
94 	m.x = 640;
95 	m.y = 480;
96 	m.d = 3;
97 	if(novgascreen == 0 && archlcdmode(&m) >= 0){
98 		memdefont = getmemdefont();
99 		setscreen(&m);
100 	}
101 }
102 
103 /*
104  * On 8 bit displays, load the default color map
105  */
106 void
107 graphicscmap(int invert)
108 {
109 	int num, den, i, j;
110 	int r, g, b, cr, cg, cb, v;
111 
112 	for(r=0,i=0;r!=4;r++) for(v=0;v!=4;v++,i+=16){
113 		for(g=0,j=v-r;g!=4;g++) for(b=0;b!=4;b++,j++){
114 			den=r;
115 			if(g>den) den=g;
116 			if(b>den) den=b;
117 			if(den==0)	/* divide check -- pick grey shades */
118 				cr=cg=cb=v*17;
119 			else{
120 				num=17*(4*den+v);
121 				cr=r*num/den;
122 				cg=g*num/den;
123 				cb=b*num/den;
124 			}
125 			if(invert)
126 				setcolor(255-i-(j&15),
127 					cr*0x01010101, cg*0x01010101, cb*0x01010101);
128 			else
129 				setcolor(i+(j&15),
130 					cr*0x01010101, cg*0x01010101, cb*0x01010101);
131 		}
132 	}
133 }
134 
135 /*
136  *  reconfigure screen shape
137  */
138 static void
139 setscreen(Mode *mode)
140 {
141 	int h;
142 
143 	if(swc)
144 		swcurs_destroy(swc);
145 
146 	gscreen = &xgscreen;
147 	xgdata.ref = 1;
148 	lcdinit(mode);
149 	xgdata.bdata = (uchar*)mode->aperture;
150 	if(xgdata.bdata == nil)
151 		panic("setscreen: vga soft memory");
152 
153 	gscreen->r = Rect(0, 0, mode->x, mode->y);
154 	gscreen->clipr = gscreen->r;
155 	gscreen->depth = 1<<mode->d;
156 	gscreen->width = wordsperline(gscreen->r, gscreen->depth);
157 	memimageinit();
158 	memdefont = getmemdefont();
159 
160 	memsetchan(gscreen, CMAP8);
161 	back = memwhite;
162 	conscol = memblack;
163 	memimagedraw(gscreen, gscreen->r, memwhite, ZP, memopaque, ZP, SoverD);
164 	graphicscmap(0);
165 
166 	/* get size for a system window */
167 	h = memdefont->height;
168 	window = insetrect(gscreen->r, 4);
169 	window.max.y = window.min.y+(Dy(window)/h)*h;
170 	curpos = window.min;
171 //	screenclear();
172 
173 	graphicscmap(0);
174 
175 //	swc = swcurs_create(gscreendata.data, gscreen.width, gscreen.ldepth, gscreen.r, 1);
176 
177 	drawcursor(nil);
178 }
179 
180 enum {
181 	ScreenCached = 1	/* non-zero if screen region not write-through */
182 };
183 
184 void
185 flushmemscreen(Rectangle r)
186 {
187 	if(rectclip(&r, gscreen->r) == 0)
188 		return;
189 	if(r.min.x >= r.max.x || r.min.y >= r.max.y)
190 		return;
191 	if(ScreenCached)
192 		dcflush((ulong*)gscreen->data->bdata + gscreen->width*r.min.y, gscreen->width*Dy(r));
193 }
194 
195 /*
196  * export screen to interpreter
197  */
198 uchar*
199 attachscreen(Rectangle *r, ulong *chan, int* d, int *width, int *softscreen)
200 {
201 	*r = gscreen->r;
202 	*d = gscreen->depth;
203 	*chan = gscreen->chan;
204 	*width = gscreen->width;
205 	*softscreen = ScreenCached;
206 
207 	return (uchar*)gscreen->data->bdata;
208 }
209 
210 void
211 detachscreen(void)
212 {
213 }
214 
215 /*
216  *  write a string to the screen
217  */
218 void
219 screenputs(char *s, int n)
220 {
221 	int i;
222 	Rune r;
223 	char buf[4];
224 
225 	if(novgascreen || xgdata.bdata == nil || memdefont == nil)
226 		return;
227 	if(islo() == 0) {
228 		/* don't deadlock trying to print in interrupt */
229 		if(!canlock(&screenlock))
230 			return;
231 	} else
232 		lock(&screenlock);
233 
234 	while(n > 0) {
235 		i = chartorune(&r, s);
236 		if(i == 0){
237 			s++;
238 			--n;
239 			continue;
240 		}
241 		memmove(buf, s, i);
242 		buf[i] = 0;
243 		n -= i;
244 		s += i;
245 		screenputc(buf);
246 	}
247 	/* Only OK for now */
248 	flushmemscreen(gscreen->r);
249 
250 	unlock(&screenlock);
251 }
252 
253 static void
254 scroll(void)
255 {
256 	int o;
257 	Point p;
258 	Rectangle r;
259 
260 	o = 4*memdefont->height;
261 	r = Rpt(window.min, Pt(window.max.x, window.max.y-o));
262 	p = Pt(window.min.x, window.min.y+o);
263 	memimagedraw(gscreen, r, gscreen, p, nil, p, SoverD);
264 	flushmemscreen(r);
265 	r = Rpt(Pt(window.min.x, window.max.y-o), window.max);
266 	memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
267 	flushmemscreen(r);
268 
269 	curpos.y -= o;
270 }
271 
272 static void
273 clearline(void)
274 {
275 	Rectangle r;
276 	int yloc = curpos.y;
277 
278 	r = Rpt(Pt(window.min.x, window.min.y + yloc),
279 		Pt(window.max.x, window.min.y+yloc+memdefont->height));
280 	memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
281 }
282 
283 static void
284 screenputc(char *buf)
285 {
286 	Point p;
287 	int h, w, pos;
288 	Rectangle r;
289 	static int *xp;
290 	static int xbuf[256];
291 
292 	h = memdefont->height;
293 	if(xp < xbuf || xp >= &xbuf[sizeof(xbuf)])
294 		xp = xbuf;
295 
296 	switch(buf[0]) {
297 	case '\n':
298 		if(curpos.y+h >= window.max.y)
299 			scroll();
300 		curpos.y += h;
301 		/* fall through */
302 	case '\r':
303 		xp = xbuf;
304 		curpos.x = window.min.x;
305 		break;
306 	case '\t':
307 		if(curpos.x == window.min.x)
308 			clearline();
309 		p = memsubfontwidth(memdefont, " ");
310 		w = p.x;
311 		*xp++ = curpos.x;
312 		pos = (curpos.x-window.min.x)/w;
313 		pos = 8-(pos%8);
314 		r = Rect(curpos.x, curpos.y, curpos.x+pos*w, curpos.y+h);
315 		memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
316 		flushmemscreen(r);
317 		curpos.x += pos*w;
318 		break;
319 	case '\b':
320 		if(xp <= xbuf)
321 			break;
322 		xp--;
323 		r = Rpt(Pt(*xp, curpos.y), Pt(curpos.x, curpos.y + h));
324 		memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
325 		flushmemscreen(r);
326 		curpos.x = *xp;
327 		break;
328 	case '\0':
329 		break;
330 	default:
331 		p = memsubfontwidth(memdefont, buf);
332 		w = p.x;
333 
334 		if(curpos.x >= window.max.x-w)
335 			screenputc("\n");
336 
337 		if(curpos.x == window.min.x)
338 			clearline();
339 		if(xp < xbuf+nelem(xbuf))
340 			*xp++ = curpos.x;
341 		r = Rect(curpos.x, curpos.y, curpos.x+w, curpos.y+h);
342 		memimagedraw(gscreen, r, back, ZP, nil, ZP, SoverD);
343 		memimagestring(gscreen, curpos, conscol, ZP, memdefont, buf);
344 		flushmemscreen(r);
345 		curpos.x += w;
346 	}
347 }
348 
349 int
350 setcolor(ulong p, ulong r, ulong g, ulong b)
351 {
352 	ulong x;
353 
354 	if(gscreen->depth >= 8)
355 		x = 0xFF;
356 	else
357 		x = 0xF;
358 	p &= x;
359 	p ^= x;
360 	lock(&palettelock);
361 	lcdsetrgb(p, r, g, b);
362 	unlock(&palettelock);
363 	return ~0;
364 }
365 
366 void
367 getcolor(ulong p, ulong *pr, ulong *pg, ulong *pb)
368 {
369 	/* TO DO */
370 	*pr = *pg = *pb = 0;
371 }
372 
373 /*
374  * See section 5.2.1 (page 5-6) of the MPC823 manual
375  */
376 static uchar lcdclock[17] = {	/* (a<<2)|b => divisor of (1<<a)*((b<<1)+1) */
377 	0, 0, (1<<2), 1,
378 	(2<<2), 2, (1<<2)|1, 3,
379 	(3<<2), (1<<2)|2, (1<<2)|2, (2<<2)|1,
380 	(2<<2)|1, (1<<2)|3, (1<<2)|3, (4<<2),
381 	(4<<2)
382 };
383 
384 enum {
385 	/* lccr */
386 	Enable = 1<<0,
387 
388 	/* lchcr */
389 	BigEndian = 1<<24,
390 	AT7 = 7<<21,	/* access type */
391 
392 	/* sdcr */
393 	LAM = 1<<6,	/* ``LCD aggressive mode'' */
394 };
395 
396 /*
397  * initialise MPC8xx LCD controller incorporating board or display-specific values in Mode.lcd
398  */
399 static void
400 lcdinit(Mode *mode)
401 {
402 	IMM *io;
403 	int i, d;
404 	long hz;
405 
406 	io = m->iomem;
407 	mode->aperture = xspanalloc(mode->x*mode->y, 16, 0);
408 	mode->apsize = mode->x*mode->y;
409 
410 	io->sdcr = 1;	/* MPC823 errata: turn off LAM before disabling controller */
411 	eieio();
412 	io->lcfaa = PADDR(mode->aperture);
413 	io->lccr = (((mode->x*mode->y*(1<<mode->d)+127)/128) << 17) | (mode->d << 5) | mode->lcd.flags;
414 	switch(mode->d){
415 	default:
416 	case 0:
417 		/* monochrome/greyscale identity map */
418 		for(i=0; i<16; i++)
419 			io->lcdmap[i] = i;
420 		break;
421 	case 2:
422 		/* 4-bit grey scale map */
423 		for(i=0; i<16; i++)
424 			io->lcdmap[0] = (i<<8)|(i<<4)|i;
425 		break;
426 	case 3:
427 		/* 8-bit linear map */
428 		for(i=0; i<256; i++)
429 			io->lcdmap[i] = (i<<8)|(i<<4)|i;
430 		break;
431 	}
432 
433 	io->lcvcr = (mode->y << 11) | (mode->lcd.vpw<<28) | (mode->lcd.ac<<21) | mode->lcd.wbf;
434 	io->lchcr = (mode->x<<10) | BigEndian | mode->lcd.wbl;
435 
436 	hz = m->cpuhz;
437 	d = hz/mode->lcd.freq;
438 	if(hz/d > mode->lcd.freq)
439 		d++;
440 	if(d >= 16)
441 		d = 16;
442 
443 	/*
444 	 * enable LCD outputs
445 	 */
446 	io->pddat = 0;
447 	lcdpdpar = 0x1fff & ~mode->lcd.notpdpar;
448 	io->pdpar = lcdpdpar;
449 	io->pddir = 0x1fff;
450 	io->pbpar |= IBIT(31) | IBIT(19) | IBIT(17);
451 	io->pbdir |= IBIT(31) | IBIT(19) | IBIT(17);
452 	io->pbodr &= ~(IBIT(31) | IBIT(19) | IBIT(17));
453 
454 	/*
455 	 * with the data cache off, early revisions of the 823 did not require
456 	 * the `aggressive' DMA priority to avoid flicker, but flicker is obvious
457 	 * on the 823A when the cache is on, so LAM is now set
458 	 */
459 	io->sdcr = (io->sdcr & ~0xF) | LAM;	/* LAM=1, LAID=0, RAID=0 */
460 
461 //	gscreen.width = gscreen.width;	/* access external memory before enabling (mpc823 errata) */
462 	eieio();
463 	io->sccrk = KEEP_ALIVE_KEY;
464 	eieio();
465 	io->sccr  = (io->sccr & ~0x1F) | lcdclock[d];
466 	eieio();
467 	io->sccrk= ~KEEP_ALIVE_KEY;
468 	io->lcsr = 7;	/* clear status */
469 	eieio();
470 	io->lccr |= Enable;
471 	archbacklight(1);
472 }
473 
474 static void
475 lcdsetrgb(int p, ulong r, ulong g, ulong b)
476 {
477 	r >>= 28;
478 	g >>= 28;
479 	b >>= 28;
480 	m->iomem->lcdmap[p&0xFF] = (r<<8) | (g<<4) | b;
481 }
482 
483 void
484 blankscreen(int blank)
485 {
486 	USED(blank);	/* TO DO */
487 }
488 
489 /*
490  * enable/disable LCD panel (eg, when using video subsystem)
491  */
492 void
493 lcdpanel(int on)
494 {
495 	IMM *io;
496 
497 	if(on){
498 		archbacklight(1);
499 		io = ioplock();
500 		io->pddat = 0;
501 		io->pdpar = lcdpdpar;
502 		io->pddir = 0x1fff;
503 		io->lccr |= Enable;
504 		iopunlock();
505 	}else{
506 		io = ioplock();
507 		io->sdcr = 1;	/* MPC823 errata: turn off LAM before disabling controller */
508 		eieio();
509 		io->pddir = 0;
510 		eieio();
511 		io->lccr &= ~Enable;
512 		iopunlock();
513 		archbacklight(0);
514 	}
515 }
516 
517 /*
518  *	Software cursor code.  Interim version (for baseline).
519  *	we may want to replace code here by memdraw primitives.
520  */
521 
522 enum {
523 	CUR_ENA = 0x01,		/* cursor is enabled */
524 	CUR_DRW = 0x02,		/* cursor is currently drawn */
525 	CUR_SWP = 0x10,		/* bit swap */
526 	CURSWID	= 16,
527 	CURSHGT	= 16,
528 };
529 
530 typedef struct SWcursor {
531 	ulong	*fb;	/* screen frame buffer */
532 	Rectangle r;
533 	int	d;	/* ldepth of screen */
534 	int 	width;	/* width of screen in ulongs */
535 	int	x;
536 	int	y;
537 	int	hotx;
538 	int	hoty;
539 	uchar	cbwid;	/* cursor byte width */
540 	uchar	f;	/* flags */
541 	uchar	cwid;
542 	uchar	chgt;
543 	int	hidecount;
544 	uchar	data[CURSWID*CURSHGT];
545 	uchar	mask[CURSWID*CURSHGT];
546 	uchar	save[CURSWID*CURSHGT];
547 } SWcursor;
548 
549 static Rectangle cursoroffrect;
550 static int	cursorisoff;
551 
552 static void swcursorflush(int, int);
553 static void	swcurs_draw_or_undraw(SWcursor *);
554 
555 static void
556 cursorupdate0(void)
557 {
558 	int inrect, x, y;
559 	Point m;
560 
561 	m = mousexy();
562 	x = m.x - swc->hotx;
563 	y = m.y - swc->hoty;
564 	inrect = (x >= cursoroffrect.min.x && x < cursoroffrect.max.x
565 		&& y >= cursoroffrect.min.y && y < cursoroffrect.max.y);
566 	if (cursorisoff == inrect)
567 		return;
568 	cursorisoff = inrect;
569 	if (inrect)
570 		swcurs_hide(swc);
571 	else {
572 		swc->hidecount = 0;
573 		swcurs_draw_or_undraw(swc);
574 	}
575 	swcursorflush(m.x, m.y);
576 }
577 
578 void
579 cursorupdate(Rectangle r)
580 {
581 	lock(&screenlock);
582 	r.min.x -= 16;
583 	r.min.y -= 16;
584 	cursoroffrect = r;
585 	if (swc)
586 		cursorupdate0();
587 	unlock(&screenlock);
588 }
589 
590 void
591 cursorenable(void)
592 {
593 	Point m;
594 
595 	lock(&screenlock);
596 	if(swc) {
597 		swcurs_enable(swc);
598 		m = mousexy();
599 		swcursorflush(m.x, m.y);
600 	}
601 	unlock(&screenlock);
602 }
603 
604 void
605 cursordisable(void)
606 {
607 	Point m;
608 
609 	lock(&screenlock);
610 	if(swc) {
611 		swcurs_disable(swc);
612 		m = mousexy();
613 		swcursorflush(m.x, m.y);
614 	}
615 	unlock(&screenlock);
616 }
617 
618 void
619 drawcursor(Drawcursor* c)
620 {
621 	Point p;
622 	Cursor curs, *cp;
623 	int j, i, h, bpl;
624 	uchar *bc, *bs, *cclr, *cset;
625 
626 	if(!swc)
627 		return;
628 
629 	/* Set the default system cursor */
630 	if(!c || c->data == nil)
631 		cp = &arrow /*&crosshair_black*/;
632 	else {
633 		cp = &curs;
634 		p.x = c->hotx;
635 		p.y = c->hoty;
636 		cp->offset = p;
637 		bpl = bytesperline(Rect(c->minx, c->miny, c->maxx, c->maxy), 1);
638 
639 		h = (c->maxy-c->miny)/2;
640 		if(h > 16)
641 			h = 16;
642 
643 		bc = c->data;
644 		bs = c->data + h*bpl;
645 
646 		cclr = cp->clr;
647 		cset = cp->set;
648 		for(i = 0; i < h; i++) {
649 			for(j = 0; j < 2; j++) {
650 				cclr[j] = bc[j];
651 				cset[j] = bs[j];
652 			}
653 			bc += bpl;
654 			bs += bpl;
655 			cclr += 2;
656 			cset += 2;
657 		}
658 	}
659 
660 	if(swc) {
661 		swcurs_load(swc, cp);
662 		p = mousexy();
663 		swcursorflush(p.x, p.y);
664 	}
665 }
666 
667 SWcursor*
668 swcurs_create(ulong *fb, int width, int ldepth, Rectangle r, int bitswap)
669 {
670 	SWcursor *swc = (SWcursor*)malloc(sizeof(SWcursor));
671 	swc->fb = fb;
672 	swc->r = r;
673 	swc->d = ldepth;
674 	swc->width = width;
675 	swc->f = bitswap ? CUR_SWP : 0;
676 	swc->x = swc->y = 0;
677 	swc->hotx = swc->hoty = 0;
678 	swc->hidecount = 0;
679 	return swc;
680 }
681 
682 void
683 swcurs_destroy(SWcursor *swc)
684 {
685 	swcurs_disable(swc);
686 	free(swc);
687 }
688 
689 static void
690 swcursorflush(int x, int y)
691 {
692 	Rectangle r;
693 
694 	/* XXX a little too paranoid here */
695 	r.min.x = x-16;
696 	r.min.y = y-16;
697 	r.max.x = x+17;
698 	r.max.y = y+17;
699 	flushmemscreen(r);
700 }
701 
702 static void
703 swcurs_draw_or_undraw(SWcursor *swc)
704 {
705 	uchar *p;
706 	uchar *cs;
707 	int w, vw;
708 	int x1 = swc->r.min.x;
709 	int y1 = swc->r.min.y;
710 	int x2 = swc->r.max.x;
711 	int y2 = swc->r.max.y;
712 	int xp = swc->x - swc->hotx;
713 	int yp = swc->y - swc->hoty;
714 	int ofs;
715 
716 	if(((swc->f & CUR_ENA) && (swc->hidecount <= 0))
717 			 == ((swc->f & CUR_DRW) != 0))
718 		return;
719 	w = swc->cbwid*BI2BY/(1 << swc->d);
720 	x1 = xp < x1 ? x1 : xp;
721 	y1 = yp < y1 ? y1 : yp;
722 	x2 = xp+w >= x2 ? x2 : xp+w;
723 	y2 = yp+swc->chgt >= y2 ? y2 : yp+swc->chgt;
724 	if(x2 <= x1 || y2 <= y1)
725 		return;
726 	p = (uchar*)(swc->fb + swc->width*y1)
727 		+ x1*(1 << swc->d)/BI2BY;
728 	y2 -= y1;
729 	x2 = (x2-x1)*(1 << swc->d)/BI2BY;
730 	vw = swc->width*BY2WD - x2;
731 	w = swc->cbwid - x2;
732 	ofs = swc->cbwid*(y1-yp)+(x1-xp);
733 	cs = swc->save + ofs;
734 	if((swc->f ^= CUR_DRW) & CUR_DRW) {
735 		uchar *cm = swc->mask + ofs;
736 		uchar *cd = swc->data + ofs;
737 		while(y2--) {
738 			x1 = x2;
739 			while(x1--) {
740 				*p = ((*cs++ = *p) & *cm++) ^ *cd++;
741 				p++;
742 			}
743 			cs += w;
744 			cm += w;
745 			cd += w;
746 			p += vw;
747 		}
748 	} else {
749 		while(y2--) {
750 			x1 = x2;
751 			while(x1--)
752 				*p++ = *cs++;
753 			cs += w;
754 			p += vw;
755 		}
756 	}
757 }
758 
759 void
760 swcurs_hide(SWcursor *swc)
761 {
762 	++swc->hidecount;
763 	swcurs_draw_or_undraw(swc);
764 }
765 
766 void
767 swcurs_unhide(SWcursor *swc)
768 {
769 	if (--swc->hidecount < 0)
770 		swc->hidecount = 0;
771 	swcurs_draw_or_undraw(swc);
772 }
773 
774 void
775 swcurs_enable(SWcursor *swc)
776 {
777 	swc->f |= CUR_ENA;
778 	swcurs_draw_or_undraw(swc);
779 }
780 
781 void
782 swcurs_disable(SWcursor *swc)
783 {
784 	swc->f &= ~CUR_ENA;
785 	swcurs_draw_or_undraw(swc);
786 }
787 
788 void
789 swcurs_load(SWcursor *swc, Cursor *c)
790 {
791 	int i, k;
792 	uchar *bc, *bs, *cd, *cm;
793 	static uchar bdv[4] = {0,Backgnd,Foregnd,0xff};
794 	static uchar bmv[4] = {0xff,0,0,0xff};
795 	int bits = 1<<swc->d;
796 	uchar mask = (1<<bits)-1;
797 	int bswp = (swc->f&CUR_SWP) ? 8-bits : 0;
798 
799 	bc = c->clr;
800 	bs = c->set;
801 
802 	swcurs_hide(swc);
803 	cd = swc->data;
804 	cm = swc->mask;
805 	swc->hotx = c->offset.x;
806 	swc->hoty = c->offset.y;
807 	swc->chgt = CURSHGT;
808 	swc->cwid = CURSWID;
809 	swc->cbwid = CURSWID*(1<<swc->d)/BI2BY;
810 	for(i = 0; i < CURSWID/BI2BY*CURSHGT; i++) {
811 		uchar bcb = *bc++;
812 		uchar bsb = *bs++;
813 		for(k=0; k<BI2BY;) {
814 			uchar cdv = 0;
815 			uchar cmv = 0;
816 			int z;
817 			for(z=0; z<BI2BY; z += bits) {
818 				int n = ((bsb&(0x80))|((bcb&(0x80))<<1))>>7;
819 				int s = z^bswp;
820 				cdv |= (bdv[n]&mask) << s;
821 				cmv |= (bmv[n]&mask) << s;
822 				bcb <<= 1;
823 				bsb <<= 1;
824 				k++;
825 			}
826 			*cd++ = cdv;
827 			*cm++ = cmv;
828 		}
829 	}
830 	swcurs_unhide(swc);
831 }
832 
833