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
13 // * extract hw code from framebuffer code
14 // * use clear_screen() in scroll code
15 // * merge car/attr/with_attr into one param
16 // * combine biosfn_write_char_attr/_only()
17 // * read/write_char should take a position; should not take count
18 // * remove vmode_g->class (integrate into vmode_g->memmodel)
19 // * normalize params (don't use AX/BX/CX/etc.)
23 memcpy16_far(u16 d_seg, void *d_far, u16 s_seg, const void *s_far, size_t len)
25 memcpy_far(d_seg, d_far, s_seg, s_far, len);
29 /****************************************************************
31 ****************************************************************/
34 vgamem_copy_pl4(u8 xstart, u8 ysrc, u8 ydest, u8 cols, u8 nbcols,
37 u16 src = ysrc * cheight * nbcols + xstart;
38 u16 dest = ydest * cheight * nbcols + xstart;
39 outw(0x0105, VGAREG_GRDC_ADDRESS);
41 for (i = 0; i < cheight; i++)
42 memcpy_far(SEG_GRAPH, (void*)(dest + i * nbcols)
43 , SEG_GRAPH, (void*)(src + i * nbcols), cols);
44 outw(0x0005, VGAREG_GRDC_ADDRESS);
48 vgamem_fill_pl4(u8 xstart, u8 ystart, u8 cols, u8 nbcols, u8 cheight,
51 u16 dest = ystart * cheight * nbcols + xstart;
52 outw(0x0205, VGAREG_GRDC_ADDRESS);
54 for (i = 0; i < cheight; i++)
55 memset_far(SEG_GRAPH, (void*)(dest + i * nbcols), attr, cols);
56 outw(0x0005, VGAREG_GRDC_ADDRESS);
60 vgamem_copy_cga(u8 xstart, u8 ysrc, u8 ydest, u8 cols, u8 nbcols,
63 u16 src = ((ysrc * cheight * nbcols) >> 1) + xstart;
64 u16 dest = ((ydest * cheight * nbcols) >> 1) + xstart;
66 for (i = 0; i < cheight; i++)
68 memcpy_far(SEG_CTEXT, (void*)(0x2000 + dest + (i >> 1) * nbcols)
69 , SEG_CTEXT, (void*)(0x2000 + src + (i >> 1) * nbcols)
72 memcpy_far(SEG_CTEXT, (void*)(dest + (i >> 1) * nbcols)
73 , SEG_CTEXT, (void*)(src + (i >> 1) * nbcols), cols);
77 vgamem_fill_cga(u8 xstart, u8 ystart, u8 cols, u8 nbcols, u8 cheight,
80 u16 dest = ((ystart * cheight * nbcols) >> 1) + xstart;
82 for (i = 0; i < cheight; i++)
84 memset_far(SEG_CTEXT, (void*)(0x2000 + dest + (i >> 1) * nbcols)
87 memset_far(SEG_CTEXT, (void*)(dest + (i >> 1) * nbcols), attr, cols);
91 clear_screen(struct vgamode_s *vmode_g)
93 if (GET_GLOBAL(vmode_g->class) == TEXT) {
94 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0720, 32*1024);
97 if (GET_GLOBAL(vmode_g->svgamode) < 0x0d) {
98 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 32*1024);
101 outb(0x02, VGAREG_SEQU_ADDRESS);
102 u8 mmask = inb(VGAREG_SEQU_DATA);
103 outb(0x0f, VGAREG_SEQU_DATA); // all planes
104 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 64*1024);
105 outb(mmask, VGAREG_SEQU_DATA);
109 biosfn_scroll(u8 nblines, u8 attr, u8 rul, u8 cul, u8 rlr, u8 clr, u8 page,
112 // page == 0xFF if current
119 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
123 // Get the dimensions
124 u16 nbrows = GET_BDA(video_rows) + 1;
125 u16 nbcols = GET_BDA(video_cols);
127 // Get the current page
129 page = GET_BDA(video_page);
135 if (nblines > nbrows)
137 u8 cols = clr - cul + 1;
139 if (GET_GLOBAL(vmode_g->class) == TEXT) {
140 // Compute the address
141 void *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, page));
142 dprintf(3, "Scroll, address %p (%d %d %02x)\n"
143 , address_far, nbrows, nbcols, page);
145 if (nblines == 0 && rul == 0 && cul == 0 && rlr == nbrows - 1
146 && clr == nbcols - 1) {
147 memset16_far(GET_GLOBAL(vmode_g->sstart), address_far
148 , (u16)attr * 0x100 + ' ', nbrows * nbcols * 2);
149 } else { // if Scroll up
150 if (dir == SCROLL_UP) {
152 for (i = rul; i <= rlr; i++)
153 if ((i + nblines > rlr) || (nblines == 0))
154 memset16_far(GET_GLOBAL(vmode_g->sstart)
155 , address_far + (i * nbcols + cul) * 2
156 , (u16)attr * 0x100 + ' ', cols * 2);
158 memcpy16_far(GET_GLOBAL(vmode_g->sstart)
159 , address_far + (i * nbcols + cul) * 2
160 , GET_GLOBAL(vmode_g->sstart)
161 , (void*)(((i + nblines) * nbcols + cul) * 2)
165 for (i = rlr; i >= rul; i--) {
166 if ((i < rul + nblines) || (nblines == 0))
167 memset16_far(GET_GLOBAL(vmode_g->sstart)
168 , address_far + (i * nbcols + cul) * 2
169 , (u16)attr * 0x100 + ' ', cols * 2);
171 memcpy16_far(GET_GLOBAL(vmode_g->sstart)
172 , address_far + (i * nbcols + cul) * 2
173 , GET_GLOBAL(vmode_g->sstart)
174 , (void*)(((i - nblines) * nbcols + cul) * 2)
184 // FIXME gfx mode not complete
185 struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
186 u8 cheight = GET_GLOBAL(vparam_g->cheight);
187 switch (GET_GLOBAL(vmode_g->memmodel)) {
190 if (nblines == 0 && rul == 0 && cul == 0 && rlr == nbrows - 1
191 && clr == nbcols - 1) {
192 outw(0x0205, VGAREG_GRDC_ADDRESS);
193 memset_far(GET_GLOBAL(vmode_g->sstart), 0, attr,
194 nbrows * nbcols * cheight);
195 outw(0x0005, VGAREG_GRDC_ADDRESS);
196 } else { // if Scroll up
197 if (dir == SCROLL_UP) {
199 for (i = rul; i <= rlr; i++)
200 if ((i + nblines > rlr) || (nblines == 0))
201 vgamem_fill_pl4(cul, i, cols, nbcols, cheight,
204 vgamem_copy_pl4(cul, i + nblines, i, cols,
208 for (i = rlr; i >= rul; i--) {
209 if ((i < rul + nblines) || (nblines == 0))
210 vgamem_fill_pl4(cul, i, cols, nbcols, cheight,
213 vgamem_copy_pl4(cul, i, i - nblines, cols,
222 u8 bpp = GET_GLOBAL(vmode_g->pixbits);
223 if (nblines == 0 && rul == 0 && cul == 0 && rlr == nbrows - 1
224 && clr == nbcols - 1) {
225 memset_far(GET_GLOBAL(vmode_g->sstart), 0, attr,
226 nbrows * nbcols * cheight * bpp);
234 if (dir == SCROLL_UP) {
236 for (i = rul; i <= rlr; i++)
237 if ((i + nblines > rlr) || (nblines == 0))
238 vgamem_fill_cga(cul, i, cols, nbcols, cheight,
241 vgamem_copy_cga(cul, i + nblines, i, cols,
245 for (i = rlr; i >= rul; i--) {
246 if ((i < rul + nblines) || (nblines == 0))
247 vgamem_fill_cga(cul, i, cols, nbcols, cheight,
250 vgamem_copy_cga(cul, i, i - nblines, cols,
260 dprintf(1, "Scroll in graphics mode\n");
265 /****************************************************************
266 * Read/write characters to screen
267 ****************************************************************/
270 write_gfx_char_pl4(struct cursorpos cp, u8 car, u8 attr, u8 nbcols,
284 u16 addr = cp.x + cp.y * cheight * nbcols;
285 u16 src = car * cheight;
286 outw(0x0f02, VGAREG_SEQU_ADDRESS);
287 outw(0x0205, VGAREG_GRDC_ADDRESS);
289 outw(0x1803, VGAREG_GRDC_ADDRESS);
291 outw(0x0003, VGAREG_GRDC_ADDRESS);
293 for (i = 0; i < cheight; i++) {
294 u8 *dest_far = (void*)(addr + i * nbcols);
296 for (j = 0; j < 8; j++) {
298 outw((mask << 8) | 0x08, VGAREG_GRDC_ADDRESS);
299 GET_FARVAR(SEG_GRAPH, *dest_far);
300 if (GET_GLOBAL(fdata_g[src + i]) & mask)
301 SET_FARVAR(SEG_GRAPH, *dest_far, attr & 0x0f);
303 SET_FARVAR(SEG_GRAPH, *dest_far, 0x00);
306 outw(0xff08, VGAREG_GRDC_ADDRESS);
307 outw(0x0005, VGAREG_GRDC_ADDRESS);
308 outw(0x0003, VGAREG_GRDC_ADDRESS);
312 write_gfx_char_cga(struct cursorpos cp, u8 car, u8 attr, u8 nbcols, u8 bpp)
314 u8 *fdata_g = vgafont8;
315 u16 addr = (cp.x * bpp) + cp.y * 320;
318 for (i = 0; i < 8; i++) {
319 u8 *dest_far = (void*)(addr + (i >> 1) * 80);
326 data = GET_FARVAR(SEG_CTEXT, *dest_far);
328 for (j = 0; j < 8; j++) {
329 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
331 data ^= (attr & 0x01) << (7 - j);
333 data |= (attr & 0x01) << (7 - j);
337 SET_FARVAR(SEG_CTEXT, *dest_far, data);
342 data = GET_FARVAR(SEG_CTEXT, *dest_far);
344 for (j = 0; j < 4; j++) {
345 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
347 data ^= (attr & 0x03) << ((3 - j) * 2);
349 data |= (attr & 0x03) << ((3 - j) * 2);
353 SET_FARVAR(SEG_CTEXT, *dest_far, data);
361 write_gfx_char_lin(struct cursorpos cp, u8 car, u8 attr, u8 nbcols)
363 u8 *fdata_g = vgafont8;
364 u16 addr = cp.x * 8 + cp.y * nbcols * 64;
367 for (i = 0; i < 8; i++) {
368 u8 *dest_far = (void*)(addr + i * nbcols * 8);
371 for (j = 0; j < 8; j++) {
373 if (GET_GLOBAL(fdata_g[src + i]) & mask)
375 SET_FARVAR(SEG_GRAPH, dest_far[j], data);
382 biosfn_write_char_attr(u8 car, u8 page, u8 attr, u16 count)
385 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
389 // Get the cursor pos for the page
390 struct cursorpos cp = get_cursor_pos(page);
392 // Get the dimensions
393 u16 nbrows = GET_BDA(video_rows) + 1;
394 u16 nbcols = GET_BDA(video_cols);
396 if (GET_GLOBAL(vmode_g->class) == TEXT) {
397 // Compute the address
398 void *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, cp.page)
399 + (cp.x + cp.y * nbcols) * 2);
401 u16 dummy = ((u16)attr << 8) + car;
402 memset16_far(GET_GLOBAL(vmode_g->sstart), address_far, dummy, count * 2);
406 // FIXME gfx mode not complete
407 struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
408 u8 cheight = GET_GLOBAL(vparam_g->cheight);
409 u8 bpp = GET_GLOBAL(vmode_g->pixbits);
410 while ((count-- > 0) && (cp.x < nbcols)) {
411 switch (GET_GLOBAL(vmode_g->memmodel)) {
414 write_gfx_char_pl4(cp, car, attr, nbcols, cheight);
417 write_gfx_char_cga(cp, car, attr, nbcols, bpp);
420 write_gfx_char_lin(cp, car, attr, nbcols);
428 biosfn_write_char_only(u8 car, u8 page, u8 attr, u16 count)
431 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
435 // Get the cursor pos for the page
436 struct cursorpos cp = get_cursor_pos(page);
438 // Get the dimensions
439 u16 nbrows = GET_BDA(video_rows) + 1;
440 u16 nbcols = GET_BDA(video_cols);
442 if (GET_GLOBAL(vmode_g->class) == TEXT) {
443 // Compute the address
444 u8 *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, cp.page)
445 + (cp.x + cp.y * nbcols) * 2);
446 while (count-- > 0) {
447 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *address_far, car);
453 // FIXME gfx mode not complete
454 struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
455 u8 cheight = GET_GLOBAL(vparam_g->cheight);
456 u8 bpp = GET_GLOBAL(vmode_g->pixbits);
457 while ((count-- > 0) && (cp.x < nbcols)) {
458 switch (GET_GLOBAL(vmode_g->memmodel)) {
461 write_gfx_char_pl4(cp, car, attr, nbcols, cheight);
464 write_gfx_char_cga(cp, car, attr, nbcols, bpp);
467 write_gfx_char_lin(cp, car, attr, nbcols);
475 biosfn_read_char_attr(u8 page, u16 *car)
478 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
482 // Get the cursor pos for the page
483 struct cursorpos cp = get_cursor_pos(page);
485 // Get the dimensions
486 u16 nbrows = GET_BDA(video_rows) + 1;
487 u16 nbcols = GET_BDA(video_cols);
489 if (GET_GLOBAL(vmode_g->class) == TEXT) {
490 // Compute the address
491 u16 *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, cp.page)
492 + (cp.x + cp.y * nbcols) * 2);
494 *car = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *address_far);
497 dprintf(1, "Read char in graphics mode\n");
502 /****************************************************************
504 ****************************************************************/
507 biosfn_write_pixel(u8 BH, u8 AL, u16 CX, u16 DX)
510 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
513 if (GET_GLOBAL(vmode_g->class) == TEXT)
516 u8 *addr_far, mask, attr, data;
517 switch (GET_GLOBAL(vmode_g->memmodel)) {
520 addr_far = (void*)(CX / 8 + DX * GET_BDA(video_cols));
521 mask = 0x80 >> (CX & 0x07);
522 outw((mask << 8) | 0x08, VGAREG_GRDC_ADDRESS);
523 outw(0x0205, VGAREG_GRDC_ADDRESS);
524 data = GET_FARVAR(SEG_GRAPH, *addr_far);
526 outw(0x1803, VGAREG_GRDC_ADDRESS);
527 SET_FARVAR(SEG_GRAPH, *addr_far, AL);
528 outw(0xff08, VGAREG_GRDC_ADDRESS);
529 outw(0x0005, VGAREG_GRDC_ADDRESS);
530 outw(0x0003, VGAREG_GRDC_ADDRESS);
533 if (GET_GLOBAL(vmode_g->pixbits) == 2)
534 addr_far = (void*)((CX >> 2) + (DX >> 1) * 80);
536 addr_far = (void*)((CX >> 3) + (DX >> 1) * 80);
539 data = GET_FARVAR(SEG_CTEXT, *addr_far);
540 if (GET_GLOBAL(vmode_g->pixbits) == 2) {
541 attr = (AL & 0x03) << ((3 - (CX & 0x03)) * 2);
542 mask = 0x03 << ((3 - (CX & 0x03)) * 2);
544 attr = (AL & 0x01) << (7 - (CX & 0x07));
545 mask = 0x01 << (7 - (CX & 0x07));
553 SET_FARVAR(SEG_CTEXT, *addr_far, data);
556 addr_far = (void*)(CX + DX * (GET_BDA(video_cols) * 8));
557 SET_FARVAR(SEG_GRAPH, *addr_far, AL);
563 biosfn_read_pixel(u8 BH, u16 CX, u16 DX, u16 *AX)
566 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
569 if (GET_GLOBAL(vmode_g->class) == TEXT)
572 u8 *addr_far, mask, attr=0, data, i;
573 switch (GET_GLOBAL(vmode_g->memmodel)) {
576 addr_far = (void*)(CX / 8 + DX * GET_BDA(video_cols));
577 mask = 0x80 >> (CX & 0x07);
579 for (i = 0; i < 4; i++) {
580 outw((i << 8) | 0x04, VGAREG_GRDC_ADDRESS);
581 data = GET_FARVAR(SEG_GRAPH, *addr_far) & mask;
587 addr_far = (void*)((CX >> 2) + (DX >> 1) * 80);
590 data = GET_FARVAR(SEG_CTEXT, *addr_far);
591 if (GET_GLOBAL(vmode_g->pixbits) == 2)
592 attr = (data >> ((3 - (CX & 0x03)) * 2)) & 0x03;
594 attr = (data >> (7 - (CX & 0x07))) & 0x01;
597 addr_far = (void*)(CX + DX * (GET_BDA(video_cols) * 8));
598 attr = GET_FARVAR(SEG_GRAPH, *addr_far);
601 *AX = (*AX & 0xff00) | attr;
605 /****************************************************************
607 ****************************************************************/
610 vgafb_load_font(u16 seg, void *src_far, u16 count
611 , u16 start, u8 destflags, u8 fontsize)
614 u16 blockaddr = ((destflags & 0x03) << 14) + ((destflags & 0x04) << 11);
615 void *dest_far = (void*)(blockaddr + start*32);
617 for (i = 0; i < count; i++)
618 memcpy_far(SEG_GRAPH, dest_far + i*32
619 , seg, src_far + i*fontsize, fontsize);
620 release_font_access();