VGA: Use struct for car/attr/WITH_ATTR.
[seabios.git] / vgasrc / vgafb.c
1 // Code for manipulating VGA framebuffers.
2 //
3 // Copyright (C) 2009  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2001-2008 the LGPL VGABios developers Team
5 //
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
7
8 #include "biosvar.h" // GET_BDA
9 #include "util.h" // memset_far
10 #include "vgatables.h" // find_vga_entry
11
12 // TODO
13 //  * extract hw code from framebuffer code
14 //  * use clear_screen() in scroll code
15 //  * read/write_char should take a position; should not take count
16 //  * remove vmode_g->class (integrate into vmode_g->memmodel)
17 //  * normalize params (don't use AX/BX/CX/etc.)
18
19 // XXX
20 inline void
21 memcpy16_far(u16 d_seg, void *d_far, u16 s_seg, const void *s_far, size_t len)
22 {
23     memcpy_far(d_seg, d_far, s_seg, s_far, len);
24 }
25
26
27 /****************************************************************
28  * Screen scrolling
29  ****************************************************************/
30
31 static void
32 vgamem_copy_pl4(u8 xstart, u8 ysrc, u8 ydest, u8 cols, u8 nbcols,
33                 u8 cheight)
34 {
35     u16 src = ysrc * cheight * nbcols + xstart;
36     u16 dest = ydest * cheight * nbcols + xstart;
37     outw(0x0105, VGAREG_GRDC_ADDRESS);
38     u8 i;
39     for (i = 0; i < cheight; i++)
40         memcpy_far(SEG_GRAPH, (void*)(dest + i * nbcols)
41                    , SEG_GRAPH, (void*)(src + i * nbcols), cols);
42     outw(0x0005, VGAREG_GRDC_ADDRESS);
43 }
44
45 static void
46 vgamem_fill_pl4(u8 xstart, u8 ystart, u8 cols, u8 nbcols, u8 cheight,
47                 u8 attr)
48 {
49     u16 dest = ystart * cheight * nbcols + xstart;
50     outw(0x0205, VGAREG_GRDC_ADDRESS);
51     u8 i;
52     for (i = 0; i < cheight; i++)
53         memset_far(SEG_GRAPH, (void*)(dest + i * nbcols), attr, cols);
54     outw(0x0005, VGAREG_GRDC_ADDRESS);
55 }
56
57 static void
58 vgamem_copy_cga(u8 xstart, u8 ysrc, u8 ydest, u8 cols, u8 nbcols,
59                 u8 cheight)
60 {
61     u16 src = ((ysrc * cheight * nbcols) >> 1) + xstart;
62     u16 dest = ((ydest * cheight * nbcols) >> 1) + xstart;
63     u8 i;
64     for (i = 0; i < cheight; i++)
65         if (i & 1)
66             memcpy_far(SEG_CTEXT, (void*)(0x2000 + dest + (i >> 1) * nbcols)
67                        , SEG_CTEXT, (void*)(0x2000 + src + (i >> 1) * nbcols)
68                        , cols);
69         else
70             memcpy_far(SEG_CTEXT, (void*)(dest + (i >> 1) * nbcols)
71                        , SEG_CTEXT, (void*)(src + (i >> 1) * nbcols), cols);
72 }
73
74 static void
75 vgamem_fill_cga(u8 xstart, u8 ystart, u8 cols, u8 nbcols, u8 cheight,
76                 u8 attr)
77 {
78     u16 dest = ((ystart * cheight * nbcols) >> 1) + xstart;
79     u8 i;
80     for (i = 0; i < cheight; i++)
81         if (i & 1)
82             memset_far(SEG_CTEXT, (void*)(0x2000 + dest + (i >> 1) * nbcols)
83                        , attr, cols);
84         else
85             memset_far(SEG_CTEXT, (void*)(dest + (i >> 1) * nbcols), attr, cols);
86 }
87
88 void
89 clear_screen(struct vgamode_s *vmode_g)
90 {
91     if (GET_GLOBAL(vmode_g->class) == TEXT) {
92         memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0720, 32*1024);
93         return;
94     }
95     if (GET_GLOBAL(vmode_g->svgamode) < 0x0d) {
96         memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 32*1024);
97         return;
98     }
99     outb(0x02, VGAREG_SEQU_ADDRESS);
100     u8 mmask = inb(VGAREG_SEQU_DATA);
101     outb(0x0f, VGAREG_SEQU_DATA);   // all planes
102     memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 64*1024);
103     outb(mmask, VGAREG_SEQU_DATA);
104 }
105
106 void
107 biosfn_scroll(u8 nblines, u8 attr, u8 rul, u8 cul, u8 rlr, u8 clr, u8 page,
108               u8 dir)
109 {
110     // page == 0xFF if current
111     if (rul > rlr)
112         return;
113     if (cul > clr)
114         return;
115
116     // Get the mode
117     struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
118     if (!vmode_g)
119         return;
120
121     // Get the dimensions
122     u16 nbrows = GET_BDA(video_rows) + 1;
123     u16 nbcols = GET_BDA(video_cols);
124
125     // Get the current page
126     if (page == 0xFF)
127         page = GET_BDA(video_page);
128
129     if (rlr >= nbrows)
130         rlr = nbrows - 1;
131     if (clr >= nbcols)
132         clr = nbcols - 1;
133     if (nblines > nbrows)
134         nblines = 0;
135     u8 cols = clr - cul + 1;
136
137     if (GET_GLOBAL(vmode_g->class) == TEXT) {
138         // Compute the address
139         void *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, page));
140         dprintf(3, "Scroll, address %p (%d %d %02x)\n"
141                 , address_far, nbrows, nbcols, page);
142
143         if (nblines == 0 && rul == 0 && cul == 0 && rlr == nbrows - 1
144             && clr == nbcols - 1) {
145             memset16_far(GET_GLOBAL(vmode_g->sstart), address_far
146                          , (u16)attr * 0x100 + ' ', nbrows * nbcols * 2);
147         } else {                // if Scroll up
148             if (dir == SCROLL_UP) {
149                 u16 i;
150                 for (i = rul; i <= rlr; i++)
151                     if ((i + nblines > rlr) || (nblines == 0))
152                         memset16_far(GET_GLOBAL(vmode_g->sstart)
153                                      , address_far + (i * nbcols + cul) * 2
154                                      , (u16)attr * 0x100 + ' ', cols * 2);
155                     else
156                         memcpy16_far(GET_GLOBAL(vmode_g->sstart)
157                                      , address_far + (i * nbcols + cul) * 2
158                                      , GET_GLOBAL(vmode_g->sstart)
159                                      , (void*)(((i + nblines) * nbcols + cul) * 2)
160                                      , cols * 2);
161             } else {
162                 u16 i;
163                 for (i = rlr; i >= rul; i--) {
164                     if ((i < rul + nblines) || (nblines == 0))
165                         memset16_far(GET_GLOBAL(vmode_g->sstart)
166                                      , address_far + (i * nbcols + cul) * 2
167                                      , (u16)attr * 0x100 + ' ', cols * 2);
168                     else
169                         memcpy16_far(GET_GLOBAL(vmode_g->sstart)
170                                      , address_far + (i * nbcols + cul) * 2
171                                      , GET_GLOBAL(vmode_g->sstart)
172                                      , (void*)(((i - nblines) * nbcols + cul) * 2)
173                                      , cols * 2);
174                     if (i > rlr)
175                         break;
176                 }
177             }
178         }
179         return;
180     }
181
182     // FIXME gfx mode not complete
183     struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
184     u8 cheight = GET_GLOBAL(vparam_g->cheight);
185     switch (GET_GLOBAL(vmode_g->memmodel)) {
186     case PLANAR4:
187     case PLANAR1:
188         if (nblines == 0 && rul == 0 && cul == 0 && rlr == nbrows - 1
189             && clr == nbcols - 1) {
190             outw(0x0205, VGAREG_GRDC_ADDRESS);
191             memset_far(GET_GLOBAL(vmode_g->sstart), 0, attr,
192                        nbrows * nbcols * cheight);
193             outw(0x0005, VGAREG_GRDC_ADDRESS);
194         } else {            // if Scroll up
195             if (dir == SCROLL_UP) {
196                 u16 i;
197                 for (i = rul; i <= rlr; i++)
198                     if ((i + nblines > rlr) || (nblines == 0))
199                         vgamem_fill_pl4(cul, i, cols, nbcols, cheight,
200                                         attr);
201                     else
202                         vgamem_copy_pl4(cul, i + nblines, i, cols,
203                                         nbcols, cheight);
204             } else {
205                 u16 i;
206                 for (i = rlr; i >= rul; i--) {
207                     if ((i < rul + nblines) || (nblines == 0))
208                         vgamem_fill_pl4(cul, i, cols, nbcols, cheight,
209                                         attr);
210                     else
211                         vgamem_copy_pl4(cul, i, i - nblines, cols,
212                                         nbcols, cheight);
213                     if (i > rlr)
214                         break;
215                 }
216             }
217         }
218         break;
219     case CGA: {
220         u8 bpp = GET_GLOBAL(vmode_g->pixbits);
221         if (nblines == 0 && rul == 0 && cul == 0 && rlr == nbrows - 1
222             && clr == nbcols - 1) {
223             memset_far(GET_GLOBAL(vmode_g->sstart), 0, attr,
224                        nbrows * nbcols * cheight * bpp);
225         } else {
226             if (bpp == 2) {
227                 cul <<= 1;
228                 cols <<= 1;
229                 nbcols <<= 1;
230             }
231             // if Scroll up
232             if (dir == SCROLL_UP) {
233                 u16 i;
234                 for (i = rul; i <= rlr; i++)
235                     if ((i + nblines > rlr) || (nblines == 0))
236                         vgamem_fill_cga(cul, i, cols, nbcols, cheight,
237                                         attr);
238                     else
239                         vgamem_copy_cga(cul, i + nblines, i, cols,
240                                         nbcols, cheight);
241             } else {
242                 u16 i;
243                 for (i = rlr; i >= rul; i--) {
244                     if ((i < rul + nblines) || (nblines == 0))
245                         vgamem_fill_cga(cul, i, cols, nbcols, cheight,
246                                         attr);
247                     else
248                         vgamem_copy_cga(cul, i, i - nblines, cols,
249                                         nbcols, cheight);
250                     if (i > rlr)
251                         break;
252                 }
253             }
254         }
255         break;
256     }
257     default:
258         dprintf(1, "Scroll in graphics mode\n");
259     }
260 }
261
262
263 /****************************************************************
264  * Read/write characters to screen
265  ****************************************************************/
266
267 static void
268 write_gfx_char_pl4(struct cursorpos cp, struct carattr ca, u8 nbcols, u8 cheight)
269 {
270     u8 *fdata_g;
271     switch (cheight) {
272     case 14:
273         fdata_g = vgafont14;
274         break;
275     case 16:
276         fdata_g = vgafont16;
277         break;
278     default:
279         fdata_g = vgafont8;
280     }
281     u16 addr = cp.x + cp.y * cheight * nbcols;
282     u16 src = ca.car * cheight;
283     outw(0x0f02, VGAREG_SEQU_ADDRESS);
284     outw(0x0205, VGAREG_GRDC_ADDRESS);
285     if (ca.attr & 0x80)
286         outw(0x1803, VGAREG_GRDC_ADDRESS);
287     else
288         outw(0x0003, VGAREG_GRDC_ADDRESS);
289     u8 i;
290     for (i = 0; i < cheight; i++) {
291         u8 *dest_far = (void*)(addr + i * nbcols);
292         u8 j;
293         for (j = 0; j < 8; j++) {
294             u8 mask = 0x80 >> j;
295             outw((mask << 8) | 0x08, VGAREG_GRDC_ADDRESS);
296             GET_FARVAR(SEG_GRAPH, *dest_far);
297             if (GET_GLOBAL(fdata_g[src + i]) & mask)
298                 SET_FARVAR(SEG_GRAPH, *dest_far, ca.attr & 0x0f);
299             else
300                 SET_FARVAR(SEG_GRAPH, *dest_far, 0x00);
301         }
302     }
303     outw(0xff08, VGAREG_GRDC_ADDRESS);
304     outw(0x0005, VGAREG_GRDC_ADDRESS);
305     outw(0x0003, VGAREG_GRDC_ADDRESS);
306 }
307
308 static void
309 write_gfx_char_cga(struct cursorpos cp, struct carattr ca, u8 nbcols, u8 bpp)
310 {
311     u8 *fdata_g = vgafont8;
312     u16 addr = (cp.x * bpp) + cp.y * 320;
313     u16 src = ca.car * 8;
314     u8 i;
315     for (i = 0; i < 8; i++) {
316         u8 *dest_far = (void*)(addr + (i >> 1) * 80);
317         if (i & 1)
318             dest_far += 0x2000;
319         u8 mask = 0x80;
320         if (bpp == 1) {
321             u8 data = 0;
322             if (ca.attr & 0x80)
323                 data = GET_FARVAR(SEG_CTEXT, *dest_far);
324             u8 j;
325             for (j = 0; j < 8; j++) {
326                 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
327                     if (ca.attr & 0x80)
328                         data ^= (ca.attr & 0x01) << (7 - j);
329                     else
330                         data |= (ca.attr & 0x01) << (7 - j);
331                 }
332                 mask >>= 1;
333             }
334             SET_FARVAR(SEG_CTEXT, *dest_far, data);
335         } else {
336             while (mask > 0) {
337                 u8 data = 0;
338                 if (ca.attr & 0x80)
339                     data = GET_FARVAR(SEG_CTEXT, *dest_far);
340                 u8 j;
341                 for (j = 0; j < 4; j++) {
342                     if (GET_GLOBAL(fdata_g[src + i]) & mask) {
343                         if (ca.attr & 0x80)
344                             data ^= (ca.attr & 0x03) << ((3 - j) * 2);
345                         else
346                             data |= (ca.attr & 0x03) << ((3 - j) * 2);
347                     }
348                     mask >>= 1;
349                 }
350                 SET_FARVAR(SEG_CTEXT, *dest_far, data);
351                 dest_far += 1;
352             }
353         }
354     }
355 }
356
357 static void
358 write_gfx_char_lin(struct cursorpos cp, struct carattr ca, u8 nbcols)
359 {
360     u8 *fdata_g = vgafont8;
361     u16 addr = cp.x * 8 + cp.y * nbcols * 64;
362     u16 src = ca.car * 8;
363     u8 i;
364     for (i = 0; i < 8; i++) {
365         u8 *dest_far = (void*)(addr + i * nbcols * 8);
366         u8 mask = 0x80;
367         u8 j;
368         for (j = 0; j < 8; j++) {
369             u8 data = 0x00;
370             if (GET_GLOBAL(fdata_g[src + i]) & mask)
371                 data = ca.attr;
372             SET_FARVAR(SEG_GRAPH, dest_far[j], data);
373             mask >>= 1;
374         }
375     }
376 }
377
378 void
379 vgafb_write_char(u8 page, struct carattr ca, u16 count)
380 {
381     // Get the mode
382     struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
383     if (!vmode_g)
384         return;
385
386     // Get the cursor pos for the page
387     struct cursorpos cp = get_cursor_pos(page);
388
389     // Get the dimensions
390     u16 nbrows = GET_BDA(video_rows) + 1;
391     u16 nbcols = GET_BDA(video_cols);
392
393     if (GET_GLOBAL(vmode_g->class) == TEXT) {
394         // Compute the address
395         void *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, cp.page)
396                                     + (cp.x + cp.y * nbcols) * 2);
397
398         if (ca.use_attr) {
399             u16 dummy = (ca.attr << 8) | ca.car;
400             memset16_far(GET_GLOBAL(vmode_g->sstart), address_far, dummy
401                          , count * 2);
402         } else {
403             while (count-- > 0) {
404                 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)address_far
405                            , ca.car);
406                 address_far += 2;
407             }
408         }
409         return;
410     }
411
412     // FIXME gfx mode not complete
413     struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
414     u8 cheight = GET_GLOBAL(vparam_g->cheight);
415     u8 bpp = GET_GLOBAL(vmode_g->pixbits);
416     while ((count-- > 0) && (cp.x < nbcols)) {
417         switch (GET_GLOBAL(vmode_g->memmodel)) {
418         case PLANAR4:
419         case PLANAR1:
420             write_gfx_char_pl4(cp, ca, nbcols, cheight);
421             break;
422         case CGA:
423             write_gfx_char_cga(cp, ca, nbcols, bpp);
424             break;
425         case LINEAR8:
426             write_gfx_char_lin(cp, ca, nbcols);
427             break;
428         }
429         cp.x++;
430     }
431 }
432
433 struct carattr
434 vgafb_read_char(u8 page)
435 {
436     // Get the mode
437     struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
438     if (!vmode_g)
439         goto fail;
440
441     // Get the cursor pos for the page
442     struct cursorpos cp = get_cursor_pos(page);
443
444     // Get the dimensions
445     u16 nbrows = GET_BDA(video_rows) + 1;
446     u16 nbcols = GET_BDA(video_cols);
447
448     if (GET_GLOBAL(vmode_g->class) == TEXT) {
449         // Compute the address
450         u16 *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, cp.page)
451                                    + (cp.x + cp.y * nbcols) * 2);
452         u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *address_far);
453         struct carattr ca = {v, v>>8, 0};
454         return ca;
455     }
456
457     // FIXME gfx mode
458     dprintf(1, "Read char in graphics mode\n");
459 fail: ;
460     struct carattr ca = {0, 0, 0};
461     return ca;
462 }
463
464
465 /****************************************************************
466  * Read/write pixels
467  ****************************************************************/
468
469 void
470 biosfn_write_pixel(u8 BH, u8 AL, u16 CX, u16 DX)
471 {
472     // Get the mode
473     struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
474     if (!vmode_g)
475         return;
476     if (GET_GLOBAL(vmode_g->class) == TEXT)
477         return;
478
479     u8 *addr_far, mask, attr, data;
480     switch (GET_GLOBAL(vmode_g->memmodel)) {
481     case PLANAR4:
482     case PLANAR1:
483         addr_far = (void*)(CX / 8 + DX * GET_BDA(video_cols));
484         mask = 0x80 >> (CX & 0x07);
485         outw((mask << 8) | 0x08, VGAREG_GRDC_ADDRESS);
486         outw(0x0205, VGAREG_GRDC_ADDRESS);
487         data = GET_FARVAR(SEG_GRAPH, *addr_far);
488         if (AL & 0x80)
489             outw(0x1803, VGAREG_GRDC_ADDRESS);
490         SET_FARVAR(SEG_GRAPH, *addr_far, AL);
491         outw(0xff08, VGAREG_GRDC_ADDRESS);
492         outw(0x0005, VGAREG_GRDC_ADDRESS);
493         outw(0x0003, VGAREG_GRDC_ADDRESS);
494         break;
495     case CGA:
496         if (GET_GLOBAL(vmode_g->pixbits) == 2)
497             addr_far = (void*)((CX >> 2) + (DX >> 1) * 80);
498         else
499             addr_far = (void*)((CX >> 3) + (DX >> 1) * 80);
500         if (DX & 1)
501             addr_far += 0x2000;
502         data = GET_FARVAR(SEG_CTEXT, *addr_far);
503         if (GET_GLOBAL(vmode_g->pixbits) == 2) {
504             attr = (AL & 0x03) << ((3 - (CX & 0x03)) * 2);
505             mask = 0x03 << ((3 - (CX & 0x03)) * 2);
506         } else {
507             attr = (AL & 0x01) << (7 - (CX & 0x07));
508             mask = 0x01 << (7 - (CX & 0x07));
509         }
510         if (AL & 0x80) {
511             data ^= attr;
512         } else {
513             data &= ~mask;
514             data |= attr;
515         }
516         SET_FARVAR(SEG_CTEXT, *addr_far, data);
517         break;
518     case LINEAR8:
519         addr_far = (void*)(CX + DX * (GET_BDA(video_cols) * 8));
520         SET_FARVAR(SEG_GRAPH, *addr_far, AL);
521         break;
522     }
523 }
524
525 void
526 biosfn_read_pixel(u8 BH, u16 CX, u16 DX, u16 *AX)
527 {
528     // Get the mode
529     struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
530     if (!vmode_g)
531         return;
532     if (GET_GLOBAL(vmode_g->class) == TEXT)
533         return;
534
535     u8 *addr_far, mask, attr=0, data, i;
536     switch (GET_GLOBAL(vmode_g->memmodel)) {
537     case PLANAR4:
538     case PLANAR1:
539         addr_far = (void*)(CX / 8 + DX * GET_BDA(video_cols));
540         mask = 0x80 >> (CX & 0x07);
541         attr = 0x00;
542         for (i = 0; i < 4; i++) {
543             outw((i << 8) | 0x04, VGAREG_GRDC_ADDRESS);
544             data = GET_FARVAR(SEG_GRAPH, *addr_far) & mask;
545             if (data > 0)
546                 attr |= (0x01 << i);
547         }
548         break;
549     case CGA:
550         addr_far = (void*)((CX >> 2) + (DX >> 1) * 80);
551         if (DX & 1)
552             addr_far += 0x2000;
553         data = GET_FARVAR(SEG_CTEXT, *addr_far);
554         if (GET_GLOBAL(vmode_g->pixbits) == 2)
555             attr = (data >> ((3 - (CX & 0x03)) * 2)) & 0x03;
556         else
557             attr = (data >> (7 - (CX & 0x07))) & 0x01;
558         break;
559     case LINEAR8:
560         addr_far = (void*)(CX + DX * (GET_BDA(video_cols) * 8));
561         attr = GET_FARVAR(SEG_GRAPH, *addr_far);
562         break;
563     }
564     *AX = (*AX & 0xff00) | attr;
565 }
566
567
568 /****************************************************************
569  * Font loading
570  ****************************************************************/
571
572 void
573 vgafb_load_font(u16 seg, void *src_far, u16 count
574                 , u16 start, u8 destflags, u8 fontsize)
575 {
576     get_font_access();
577     u16 blockaddr = ((destflags & 0x03) << 14) + ((destflags & 0x04) << 11);
578     void *dest_far = (void*)(blockaddr + start*32);
579     u16 i;
580     for (i = 0; i < count; i++)
581         memcpy_far(SEG_GRAPH, dest_far + i*32
582                    , seg, src_far + i*fontsize, fontsize);
583     release_font_access();
584 }