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 "ureg.h"
8 #include "../port/error.h"
9
10 #define Image IMAGE
11 #include <draw.h>
12 #include <memdraw.h>
13 #include <cursor.h>
14 #include "screen.h"
15
16 #define RGB2K(r,g,b) ((156763*(r)+307758*(g)+59769*(b))>>19)
17
18 Point ZP = {0, 0};
19
20 Rectangle physgscreenr;
21
22 Memdata gscreendata;
23 Memimage *gscreen;
24
25 VGAscr vgascreen[1];
26
27 Cursor arrow = {
28 { -1, -1 },
29 { 0xFF, 0xFF, 0x80, 0x01, 0x80, 0x02, 0x80, 0x0C,
30 0x80, 0x10, 0x80, 0x10, 0x80, 0x08, 0x80, 0x04,
31 0x80, 0x02, 0x80, 0x01, 0x80, 0x02, 0x8C, 0x04,
32 0x92, 0x08, 0x91, 0x10, 0xA0, 0xA0, 0xC0, 0x40,
33 },
34 { 0x00, 0x00, 0x7F, 0xFE, 0x7F, 0xFC, 0x7F, 0xF0,
35 0x7F, 0xE0, 0x7F, 0xE0, 0x7F, 0xF0, 0x7F, 0xF8,
36 0x7F, 0xFC, 0x7F, 0xFE, 0x7F, 0xFC, 0x73, 0xF8,
37 0x61, 0xF0, 0x60, 0xE0, 0x40, 0x40, 0x00, 0x00,
38 },
39 };
40
41 int didswcursorinit;
42
43 static void *softscreen;
44
45 int
screensize(int x,int y,int z,ulong chan)46 screensize(int x, int y, int z, ulong chan)
47 {
48 VGAscr *scr;
49 void *oldsoft;
50
51 lock(&vgascreenlock);
52 if(waserror()){
53 unlock(&vgascreenlock);
54 nexterror();
55 }
56
57 memimageinit();
58 scr = &vgascreen[0];
59 oldsoft = softscreen;
60
61 if(scr->paddr == 0){
62 int width = (x*z)/BI2WD;
63 void *p;
64
65 p = xalloc(width*BY2WD*y);
66 if(p == nil)
67 error("no memory for vga soft screen");
68 gscreendata.bdata = softscreen = p;
69 if(scr->dev && scr->dev->page){
70 scr->vaddr = KADDR(VGAMEM());
71 scr->apsize = 1<<16;
72 }
73 scr->useflush = 1;
74 }
75 else{
76 gscreendata.bdata = scr->vaddr;
77 scr->useflush = scr->dev && scr->dev->flush;
78 }
79
80 scr->gscreen = nil;
81 if(gscreen)
82 freememimage(gscreen);
83 gscreen = allocmemimaged(Rect(0,0,x,y), chan, &gscreendata);
84 if(gscreen == nil)
85 error("no memory for vga memimage");
86 vgaimageinit(chan);
87
88 scr->palettedepth = 6; /* default */
89 scr->gscreendata = &gscreendata;
90 scr->memdefont = getmemdefont();
91 scr->gscreen = gscreen;
92
93 physgscreenr = gscreen->r;
94 unlock(&vgascreenlock);
95 poperror();
96 if(oldsoft)
97 xfree(oldsoft);
98
99 memimagedraw(gscreen, gscreen->r, memblack, ZP, nil, ZP, S);
100 flushmemscreen(gscreen->r);
101
102 if(didswcursorinit)
103 swcursorinit();
104 drawcmap();
105 return 0;
106 }
107
108 int
screenaperture(int size,int align)109 screenaperture(int size, int align)
110 {
111 VGAscr *scr;
112
113 scr = &vgascreen[0];
114
115 if(scr->paddr) /* set up during enable */
116 return 0;
117
118 if(size == 0)
119 return 0;
120
121 if(scr->dev && scr->dev->linear){
122 scr->dev->linear(scr, size, align);
123 return 0;
124 }
125
126 /*
127 * Need to allocate some physical address space.
128 * The driver will tell the card to use it.
129 */
130 size = PGROUND(size);
131 scr->paddr = upaalloc(size, align);
132 if(scr->paddr == 0)
133 return -1;
134 scr->vaddr = vmap(scr->paddr, size);
135 if(scr->vaddr == nil)
136 return -1;
137 scr->apsize = size;
138
139 return 0;
140 }
141
142 uchar*
attachscreen(Rectangle * r,ulong * chan,int * d,int * width,int * softscreen)143 attachscreen(Rectangle* r, ulong* chan, int* d, int* width, int *softscreen)
144 {
145 VGAscr *scr;
146
147 scr = &vgascreen[0];
148 if(scr->gscreen == nil || scr->gscreendata == nil)
149 return nil;
150
151 *r = scr->gscreen->clipr;
152 *chan = scr->gscreen->chan;
153 *d = scr->gscreen->depth;
154 *width = scr->gscreen->width;
155 *softscreen = scr->useflush;
156
157 return scr->gscreendata->bdata;
158 }
159
160 /*
161 * It would be fair to say that this doesn't work for >8-bit screens.
162 */
163 void
flushmemscreen(Rectangle r)164 flushmemscreen(Rectangle r)
165 {
166 VGAscr *scr;
167 uchar *sp, *disp, *sdisp, *edisp;
168 int y, len, incs, off, page;
169
170 scr = &vgascreen[0];
171 if(scr->dev && scr->dev->flush){
172 scr->dev->flush(scr, r);
173 return;
174 }
175 if(scr->gscreen == nil || scr->useflush == 0)
176 return;
177 if(scr->dev == nil || scr->dev->page == nil)
178 return;
179
180 if(rectclip(&r, scr->gscreen->r) == 0)
181 return;
182
183 incs = scr->gscreen->width * BY2WD;
184
185 switch(scr->gscreen->depth){
186 default:
187 len = 0;
188 panic("flushmemscreen: depth\n");
189 break;
190 case 8:
191 len = Dx(r);
192 break;
193 }
194 if(len < 1)
195 return;
196
197 off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8;
198 page = off/scr->apsize;
199 off %= scr->apsize;
200 disp = scr->vaddr;
201 sdisp = disp+off;
202 edisp = disp+scr->apsize;
203
204 off = r.min.y*scr->gscreen->width*BY2WD+(r.min.x*scr->gscreen->depth)/8;
205
206 sp = scr->gscreendata->bdata + off;
207
208 scr->dev->page(scr, page);
209 for(y = r.min.y; y < r.max.y; y++) {
210 if(sdisp + incs < edisp) {
211 memmove(sdisp, sp, len);
212 sp += incs;
213 sdisp += incs;
214 }
215 else {
216 off = edisp - sdisp;
217 page++;
218 if(off <= len){
219 if(off > 0)
220 memmove(sdisp, sp, off);
221 scr->dev->page(scr, page);
222 if(len - off > 0)
223 memmove(disp, sp+off, len - off);
224 }
225 else {
226 memmove(sdisp, sp, len);
227 scr->dev->page(scr, page);
228 }
229 sp += incs;
230 sdisp += incs - scr->apsize;
231 }
232 }
233 }
234
235 void
getcolor(ulong p,ulong * pr,ulong * pg,ulong * pb)236 getcolor(ulong p, ulong* pr, ulong* pg, ulong* pb)
237 {
238 VGAscr *scr;
239 ulong x;
240
241 scr = &vgascreen[0];
242 if(scr->gscreen == nil)
243 return;
244
245 switch(scr->gscreen->depth){
246 default:
247 x = 0x0F;
248 break;
249 case 8:
250 x = 0xFF;
251 break;
252 }
253 p &= x;
254
255 lock(&cursor);
256 *pr = scr->colormap[p][0];
257 *pg = scr->colormap[p][1];
258 *pb = scr->colormap[p][2];
259 unlock(&cursor);
260 }
261
262 int
setpalette(ulong p,ulong r,ulong g,ulong b)263 setpalette(ulong p, ulong r, ulong g, ulong b)
264 {
265 VGAscr *scr;
266 int d;
267
268 scr = &vgascreen[0];
269 d = scr->palettedepth;
270
271 lock(&cursor);
272 scr->colormap[p][0] = r;
273 scr->colormap[p][1] = g;
274 scr->colormap[p][2] = b;
275 vgao(PaddrW, p);
276 vgao(Pdata, r>>(32-d));
277 vgao(Pdata, g>>(32-d));
278 vgao(Pdata, b>>(32-d));
279 unlock(&cursor);
280
281 return ~0;
282 }
283
284 /*
285 * On some video cards (e.g. Mach64), the palette is used as the
286 * DAC registers for >8-bit modes. We don't want to set them when the user
287 * is trying to set a colormap and the card is in one of these modes.
288 */
289 int
setcolor(ulong p,ulong r,ulong g,ulong b)290 setcolor(ulong p, ulong r, ulong g, ulong b)
291 {
292 VGAscr *scr;
293 int x;
294
295 scr = &vgascreen[0];
296 if(scr->gscreen == nil)
297 return 0;
298
299 switch(scr->gscreen->depth){
300 case 1:
301 case 2:
302 case 4:
303 x = 0x0F;
304 break;
305 case 8:
306 x = 0xFF;
307 break;
308 default:
309 return 0;
310 }
311 p &= x;
312
313 return setpalette(p, r, g, b);
314 }
315
316 int
cursoron(int dolock)317 cursoron(int dolock)
318 {
319 VGAscr *scr;
320 int v;
321
322 scr = &vgascreen[0];
323 if(scr->cur == nil || scr->cur->move == nil)
324 return 0;
325
326 if(dolock)
327 lock(&cursor);
328 v = scr->cur->move(scr, mousexy());
329 if(dolock)
330 unlock(&cursor);
331
332 return v;
333 }
334
335 void
cursoroff(int)336 cursoroff(int)
337 {
338 }
339
340 void
setcursor(Cursor * curs)341 setcursor(Cursor* curs)
342 {
343 VGAscr *scr;
344
345 scr = &vgascreen[0];
346 if(scr->cur == nil || scr->cur->load == nil)
347 return;
348
349 scr->cur->load(scr, curs);
350 }
351
352 int hwaccel = 1;
353 int hwblank = 0; /* turned on by drivers that are known good */
354 int panning = 0;
355
356 int
hwdraw(Memdrawparam * par)357 hwdraw(Memdrawparam *par)
358 {
359 VGAscr *scr;
360 Memimage *dst, *src, *mask;
361 int m;
362
363 if(hwaccel == 0)
364 return 0;
365
366 scr = &vgascreen[0];
367 if((dst=par->dst) == nil || dst->data == nil)
368 return 0;
369 if((src=par->src) == nil || src->data == nil)
370 return 0;
371 if((mask=par->mask) == nil || mask->data == nil)
372 return 0;
373
374 if(scr->cur == &swcursor){
375 /*
376 * always calling swcursorhide here doesn't cure
377 * leaving cursor tracks nor failing to refresh menus
378 * with the latest libmemdraw/draw.c.
379 */
380 if(dst->data->bdata == gscreendata.bdata)
381 swcursoravoid(par->r);
382 if(src->data->bdata == gscreendata.bdata)
383 swcursoravoid(par->sr);
384 if(mask->data->bdata == gscreendata.bdata)
385 swcursoravoid(par->mr);
386 }
387
388 if(dst->data->bdata != gscreendata.bdata)
389 return 0;
390
391 if(scr->fill==nil && scr->scroll==nil)
392 return 0;
393
394 /*
395 * If we have an opaque mask and source is one opaque
396 * pixel we can convert to the destination format and just
397 * replicate with memset.
398 */
399 m = Simplesrc|Simplemask|Fullmask;
400 if(scr->fill
401 && (par->state&m)==m
402 && ((par->srgba&0xFF) == 0xFF)
403 && (par->op&S) == S)
404 return scr->fill(scr, par->r, par->sdval);
405
406 /*
407 * If no source alpha, an opaque mask, we can just copy the
408 * source onto the destination. If the channels are the same and
409 * the source is not replicated, memmove suffices.
410 */
411 m = Simplemask|Fullmask;
412 if(scr->scroll
413 && src->data->bdata==dst->data->bdata
414 && !(src->flags&Falpha)
415 && (par->state&m)==m
416 && (par->op&S) == S)
417 return scr->scroll(scr, par->r, par->sr);
418
419 return 0;
420 }
421
422 void
blankscreen(int blank)423 blankscreen(int blank)
424 {
425 VGAscr *scr;
426
427 scr = &vgascreen[0];
428 if(hwblank){
429 if(scr->blank)
430 scr->blank(scr, blank);
431 else
432 vgablank(scr, blank);
433 }
434 }
435
436 void
vgalinearpciid(VGAscr * scr,int vid,int did)437 vgalinearpciid(VGAscr *scr, int vid, int did)
438 {
439 Pcidev *p;
440
441 p = nil;
442 while((p = pcimatch(p, vid, 0)) != nil){
443 if(p->ccrb != 3) /* video card */
444 continue;
445 if(did != 0 && p->did != did)
446 continue;
447 break;
448 }
449 if(p == nil)
450 error("pci video card not found");
451
452 scr->pci = p;
453 vgalinearpci(scr);
454 }
455
456 void
vgalinearpci(VGAscr * scr)457 vgalinearpci(VGAscr *scr)
458 {
459 ulong paddr;
460 int i, size, best;
461 Pcidev *p;
462
463 p = scr->pci;
464 if(p == nil)
465 return;
466
467 /*
468 * Scan for largest memory region on card.
469 * Some S3 cards (e.g. Savage) have enormous
470 * mmio regions (but even larger frame buffers).
471 * Some 3dfx cards (e.g., Voodoo3) have mmio
472 * buffers the same size as the frame buffer,
473 * but only the frame buffer is marked as
474 * prefetchable (bar&8). If a card doesn't fit
475 * into these heuristics, its driver will have to
476 * call vgalinearaddr directly.
477 */
478 best = -1;
479 for(i=0; i<nelem(p->mem); i++){
480 if(p->mem[i].bar&1) /* not memory */
481 continue;
482 if(p->mem[i].size < 640*480) /* not big enough */
483 continue;
484 if(best==-1
485 || p->mem[i].size > p->mem[best].size
486 || (p->mem[i].size == p->mem[best].size
487 && (p->mem[i].bar&8)
488 && !(p->mem[best].bar&8)))
489 best = i;
490 }
491 if(best >= 0){
492 paddr = p->mem[best].bar & ~0x0F;
493 size = p->mem[best].size;
494 vgalinearaddr(scr, paddr, size);
495 return;
496 }
497 error("no video memory found on pci card");
498 }
499
500 void
vgalinearaddr(VGAscr * scr,ulong paddr,int size)501 vgalinearaddr(VGAscr *scr, ulong paddr, int size)
502 {
503 int x, nsize;
504 ulong npaddr;
505
506 /*
507 * new approach. instead of trying to resize this
508 * later, let's assume that we can just allocate the
509 * entire window to start with.
510 */
511
512 if(scr->paddr == paddr && size <= scr->apsize)
513 return;
514
515 if(scr->paddr){
516 /*
517 * could call vunmap and vmap,
518 * but worried about dangling pointers in devdraw
519 */
520 error("cannot grow vga frame buffer");
521 }
522
523 /* round to page boundary, just in case */
524 x = paddr&(BY2PG-1);
525 npaddr = paddr-x;
526 nsize = PGROUND(size+x);
527
528 /*
529 * Don't bother trying to map more than 4000x4000x32 = 64MB.
530 * We only have a 256MB window.
531 */
532 if(nsize > 64*MB)
533 nsize = 64*MB;
534 scr->vaddr = vmap(npaddr, nsize);
535 if(scr->vaddr == 0)
536 error("cannot allocate vga frame buffer");
537 scr->vaddr = (char*)scr->vaddr+x;
538 scr->paddr = paddr;
539 scr->apsize = nsize;
540 /* let mtrr harmlessly fail on old CPUs, e.g., P54C */
541 if(!waserror()){
542 mtrr(npaddr, nsize, "wc");
543 poperror();
544 }
545 }
546
547
548 /*
549 * Software cursor.
550 */
551 int swvisible; /* is the cursor visible? */
552 int swenabled; /* is the cursor supposed to be on the screen? */
553 Memimage* swback; /* screen under cursor */
554 Memimage* swimg; /* cursor image */
555 Memimage* swmask; /* cursor mask */
556 Memimage* swimg1;
557 Memimage* swmask1;
558
559 Point swoffset;
560 Rectangle swrect; /* screen rectangle in swback */
561 Point swpt; /* desired cursor location */
562 Point swvispt; /* actual cursor location */
563 int swvers; /* incremented each time cursor image changes */
564 int swvisvers; /* the version on the screen */
565
566 /*
567 * called with drawlock locked for us, most of the time.
568 * kernel prints at inopportune times might mean we don't
569 * hold the lock, but memimagedraw is now reentrant so
570 * that should be okay: worst case we get cursor droppings.
571 */
572 void
swcursorhide(void)573 swcursorhide(void)
574 {
575 if(swvisible == 0)
576 return;
577 if(swback == nil)
578 return;
579 swvisible = 0;
580 memimagedraw(gscreen, swrect, swback, ZP, memopaque, ZP, S);
581 flushmemscreen(swrect);
582 }
583
584 void
swcursoravoid(Rectangle r)585 swcursoravoid(Rectangle r)
586 {
587 if(swvisible && rectXrect(r, swrect))
588 swcursorhide();
589 }
590
591 void
swcursordraw(void)592 swcursordraw(void)
593 {
594 if(swvisible)
595 return;
596 if(swenabled == 0)
597 return;
598 if(swback == nil || swimg1 == nil || swmask1 == nil)
599 return;
600 assert(!canqlock(&drawlock));
601 swvispt = swpt;
602 swvisvers = swvers;
603 swrect = rectaddpt(Rect(0,0,16,16), swvispt);
604 memimagedraw(swback, swback->r, gscreen, swpt, memopaque, ZP, S);
605 memimagedraw(gscreen, swrect, swimg1, ZP, swmask1, ZP, SoverD);
606 flushmemscreen(swrect);
607 swvisible = 1;
608 }
609
610 /*
611 * Need to lock drawlock for ourselves.
612 */
613 void
swenable(VGAscr *)614 swenable(VGAscr*)
615 {
616 swenabled = 1;
617 if(canqlock(&drawlock)){
618 swcursordraw();
619 qunlock(&drawlock);
620 }
621 }
622
623 void
swdisable(VGAscr *)624 swdisable(VGAscr*)
625 {
626 swenabled = 0;
627 if(canqlock(&drawlock)){
628 swcursorhide();
629 qunlock(&drawlock);
630 }
631 }
632
633 void
swload(VGAscr *,Cursor * curs)634 swload(VGAscr*, Cursor *curs)
635 {
636 uchar *ip, *mp;
637 int i, j, set, clr;
638
639 if(!swimg || !swmask || !swimg1 || !swmask1)
640 return;
641 /*
642 * Build cursor image and mask.
643 * Image is just the usual cursor image
644 * but mask is a transparent alpha mask.
645 *
646 * The 16x16x8 memimages do not have
647 * padding at the end of their scan lines.
648 */
649 ip = byteaddr(swimg, ZP);
650 mp = byteaddr(swmask, ZP);
651 for(i=0; i<32; i++){
652 set = curs->set[i];
653 clr = curs->clr[i];
654 for(j=0x80; j; j>>=1){
655 *ip++ = set&j ? 0x00 : 0xFF;
656 *mp++ = (clr|set)&j ? 0xFF : 0x00;
657 }
658 }
659 swoffset = curs->offset;
660 swvers++;
661 memimagedraw(swimg1, swimg1->r, swimg, ZP, memopaque, ZP, S);
662 memimagedraw(swmask1, swmask1->r, swmask, ZP, memopaque, ZP, S);
663 }
664
665 int
swmove(VGAscr *,Point p)666 swmove(VGAscr*, Point p)
667 {
668 swpt = addpt(p, swoffset);
669 return 0;
670 }
671
672 void
swcursorclock(void)673 swcursorclock(void)
674 {
675 int x;
676
677 if(!swenabled)
678 return;
679 if(swvisible && eqpt(swpt, swvispt) && swvers==swvisvers)
680 return;
681
682 x = splhi();
683 if(swenabled)
684 if(!swvisible || !eqpt(swpt, swvispt) || swvers!=swvisvers)
685 if(canqlock(&drawlock)){
686 swcursorhide();
687 swcursordraw();
688 qunlock(&drawlock);
689 }
690 splx(x);
691 }
692
693 void
swcursorinit(void)694 swcursorinit(void)
695 {
696 static int init, warned;
697 VGAscr *scr;
698
699 didswcursorinit = 1;
700 if(!init){
701 init = 1;
702 addclock0link(swcursorclock, 10);
703 }
704 scr = &vgascreen[0];
705 if(scr==nil || scr->gscreen==nil)
706 return;
707
708 if(scr->dev == nil || scr->dev->linear == nil){
709 if(!warned){
710 print("cannot use software cursor on non-linear vga screen\n");
711 warned = 1;
712 }
713 return;
714 }
715
716 if(swback){
717 freememimage(swback);
718 freememimage(swmask);
719 freememimage(swmask1);
720 freememimage(swimg);
721 freememimage(swimg1);
722 }
723
724 swback = allocmemimage(Rect(0,0,32,32), gscreen->chan);
725 swmask = allocmemimage(Rect(0,0,16,16), GREY8);
726 swmask1 = allocmemimage(Rect(0,0,16,16), GREY1);
727 swimg = allocmemimage(Rect(0,0,16,16), GREY8);
728 swimg1 = allocmemimage(Rect(0,0,16,16), GREY1);
729 if(swback==nil || swmask==nil || swmask1==nil || swimg==nil || swimg1 == nil){
730 print("software cursor: allocmemimage fails");
731 return;
732 }
733
734 memfillcolor(swmask, DOpaque);
735 memfillcolor(swmask1, DOpaque);
736 memfillcolor(swimg, DBlack);
737 memfillcolor(swimg1, DBlack);
738 }
739
740 VGAcur swcursor =
741 {
742 "soft",
743 swenable,
744 swdisable,
745 swload,
746 swmove,
747 };
748
749