1 // Code for manipulating VGA framebuffers.
3 // Copyright (C) 2009 Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2001-2008 the LGPL VGABios developers Team
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
8 #include "biosvar.h" // GET_BDA
9 #include "util.h" // memset_far
10 #include "vgatables.h" // find_vga_entry
14 memcpy16_far(u16 d_seg, void *d_far, u16 s_seg, const void *s_far, size_t len)
16 memcpy_far(d_seg, d_far, s_seg, s_far, len);
20 /****************************************************************
22 ****************************************************************/
25 vgamem_copy_pl4(u8 xstart, u8 ysrc, u8 ydest, u8 cols, u8 nbcols,
28 u16 src = ysrc * cheight * nbcols + xstart;
29 u16 dest = ydest * cheight * nbcols + xstart;
30 outw(0x0105, VGAREG_GRDC_ADDRESS);
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);
39 vgamem_fill_pl4(u8 xstart, u8 ystart, u8 cols, u8 nbcols, u8 cheight,
42 u16 dest = ystart * cheight * nbcols + xstart;
43 outw(0x0205, VGAREG_GRDC_ADDRESS);
45 for (i = 0; i < cheight; i++)
46 memset_far(SEG_GRAPH, (void*)(dest + i * nbcols), attr, cols);
47 outw(0x0005, VGAREG_GRDC_ADDRESS);
51 vgamem_copy_cga(u8 xstart, u8 ysrc, u8 ydest, u8 cols, u8 nbcols,
54 u16 src = ((ysrc * cheight * nbcols) >> 1) + xstart;
55 u16 dest = ((ydest * cheight * nbcols) >> 1) + xstart;
57 for (i = 0; i < cheight; i++)
59 memcpy_far(SEG_CTEXT, (void*)(0x2000 + dest + (i >> 1) * nbcols)
60 , SEG_CTEXT, (void*)(0x2000 + src + (i >> 1) * nbcols)
63 memcpy_far(SEG_CTEXT, (void*)(dest + (i >> 1) * nbcols)
64 , SEG_CTEXT, (void*)(src + (i >> 1) * nbcols), cols);
68 vgamem_fill_cga(u8 xstart, u8 ystart, u8 cols, u8 nbcols, u8 cheight,
71 u16 dest = ((ystart * cheight * nbcols) >> 1) + xstart;
73 for (i = 0; i < cheight; i++)
75 memset_far(SEG_CTEXT, (void*)(0x2000 + dest + (i >> 1) * nbcols)
78 memset_far(SEG_CTEXT, (void*)(dest + (i >> 1) * nbcols), attr, cols);
82 clear_screen(struct vgamode_s *vmode_g)
84 if (GET_GLOBAL(vmode_g->class) == TEXT) {
85 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0720, 32*1024);
88 if (GET_GLOBAL(vmode_g->svgamode) < 0x0d) {
89 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 32*1024);
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);
100 biosfn_scroll(u8 nblines, u8 attr, u8 rul, u8 cul, u8 rlr, u8 clr, u8 page,
103 // page == 0xFF if current
110 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
114 // Get the dimensions
115 u16 nbrows = GET_BDA(video_rows) + 1;
116 u16 nbcols = GET_BDA(video_cols);
118 // Get the current page
120 page = GET_BDA(video_page);
126 if (nblines > nbrows)
128 u8 cols = clr - cul + 1;
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);
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) {
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);
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)
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);
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)
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)) {
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) {
190 for (i = rul; i <= rlr; i++)
191 if ((i + nblines > rlr) || (nblines == 0))
192 vgamem_fill_pl4(cul, i, cols, nbcols, cheight,
195 vgamem_copy_pl4(cul, i + nblines, i, cols,
199 for (i = rlr; i >= rul; i--) {
200 if ((i < rul + nblines) || (nblines == 0))
201 vgamem_fill_pl4(cul, i, cols, nbcols, cheight,
204 vgamem_copy_pl4(cul, i, i - nblines, cols,
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);
225 if (dir == SCROLL_UP) {
227 for (i = rul; i <= rlr; i++)
228 if ((i + nblines > rlr) || (nblines == 0))
229 vgamem_fill_cga(cul, i, cols, nbcols, cheight,
232 vgamem_copy_cga(cul, i + nblines, i, cols,
236 for (i = rlr; i >= rul; i--) {
237 if ((i < rul + nblines) || (nblines == 0))
238 vgamem_fill_cga(cul, i, cols, nbcols, cheight,
241 vgamem_copy_cga(cul, i, i - nblines, cols,
251 dprintf(1, "Scroll in graphics mode\n");
256 /****************************************************************
257 * Read/write characters to screen
258 ****************************************************************/
261 write_gfx_char_pl4(u8 car, u8 attr, u8 xcurs, u8 ycurs, u8 nbcols,
275 u16 addr = xcurs + ycurs * cheight * nbcols;
276 u16 src = car * cheight;
277 outw(0x0f02, VGAREG_SEQU_ADDRESS);
278 outw(0x0205, VGAREG_GRDC_ADDRESS);
280 outw(0x1803, VGAREG_GRDC_ADDRESS);
282 outw(0x0003, VGAREG_GRDC_ADDRESS);
284 for (i = 0; i < cheight; i++) {
285 u8 *dest_far = (void*)(addr + i * nbcols);
287 for (j = 0; j < 8; 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);
294 SET_FARVAR(SEG_GRAPH, *dest_far, 0x00);
297 outw(0xff08, VGAREG_GRDC_ADDRESS);
298 outw(0x0005, VGAREG_GRDC_ADDRESS);
299 outw(0x0003, VGAREG_GRDC_ADDRESS);
303 write_gfx_char_cga(u8 car, u8 attr, u8 xcurs, u8 ycurs, u8 nbcols, u8 bpp)
305 u8 *fdata_g = vgafont8;
306 u16 addr = (xcurs * bpp) + ycurs * 320;
309 for (i = 0; i < 8; i++) {
310 u8 *dest_far = (void*)(addr + (i >> 1) * 80);
317 data = GET_FARVAR(SEG_CTEXT, *dest_far);
319 for (j = 0; j < 8; j++) {
320 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
322 data ^= (attr & 0x01) << (7 - j);
324 data |= (attr & 0x01) << (7 - j);
328 SET_FARVAR(SEG_CTEXT, *dest_far, data);
333 data = GET_FARVAR(SEG_CTEXT, *dest_far);
335 for (j = 0; j < 4; j++) {
336 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
338 data ^= (attr & 0x03) << ((3 - j) * 2);
340 data |= (attr & 0x03) << ((3 - j) * 2);
344 SET_FARVAR(SEG_CTEXT, *dest_far, data);
352 write_gfx_char_lin(u8 car, u8 attr, u8 xcurs, u8 ycurs, u8 nbcols)
354 u8 *fdata_g = vgafont8;
355 u16 addr = xcurs * 8 + ycurs * nbcols * 64;
358 for (i = 0; i < 8; i++) {
359 u8 *dest_far = (void*)(addr + i * nbcols * 8);
362 for (j = 0; j < 8; j++) {
364 if (GET_GLOBAL(fdata_g[src + i]) & mask)
366 SET_FARVAR(SEG_GRAPH, dest_far[j], data);
373 biosfn_write_char_attr(u8 car, u8 page, u8 attr, u16 count)
376 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
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;
385 // Get the dimensions
386 u16 nbrows = GET_BDA(video_rows) + 1;
387 u16 nbcols = GET_BDA(video_cols);
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);
394 u16 dummy = ((u16)attr << 8) + car;
395 memset16_far(GET_GLOBAL(vmode_g->sstart), address_far, dummy, count * 2);
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)) {
407 write_gfx_char_pl4(car, attr, xcurs, ycurs, nbcols, cheight);
410 write_gfx_char_cga(car, attr, xcurs, ycurs, nbcols, bpp);
413 write_gfx_char_lin(car, attr, xcurs, ycurs, nbcols);
421 biosfn_write_char_only(u8 car, u8 page, u8 attr, u16 count)
424 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
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;
433 // Get the dimensions
434 u16 nbrows = GET_BDA(video_rows) + 1;
435 u16 nbcols = GET_BDA(video_cols);
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);
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)) {
456 write_gfx_char_pl4(car, attr, xcurs, ycurs, nbcols, cheight);
459 write_gfx_char_cga(car, attr, xcurs, ycurs, nbcols, bpp);
462 write_gfx_char_lin(car, attr, xcurs, ycurs, nbcols);
470 biosfn_read_char_attr(u8 page, u16 *car)
473 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
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;
482 // Get the dimensions
483 u16 nbrows = GET_BDA(video_rows) + 1;
484 u16 nbcols = GET_BDA(video_cols);
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);
491 *car = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *address_far);
494 dprintf(1, "Read char in graphics mode\n");
499 /****************************************************************
501 ****************************************************************/
504 biosfn_write_pixel(u8 BH, u8 AL, u16 CX, u16 DX)
507 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
510 if (GET_GLOBAL(vmode_g->class) == TEXT)
513 u8 *addr_far, mask, attr, data;
514 switch (GET_GLOBAL(vmode_g->memmodel)) {
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);
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);
530 if (GET_GLOBAL(vmode_g->pixbits) == 2)
531 addr_far = (void*)((CX >> 2) + (DX >> 1) * 80);
533 addr_far = (void*)((CX >> 3) + (DX >> 1) * 80);
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);
541 attr = (AL & 0x01) << (7 - (CX & 0x07));
542 mask = 0x01 << (7 - (CX & 0x07));
550 SET_FARVAR(SEG_CTEXT, *addr_far, data);
553 addr_far = (void*)(CX + DX * (GET_BDA(video_cols) * 8));
554 SET_FARVAR(SEG_GRAPH, *addr_far, AL);
560 biosfn_read_pixel(u8 BH, u16 CX, u16 DX, u16 *AX)
563 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
566 if (GET_GLOBAL(vmode_g->class) == TEXT)
569 u8 *addr_far, mask, attr=0, data, i;
570 switch (GET_GLOBAL(vmode_g->memmodel)) {
573 addr_far = (void*)(CX / 8 + DX * GET_BDA(video_cols));
574 mask = 0x80 >> (CX & 0x07);
576 for (i = 0; i < 4; i++) {
577 outw((i << 8) | 0x04, VGAREG_GRDC_ADDRESS);
578 data = GET_FARVAR(SEG_GRAPH, *addr_far) & mask;
584 addr_far = (void*)((CX >> 2) + (DX >> 1) * 80);
587 data = GET_FARVAR(SEG_CTEXT, *addr_far);
588 if (GET_GLOBAL(vmode_g->pixbits) == 2)
589 attr = (data >> ((3 - (CX & 0x03)) * 2)) & 0x03;
591 attr = (data >> (7 - (CX & 0x07))) & 0x01;
594 addr_far = (void*)(CX + DX * (GET_BDA(video_cols) * 8));
595 attr = GET_FARVAR(SEG_GRAPH, *addr_far);
598 *AX = (*AX & 0xff00) | attr;
602 /****************************************************************
604 ****************************************************************/
607 biosfn_load_text_user_pat(u16 ES, u16 BP, u16 CX, u16 DX, u8 BL, u8 BH)
610 u16 blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
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);
617 release_font_access();
621 biosfn_load_text_8_14_pat(u8 BL)
624 u16 blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
626 for (i = 0; i < 0x100; i++) {
628 void *dest_far = (void*)(blockaddr + i * 32);
629 memcpy_far(SEG_GRAPH, dest_far, get_global_seg(), &vgafont14[src], 14);
631 release_font_access();
635 biosfn_load_text_8_8_pat(u8 BL)
638 u16 blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
640 for (i = 0; i < 0x100; i++) {
642 void *dest_far = (void*)(blockaddr + i * 32);
643 memcpy_far(SEG_GRAPH, dest_far, get_global_seg(), &vgafont8[src], 8);
645 release_font_access();
649 biosfn_load_text_8_16_pat(u8 BL)
652 u16 blockaddr = ((BL & 0x03) << 14) + ((BL & 0x04) << 11);
654 for (i = 0; i < 0x100; i++) {
656 void *dest_far = (void*)(blockaddr + i * 32);
657 memcpy_far(SEG_GRAPH, dest_far, get_global_seg(), &vgafont16[src], 16);
659 release_font_access();