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