1 // Code for manipulating VGA framebuffers.
3 // Copyright (C) 2009-2014 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 "byteorder.h" // cpu_to_be16
10 #include "output.h" // dprintf
11 #include "stdvga.h" // stdvga_planar4_plane
12 #include "string.h" // memset_far
13 #include "vgabios.h" // vgafb_scroll
14 #include "vgahw.h" // vgahw_get_linelength
17 memmove_stride(u16 seg, void *dst, void *src, int copylen, int stride, int lines)
20 dst += stride * (lines - 1);
21 src += stride * (lines - 1);
24 for (; lines; lines--, dst+=stride, src+=stride)
25 memcpy_far(seg, dst, seg, src, copylen);
29 memset_stride(u16 seg, void *dst, u8 val, int setlen, int stride, int lines)
31 for (; lines; lines--, dst+=stride)
32 memset_far(seg, dst, val, setlen);
36 memset16_stride(u16 seg, void *dst, u16 val, int setlen, int stride, int lines)
38 for (; lines; lines--, dst+=stride)
39 memset16_far(seg, dst, val, setlen);
43 /****************************************************************
44 * Basic stdvga graphic manipulation
45 ****************************************************************/
48 gfx_planar(struct gfx_op *op)
50 if (!CONFIG_VGA_STDVGA_PORTS)
52 void *dest_far = (void*)(op->y * op->linelength + op->x / 8);
57 memset(op->pixels, 0, sizeof(op->pixels));
58 for (plane = 0; plane < 4; plane++) {
59 stdvga_planar4_plane(plane);
60 u8 data = GET_FARVAR(SEG_GRAPH, *(u8*)dest_far);
62 for (pixel=0; pixel<8; pixel++)
63 op->pixels[pixel] |= ((data>>(7-pixel)) & 1) << plane;
67 for (plane = 0; plane<4; plane++) {
68 stdvga_planar4_plane(plane);
71 for (pixel=0; pixel<8; pixel++)
72 data |= ((op->pixels[pixel]>>plane) & 1) << (7-pixel);
73 SET_FARVAR(SEG_GRAPH, *(u8*)dest_far, data);
77 for (plane = 0; plane < 4; plane++) {
78 stdvga_planar4_plane(plane);
79 u8 data = (op->pixels[0] & (1<<plane)) ? 0xff : 0x00;
80 memset_stride(SEG_GRAPH, dest_far, data
81 , op->xlen / 8, op->linelength, op->ylen);
85 void *src_far = (void*)(op->srcy * op->linelength + op->x / 8);
86 for (plane = 0; plane < 4; plane++) {
87 stdvga_planar4_plane(plane);
88 memmove_stride(SEG_GRAPH, dest_far, src_far
89 , op->xlen / 8, op->linelength, op->ylen);
93 stdvga_planar4_plane(-1);
97 gfx_cga(struct gfx_op *op)
99 int bpp = GET_GLOBAL(op->vmode_g->depth);
100 void *dest_far = (void*)(op->y / 2 * op->linelength + op->x / 8 * bpp);
107 u8 data = GET_FARVAR(SEG_CTEXT, *(u8*)dest_far);
109 for (pixel=0; pixel<8; pixel++)
110 op->pixels[pixel] = (data >> (7-pixel)) & 1;
112 u16 data = GET_FARVAR(SEG_CTEXT, *(u16*)dest_far);
113 data = be16_to_cpu(data);
115 for (pixel=0; pixel<8; pixel++)
116 op->pixels[pixel] = (data >> ((7-pixel)*2)) & 3;
125 for (pixel=0; pixel<8; pixel++)
126 data |= (op->pixels[pixel] & 1) << (7-pixel);
127 SET_FARVAR(SEG_CTEXT, *(u8*)dest_far, data);
131 for (pixel=0; pixel<8; pixel++)
132 data |= (op->pixels[pixel] & 3) << ((7-pixel) * 2);
133 data = cpu_to_be16(data);
134 SET_FARVAR(SEG_CTEXT, *(u16*)dest_far, data);
138 u8 data = op->pixels[0];
140 data = (data&1) | ((data&1)<<1);
142 data |= (data<<2) | (data<<4) | (data<<6);
143 memset_stride(SEG_CTEXT, dest_far, data
144 , op->xlen / 8 * bpp, op->linelength, op->ylen / 2);
145 memset_stride(SEG_CTEXT, dest_far + 0x2000, data
146 , op->xlen / 8 * bpp, op->linelength, op->ylen / 2);
149 void *src_far = (void*)(op->srcy / 2 * op->linelength + op->x / 8 * bpp);
150 memmove_stride(SEG_CTEXT, dest_far, src_far
151 , op->xlen / 8 * bpp, op->linelength, op->ylen / 2);
152 memmove_stride(SEG_CTEXT, dest_far + 0x2000, src_far + 0x2000
153 , op->xlen / 8 * bpp, op->linelength, op->ylen / 2);
159 gfx_packed(struct gfx_op *op)
161 void *dest_far = (void*)(op->y * op->linelength + op->x);
165 memcpy_far(GET_SEG(SS), op->pixels, SEG_GRAPH, dest_far, 8);
168 memcpy_far(SEG_GRAPH, dest_far, GET_SEG(SS), op->pixels, 8);
171 memset_stride(SEG_GRAPH, dest_far, op->pixels[0]
172 , op->xlen, op->linelength, op->ylen);
175 void *src_far = (void*)(op->srcy * op->linelength + op->x);
176 memmove_stride(SEG_GRAPH, dest_far, src_far
177 , op->xlen, op->linelength, op->ylen);
183 /****************************************************************
184 * Direct framebuffers in high mem
185 ****************************************************************/
187 // Use int 1587 call to copy memory to/from the framebuffer.
189 memcpy_high(void *dest, void *src, u32 len)
192 gdt[2] = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE((u32)src);
193 gdt[3] = GDT_DATA | GDT_LIMIT(0xfffff) | GDT_BASE((u32)dest);
195 // Call int 1587 to copy data.
200 SET_SEG(ES, GET_SEG(SS));
208 : "=r" (flags), "+a" (eax), "+S" (si), "+c" (len)
213 memmove_stride_high(void *dst, void *src, int copylen, int stride, int lines)
216 dst += stride * (lines - 1);
217 src += stride * (lines - 1);
220 for (; lines; lines--, dst+=stride, src+=stride)
221 memcpy_high(dst, src, copylen);
224 // Map a CGA color to a "direct" mode rgb value.
226 get_color(int depth, u8 attr)
228 int rbits, gbits, bbits;
230 case 15: rbits=5; gbits=5; bbits=5; break;
231 case 16: rbits=5; gbits=6; bbits=5; break;
233 case 24: rbits=8; gbits=8; bbits=8; break;
235 int h = (attr&8) ? 1 : 0;
236 int r = (attr&4) ? 2 : 0, g = (attr&2) ? 2 : 0, b = (attr&1) ? 2 : 0;
237 if ((attr & 0xf) == 6)
239 int rv = DIV_ROUND_CLOSEST(((1<<rbits) - 1) * (r + h), 3);
240 int gv = DIV_ROUND_CLOSEST(((1<<gbits) - 1) * (g + h), 3);
241 int bv = DIV_ROUND_CLOSEST(((1<<bbits) - 1) * (b + h), 3);
242 return (rv << (gbits+bbits)) + (gv << bbits) + bv;
245 // Find the closest attribute for a given framebuffer color
247 reverse_color(int depth, u32 color)
249 int rbits, gbits, bbits;
251 case 15: rbits=5; gbits=5; bbits=5; break;
252 case 16: rbits=5; gbits=6; bbits=5; break;
254 case 24: rbits=8; gbits=8; bbits=8; break;
256 int rv = (color >> (gbits+bbits)) & ((1<<rbits)-1);
257 int gv = (color >> bbits) & ((1<<gbits)-1);
258 int bv = color & ((1<<bbits)-1);
259 int r = DIV_ROUND_CLOSEST(rv * 3, (1<<rbits) - 1);
260 int g = DIV_ROUND_CLOSEST(gv * 3, (1<<gbits) - 1);
261 int b = DIV_ROUND_CLOSEST(bv * 3, (1<<bbits) - 1);
262 int h = r && g && b && (r != 2 || g != 2 || b != 2);
263 return (h ? 8 : 0) | ((r-h) ? 4 : 0) | ((g-h) ? 2 : 0) | ((b-h) ? 1 : 0);
267 gfx_direct(struct gfx_op *op)
269 void *fb = (void*)GET_GLOBAL(VBE_framebuffer);
272 int depth = GET_GLOBAL(op->vmode_g->depth);
273 int bypp = DIV_ROUND_UP(depth, 8);
274 void *dest_far = (fb + op->displaystart + op->y * op->linelength
280 memcpy_high(MAKE_FLATPTR(GET_SEG(SS), data), dest_far, bypp * 8);
283 op->pixels[i] = reverse_color(depth, *(u32*)&data[i*bypp]);
290 *(u32*)&data[i*bypp] = get_color(depth, op->pixels[i]);
291 memcpy_high(dest_far, MAKE_FLATPTR(GET_SEG(SS), data), bypp * 8);
295 u32 color = get_color(depth, op->pixels[0]);
299 *(u32*)&data[i*bypp] = color;
300 memcpy_high(dest_far, MAKE_FLATPTR(GET_SEG(SS), data), bypp * 8);
301 memcpy_high(dest_far + bypp * 8, dest_far, op->xlen * bypp - bypp * 8);
302 for (i=1; i < op->ylen; i++)
303 memcpy_high(dest_far + op->linelength * i
304 , dest_far, op->xlen * bypp);
308 void *src_far = (fb + op->displaystart + op->srcy * op->linelength
310 memmove_stride_high(dest_far, src_far
311 , op->xlen * bypp, op->linelength, op->ylen);
317 /****************************************************************
319 ****************************************************************/
321 // Prepare a struct gfx_op for use.
323 init_gfx_op(struct gfx_op *op, struct vgamode_s *vmode_g)
325 memset(op, 0, sizeof(*op));
326 op->vmode_g = vmode_g;
327 op->linelength = vgahw_get_linelength(vmode_g);
328 op->displaystart = vgahw_get_displaystart(vmode_g);
331 // Issue a graphics operation.
333 handle_gfx_op(struct gfx_op *op)
335 switch (GET_GLOBAL(op->vmode_g->memmodel)) {
353 // Move characters when in graphics mode.
355 gfx_move_chars(struct vgamode_s *vmode_g, struct cursorpos dest
356 , struct cursorpos src, struct cursorpos movesize)
359 init_gfx_op(&op, vmode_g);
361 op.xlen = movesize.x * 8;
362 int cheight = GET_BDA(char_height);
363 op.y = dest.y * cheight;
364 op.ylen = movesize.y * cheight;
365 op.srcy = src.y * cheight;
370 // Clear area of screen in graphics mode.
372 gfx_clear_chars(struct vgamode_s *vmode_g, struct cursorpos dest
373 , struct carattr ca, struct cursorpos clearsize)
376 init_gfx_op(&op, vmode_g);
378 op.xlen = clearsize.x * 8;
379 int cheight = GET_BDA(char_height);
380 op.y = dest.y * cheight;
381 op.ylen = clearsize.y * cheight;
382 op.pixels[0] = ca.attr;
383 if (vga_emulate_text())
384 op.pixels[0] = ca.attr >> 4;
389 // Return the font for a given character
393 int char_height = GET_BDA(char_height);
394 struct segoff_s font;
395 if (char_height == 8 && c >= 128) {
396 font = GET_IVT(0x1f);
399 font = GET_IVT(0x43);
401 font.offset += c * char_height;
405 // Write a character to the screen in graphics mode.
407 gfx_write_char(struct vgamode_s *vmode_g
408 , struct cursorpos cp, struct carattr ca)
410 if (cp.x >= GET_BDA(video_cols))
413 struct segoff_s font = get_font_data(ca.car);
415 init_gfx_op(&op, vmode_g);
417 int cheight = GET_BDA(char_height);
418 op.y = cp.y * cheight;
419 u8 fgattr = ca.attr, bgattr = 0x00;
421 if (vga_emulate_text()) {
423 bgattr = fgattr >> 4;
424 fgattr = fgattr & 0x0f;
426 // Read bottom right pixel of the cell to guess bg color
431 bgattr = op.pixels[7];
432 fgattr = bgattr ^ 0x7;
434 } else if (fgattr & 0x80 && GET_GLOBAL(vmode_g->depth) < 8) {
439 for (i = 0; i < cheight; i++, op.y++) {
440 u8 fontline = GET_FARVAR(font.seg, *(u8*)(font.offset+i));
445 for (j = 0; j < 8; j++)
446 op.pixels[j] ^= (fontline & (0x80>>j)) ? fgattr : 0x00;
449 for (j = 0; j < 8; j++)
450 op.pixels[j] = (fontline & (0x80>>j)) ? fgattr : bgattr;
457 // Read a character from the screen in graphics mode.
458 static struct carattr
459 gfx_read_char(struct vgamode_s *vmode_g, struct cursorpos cp)
462 int cheight = GET_BDA(char_height);
463 if (cp.x >= GET_BDA(video_cols) || cheight > ARRAY_SIZE(lines))
466 // Read cell from screen
468 init_gfx_op(&op, vmode_g);
471 op.y = cp.y * cheight;
473 u8 fgattr = 0x00, bgattr = 0x00;
474 if (vga_emulate_text()) {
475 // Read bottom right pixel of the cell to guess bg color
479 bgattr = op.pixels[7];
480 fgattr = bgattr ^ 0x7;
481 // Report space character for blank cells (skip null character check)
485 for (i=0; i<cheight; i++, op.y++) {
489 if (op.pixels[j] != bgattr) {
491 fgattr = op.pixels[j];
497 for (; car<256; car++) {
498 struct segoff_s font = get_font_data(car);
499 if (memcmp_far(GET_SEG(SS), lines
500 , font.seg, (void*)(font.offset+0), cheight) == 0)
501 return (struct carattr){car, fgattr | (bgattr << 4), 0};
504 return (struct carattr){0, 0, 0};
507 // Draw/undraw a cursor on the framebuffer by xor'ing the cursor cell
509 gfx_set_swcursor(struct vgamode_s *vmode_g, int enable, struct cursorpos cp)
511 u16 cursor_type = get_cursor_shape();
512 u8 start = cursor_type >> 8, end = cursor_type & 0xff;
514 init_gfx_op(&op, vmode_g);
516 int cheight = GET_BDA(char_height);
517 op.y = cp.y * cheight + start;
520 for (i = start; i < cheight && i <= end; i++, op.y++) {
524 for (j = 0; j < 8; j++)
525 op.pixels[j] ^= 0x07;
531 // Set the pixel at the given position.
533 vgafb_write_pixel(u8 color, u16 x, u16 y)
535 struct vgamode_s *vmode_g = get_current_mode();
538 vgafb_set_swcursor(0);
541 init_gfx_op(&op, vmode_g);
542 op.x = ALIGN_DOWN(x, 8);
547 int usexor = color & 0x80 && GET_GLOBAL(vmode_g->depth) < 8;
549 op.pixels[x & 0x07] ^= color & 0x7f;
551 op.pixels[x & 0x07] = color;
556 // Return the pixel at the given position.
558 vgafb_read_pixel(u16 x, u16 y)
560 struct vgamode_s *vmode_g = get_current_mode();
563 vgafb_set_swcursor(0);
566 init_gfx_op(&op, vmode_g);
567 op.x = ALIGN_DOWN(x, 8);
572 return op.pixels[x & 0x07];
576 /****************************************************************
578 ****************************************************************/
580 // Return the fb offset for the given character address when in text mode.
582 text_address(struct cursorpos cp)
584 int stride = GET_BDA(video_cols) * 2;
585 u32 pageoffset = GET_BDA(video_pagesize) * cp.page;
586 return (void*)pageoffset + cp.y * stride + cp.x * 2;
589 // Move characters on screen.
591 vgafb_move_chars(struct cursorpos dest
592 , struct cursorpos src, struct cursorpos movesize)
594 struct vgamode_s *vmode_g = get_current_mode();
597 vgafb_set_swcursor(0);
599 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
600 gfx_move_chars(vmode_g, dest, src, movesize);
604 int stride = GET_BDA(video_cols) * 2;
605 memmove_stride(GET_GLOBAL(vmode_g->sstart)
606 , text_address(dest), text_address(src)
607 , movesize.x * 2, stride, movesize.y);
610 // Clear area of screen.
612 vgafb_clear_chars(struct cursorpos dest
613 , struct carattr ca, struct cursorpos clearsize)
615 struct vgamode_s *vmode_g = get_current_mode();
618 vgafb_set_swcursor(0);
620 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
621 gfx_clear_chars(vmode_g, dest, ca, clearsize);
625 int stride = GET_BDA(video_cols) * 2;
626 u16 attr = ((ca.use_attr ? ca.attr : 0x07) << 8) | ca.car;
627 memset16_stride(GET_GLOBAL(vmode_g->sstart), text_address(dest), attr
628 , clearsize.x * 2, stride, clearsize.y);
631 // Write a character to the screen.
633 vgafb_write_char(struct cursorpos cp, struct carattr ca)
635 struct vgamode_s *vmode_g = get_current_mode();
638 vgafb_set_swcursor(0);
640 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
641 gfx_write_char(vmode_g, cp, ca);
645 void *dest_far = text_address(cp);
647 u16 dummy = (ca.attr << 8) | ca.car;
648 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u16*)dest_far, dummy);
650 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far, ca.car);
654 // Return the character at the given position on the screen.
656 vgafb_read_char(struct cursorpos cp)
658 struct vgamode_s *vmode_g = get_current_mode();
660 return (struct carattr){0, 0, 0};
661 vgafb_set_swcursor(0);
663 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT)
664 return gfx_read_char(vmode_g, cp);
666 u16 *dest_far = text_address(cp);
667 u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *dest_far);
668 return (struct carattr){v, v>>8, 0};
671 // Draw/undraw a cursor on the screen
673 vgafb_set_swcursor(int enable)
675 if (!vga_emulate_text())
677 u8 flags = GET_BDA_EXT(flags);
678 if (!!(flags & BF_SWCURSOR) == enable)
679 // Already in requested mode.
681 struct vgamode_s *vmode_g = get_current_mode();
684 struct cursorpos cp = get_cursor_pos(0xff);
685 if (cp.x >= GET_BDA(video_cols) || cp.y > GET_BDA(video_rows)
686 || GET_BDA(cursor_type) >= 0x2000)
687 // Cursor not visible
690 SET_BDA_EXT(flags, (flags & ~BF_SWCURSOR) | (enable ? BF_SWCURSOR : 0));
692 if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
693 gfx_set_swcursor(vmode_g, enable, cp);
697 // In text mode, swap foreground and background attributes for cursor
698 void *dest_far = text_address(cp) + 1;
699 u8 attr = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far);
700 attr = (attr >> 4) | (attr << 4);
701 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)dest_far, attr);