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