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