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