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