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