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 // * read/write_char should take a position; should not take count
16 // * remove vmode_g->class (integrate into vmode_g->memmodel)
17 // * normalize params (don't use AX/BX/CX/etc.)
21 memcpy16_far(u16 d_seg, void *d_far, u16 s_seg, const void *s_far, size_t len)
23 memcpy_far(d_seg, d_far, s_seg, s_far, len);
27 /****************************************************************
29 ****************************************************************/
32 vgamem_copy_pl4(u8 xstart, u8 ysrc, u8 ydest, u8 cols, u8 nbcols,
35 u16 src = ysrc * cheight * nbcols + xstart;
36 u16 dest = ydest * cheight * nbcols + xstart;
37 outw(0x0105, VGAREG_GRDC_ADDRESS);
39 for (i = 0; i < cheight; i++)
40 memcpy_far(SEG_GRAPH, (void*)(dest + i * nbcols)
41 , SEG_GRAPH, (void*)(src + i * nbcols), cols);
42 outw(0x0005, VGAREG_GRDC_ADDRESS);
46 vgamem_fill_pl4(u8 xstart, u8 ystart, u8 cols, u8 nbcols, u8 cheight,
49 u16 dest = ystart * cheight * nbcols + xstart;
50 outw(0x0205, VGAREG_GRDC_ADDRESS);
52 for (i = 0; i < cheight; i++)
53 memset_far(SEG_GRAPH, (void*)(dest + i * nbcols), attr, cols);
54 outw(0x0005, VGAREG_GRDC_ADDRESS);
58 vgamem_copy_cga(u8 xstart, u8 ysrc, u8 ydest, u8 cols, u8 nbcols,
61 u16 src = ((ysrc * cheight * nbcols) >> 1) + xstart;
62 u16 dest = ((ydest * cheight * nbcols) >> 1) + xstart;
64 for (i = 0; i < cheight; i++)
66 memcpy_far(SEG_CTEXT, (void*)(0x2000 + dest + (i >> 1) * nbcols)
67 , SEG_CTEXT, (void*)(0x2000 + src + (i >> 1) * nbcols)
70 memcpy_far(SEG_CTEXT, (void*)(dest + (i >> 1) * nbcols)
71 , SEG_CTEXT, (void*)(src + (i >> 1) * nbcols), cols);
75 vgamem_fill_cga(u8 xstart, u8 ystart, u8 cols, u8 nbcols, u8 cheight,
78 u16 dest = ((ystart * cheight * nbcols) >> 1) + xstart;
80 for (i = 0; i < cheight; i++)
82 memset_far(SEG_CTEXT, (void*)(0x2000 + dest + (i >> 1) * nbcols)
85 memset_far(SEG_CTEXT, (void*)(dest + (i >> 1) * nbcols), attr, cols);
89 clear_screen(struct vgamode_s *vmode_g)
91 if (GET_GLOBAL(vmode_g->class) == TEXT) {
92 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0720, 32*1024);
95 if (GET_GLOBAL(vmode_g->svgamode) < 0x0d) {
96 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 32*1024);
99 outb(0x02, VGAREG_SEQU_ADDRESS);
100 u8 mmask = inb(VGAREG_SEQU_DATA);
101 outb(0x0f, VGAREG_SEQU_DATA); // all planes
102 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 64*1024);
103 outb(mmask, VGAREG_SEQU_DATA);
107 biosfn_scroll(u8 nblines, u8 attr, u8 rul, u8 cul, u8 rlr, u8 clr, u8 page,
110 // page == 0xFF if current
117 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
121 // Get the dimensions
122 u16 nbrows = GET_BDA(video_rows) + 1;
123 u16 nbcols = GET_BDA(video_cols);
125 // Get the current page
127 page = GET_BDA(video_page);
133 if (nblines > nbrows)
135 u8 cols = clr - cul + 1;
137 if (GET_GLOBAL(vmode_g->class) == TEXT) {
138 // Compute the address
139 void *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, page));
140 dprintf(3, "Scroll, address %p (%d %d %02x)\n"
141 , address_far, nbrows, nbcols, page);
143 if (nblines == 0 && rul == 0 && cul == 0 && rlr == nbrows - 1
144 && clr == nbcols - 1) {
145 memset16_far(GET_GLOBAL(vmode_g->sstart), address_far
146 , (u16)attr * 0x100 + ' ', nbrows * nbcols * 2);
147 } else { // if Scroll up
148 if (dir == SCROLL_UP) {
150 for (i = rul; i <= rlr; i++)
151 if ((i + nblines > rlr) || (nblines == 0))
152 memset16_far(GET_GLOBAL(vmode_g->sstart)
153 , address_far + (i * nbcols + cul) * 2
154 , (u16)attr * 0x100 + ' ', cols * 2);
156 memcpy16_far(GET_GLOBAL(vmode_g->sstart)
157 , address_far + (i * nbcols + cul) * 2
158 , GET_GLOBAL(vmode_g->sstart)
159 , (void*)(((i + nblines) * nbcols + cul) * 2)
163 for (i = rlr; i >= rul; i--) {
164 if ((i < rul + nblines) || (nblines == 0))
165 memset16_far(GET_GLOBAL(vmode_g->sstart)
166 , address_far + (i * nbcols + cul) * 2
167 , (u16)attr * 0x100 + ' ', cols * 2);
169 memcpy16_far(GET_GLOBAL(vmode_g->sstart)
170 , address_far + (i * nbcols + cul) * 2
171 , GET_GLOBAL(vmode_g->sstart)
172 , (void*)(((i - nblines) * nbcols + cul) * 2)
182 // FIXME gfx mode not complete
183 struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
184 u8 cheight = GET_GLOBAL(vparam_g->cheight);
185 switch (GET_GLOBAL(vmode_g->memmodel)) {
188 if (nblines == 0 && rul == 0 && cul == 0 && rlr == nbrows - 1
189 && clr == nbcols - 1) {
190 outw(0x0205, VGAREG_GRDC_ADDRESS);
191 memset_far(GET_GLOBAL(vmode_g->sstart), 0, attr,
192 nbrows * nbcols * cheight);
193 outw(0x0005, VGAREG_GRDC_ADDRESS);
194 } else { // if Scroll up
195 if (dir == SCROLL_UP) {
197 for (i = rul; i <= rlr; i++)
198 if ((i + nblines > rlr) || (nblines == 0))
199 vgamem_fill_pl4(cul, i, cols, nbcols, cheight,
202 vgamem_copy_pl4(cul, i + nblines, i, cols,
206 for (i = rlr; i >= rul; i--) {
207 if ((i < rul + nblines) || (nblines == 0))
208 vgamem_fill_pl4(cul, i, cols, nbcols, cheight,
211 vgamem_copy_pl4(cul, i, i - nblines, cols,
220 u8 bpp = GET_GLOBAL(vmode_g->pixbits);
221 if (nblines == 0 && rul == 0 && cul == 0 && rlr == nbrows - 1
222 && clr == nbcols - 1) {
223 memset_far(GET_GLOBAL(vmode_g->sstart), 0, attr,
224 nbrows * nbcols * cheight * bpp);
232 if (dir == SCROLL_UP) {
234 for (i = rul; i <= rlr; i++)
235 if ((i + nblines > rlr) || (nblines == 0))
236 vgamem_fill_cga(cul, i, cols, nbcols, cheight,
239 vgamem_copy_cga(cul, i + nblines, i, cols,
243 for (i = rlr; i >= rul; i--) {
244 if ((i < rul + nblines) || (nblines == 0))
245 vgamem_fill_cga(cul, i, cols, nbcols, cheight,
248 vgamem_copy_cga(cul, i, i - nblines, cols,
258 dprintf(1, "Scroll in graphics mode\n");
263 /****************************************************************
264 * Read/write characters to screen
265 ****************************************************************/
268 write_gfx_char_pl4(struct cursorpos cp, struct carattr ca, u8 nbcols, u8 cheight)
281 u16 addr = cp.x + cp.y * cheight * nbcols;
282 u16 src = ca.car * cheight;
283 outw(0x0f02, VGAREG_SEQU_ADDRESS);
284 outw(0x0205, VGAREG_GRDC_ADDRESS);
286 outw(0x1803, VGAREG_GRDC_ADDRESS);
288 outw(0x0003, VGAREG_GRDC_ADDRESS);
290 for (i = 0; i < cheight; i++) {
291 u8 *dest_far = (void*)(addr + i * nbcols);
293 for (j = 0; j < 8; j++) {
295 outw((mask << 8) | 0x08, VGAREG_GRDC_ADDRESS);
296 GET_FARVAR(SEG_GRAPH, *dest_far);
297 if (GET_GLOBAL(fdata_g[src + i]) & mask)
298 SET_FARVAR(SEG_GRAPH, *dest_far, ca.attr & 0x0f);
300 SET_FARVAR(SEG_GRAPH, *dest_far, 0x00);
303 outw(0xff08, VGAREG_GRDC_ADDRESS);
304 outw(0x0005, VGAREG_GRDC_ADDRESS);
305 outw(0x0003, VGAREG_GRDC_ADDRESS);
309 write_gfx_char_cga(struct cursorpos cp, struct carattr ca, u8 nbcols, u8 bpp)
311 u8 *fdata_g = vgafont8;
312 u16 addr = (cp.x * bpp) + cp.y * 320;
313 u16 src = ca.car * 8;
315 for (i = 0; i < 8; i++) {
316 u8 *dest_far = (void*)(addr + (i >> 1) * 80);
323 data = GET_FARVAR(SEG_CTEXT, *dest_far);
325 for (j = 0; j < 8; j++) {
326 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
328 data ^= (ca.attr & 0x01) << (7 - j);
330 data |= (ca.attr & 0x01) << (7 - j);
334 SET_FARVAR(SEG_CTEXT, *dest_far, data);
339 data = GET_FARVAR(SEG_CTEXT, *dest_far);
341 for (j = 0; j < 4; j++) {
342 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
344 data ^= (ca.attr & 0x03) << ((3 - j) * 2);
346 data |= (ca.attr & 0x03) << ((3 - j) * 2);
350 SET_FARVAR(SEG_CTEXT, *dest_far, data);
358 write_gfx_char_lin(struct cursorpos cp, struct carattr ca, u8 nbcols)
360 u8 *fdata_g = vgafont8;
361 u16 addr = cp.x * 8 + cp.y * nbcols * 64;
362 u16 src = ca.car * 8;
364 for (i = 0; i < 8; i++) {
365 u8 *dest_far = (void*)(addr + i * nbcols * 8);
368 for (j = 0; j < 8; j++) {
370 if (GET_GLOBAL(fdata_g[src + i]) & mask)
372 SET_FARVAR(SEG_GRAPH, dest_far[j], data);
379 vgafb_write_char(u8 page, struct carattr ca, u16 count)
382 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
386 // Get the cursor pos for the page
387 struct cursorpos cp = get_cursor_pos(page);
389 // Get the dimensions
390 u16 nbrows = GET_BDA(video_rows) + 1;
391 u16 nbcols = GET_BDA(video_cols);
393 if (GET_GLOBAL(vmode_g->class) == TEXT) {
394 // Compute the address
395 void *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, cp.page)
396 + (cp.x + cp.y * nbcols) * 2);
399 u16 dummy = (ca.attr << 8) | ca.car;
400 memset16_far(GET_GLOBAL(vmode_g->sstart), address_far, dummy
403 while (count-- > 0) {
404 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)address_far
412 // FIXME gfx mode not complete
413 struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
414 u8 cheight = GET_GLOBAL(vparam_g->cheight);
415 u8 bpp = GET_GLOBAL(vmode_g->pixbits);
416 while ((count-- > 0) && (cp.x < nbcols)) {
417 switch (GET_GLOBAL(vmode_g->memmodel)) {
420 write_gfx_char_pl4(cp, ca, nbcols, cheight);
423 write_gfx_char_cga(cp, ca, nbcols, bpp);
426 write_gfx_char_lin(cp, ca, nbcols);
434 vgafb_read_char(u8 page)
437 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
441 // Get the cursor pos for the page
442 struct cursorpos cp = get_cursor_pos(page);
444 // Get the dimensions
445 u16 nbrows = GET_BDA(video_rows) + 1;
446 u16 nbcols = GET_BDA(video_cols);
448 if (GET_GLOBAL(vmode_g->class) == TEXT) {
449 // Compute the address
450 u16 *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, cp.page)
451 + (cp.x + cp.y * nbcols) * 2);
452 u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *address_far);
453 struct carattr ca = {v, v>>8, 0};
458 dprintf(1, "Read char in graphics mode\n");
460 struct carattr ca = {0, 0, 0};
465 /****************************************************************
467 ****************************************************************/
470 biosfn_write_pixel(u8 BH, u8 AL, u16 CX, u16 DX)
473 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
476 if (GET_GLOBAL(vmode_g->class) == TEXT)
479 u8 *addr_far, mask, attr, data;
480 switch (GET_GLOBAL(vmode_g->memmodel)) {
483 addr_far = (void*)(CX / 8 + DX * GET_BDA(video_cols));
484 mask = 0x80 >> (CX & 0x07);
485 outw((mask << 8) | 0x08, VGAREG_GRDC_ADDRESS);
486 outw(0x0205, VGAREG_GRDC_ADDRESS);
487 data = GET_FARVAR(SEG_GRAPH, *addr_far);
489 outw(0x1803, VGAREG_GRDC_ADDRESS);
490 SET_FARVAR(SEG_GRAPH, *addr_far, AL);
491 outw(0xff08, VGAREG_GRDC_ADDRESS);
492 outw(0x0005, VGAREG_GRDC_ADDRESS);
493 outw(0x0003, VGAREG_GRDC_ADDRESS);
496 if (GET_GLOBAL(vmode_g->pixbits) == 2)
497 addr_far = (void*)((CX >> 2) + (DX >> 1) * 80);
499 addr_far = (void*)((CX >> 3) + (DX >> 1) * 80);
502 data = GET_FARVAR(SEG_CTEXT, *addr_far);
503 if (GET_GLOBAL(vmode_g->pixbits) == 2) {
504 attr = (AL & 0x03) << ((3 - (CX & 0x03)) * 2);
505 mask = 0x03 << ((3 - (CX & 0x03)) * 2);
507 attr = (AL & 0x01) << (7 - (CX & 0x07));
508 mask = 0x01 << (7 - (CX & 0x07));
516 SET_FARVAR(SEG_CTEXT, *addr_far, data);
519 addr_far = (void*)(CX + DX * (GET_BDA(video_cols) * 8));
520 SET_FARVAR(SEG_GRAPH, *addr_far, AL);
526 biosfn_read_pixel(u8 BH, u16 CX, u16 DX, u16 *AX)
529 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
532 if (GET_GLOBAL(vmode_g->class) == TEXT)
535 u8 *addr_far, mask, attr=0, data, i;
536 switch (GET_GLOBAL(vmode_g->memmodel)) {
539 addr_far = (void*)(CX / 8 + DX * GET_BDA(video_cols));
540 mask = 0x80 >> (CX & 0x07);
542 for (i = 0; i < 4; i++) {
543 outw((i << 8) | 0x04, VGAREG_GRDC_ADDRESS);
544 data = GET_FARVAR(SEG_GRAPH, *addr_far) & mask;
550 addr_far = (void*)((CX >> 2) + (DX >> 1) * 80);
553 data = GET_FARVAR(SEG_CTEXT, *addr_far);
554 if (GET_GLOBAL(vmode_g->pixbits) == 2)
555 attr = (data >> ((3 - (CX & 0x03)) * 2)) & 0x03;
557 attr = (data >> (7 - (CX & 0x07))) & 0x01;
560 addr_far = (void*)(CX + DX * (GET_BDA(video_cols) * 8));
561 attr = GET_FARVAR(SEG_GRAPH, *addr_far);
564 *AX = (*AX & 0xff00) | attr;
568 /****************************************************************
570 ****************************************************************/
573 vgafb_load_font(u16 seg, void *src_far, u16 count
574 , u16 start, u8 destflags, u8 fontsize)
577 u16 blockaddr = ((destflags & 0x03) << 14) + ((destflags & 0x04) << 11);
578 void *dest_far = (void*)(blockaddr + start*32);
580 for (i = 0; i < count; i++)
581 memcpy_far(SEG_GRAPH, dest_far + i*32
582 , seg, src_far + i*fontsize, fontsize);
583 release_font_access();