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