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 // * merge page/x/y into one param
17 // * combine biosfn_write_char_attr/_only()
18 // * read/write_char should take a position; should not take count
19 // * remove vmode_g->class (integrate into vmode_g->memmodel)
20 // * normalize params (don't use AX/BX/CX/etc.)
24 memcpy16_far(u16 d_seg, void *d_far, u16 s_seg, const void *s_far, size_t len)
26 memcpy_far(d_seg, d_far, s_seg, s_far, len);
30 /****************************************************************
32 ****************************************************************/
35 vgamem_copy_pl4(u8 xstart, u8 ysrc, u8 ydest, u8 cols, u8 nbcols,
38 u16 src = ysrc * cheight * nbcols + xstart;
39 u16 dest = ydest * cheight * nbcols + xstart;
40 outw(0x0105, VGAREG_GRDC_ADDRESS);
42 for (i = 0; i < cheight; i++)
43 memcpy_far(SEG_GRAPH, (void*)(dest + i * nbcols)
44 , SEG_GRAPH, (void*)(src + i * nbcols), cols);
45 outw(0x0005, VGAREG_GRDC_ADDRESS);
49 vgamem_fill_pl4(u8 xstart, u8 ystart, u8 cols, u8 nbcols, u8 cheight,
52 u16 dest = ystart * cheight * nbcols + xstart;
53 outw(0x0205, VGAREG_GRDC_ADDRESS);
55 for (i = 0; i < cheight; i++)
56 memset_far(SEG_GRAPH, (void*)(dest + i * nbcols), attr, cols);
57 outw(0x0005, VGAREG_GRDC_ADDRESS);
61 vgamem_copy_cga(u8 xstart, u8 ysrc, u8 ydest, u8 cols, u8 nbcols,
64 u16 src = ((ysrc * cheight * nbcols) >> 1) + xstart;
65 u16 dest = ((ydest * cheight * nbcols) >> 1) + xstart;
67 for (i = 0; i < cheight; i++)
69 memcpy_far(SEG_CTEXT, (void*)(0x2000 + dest + (i >> 1) * nbcols)
70 , SEG_CTEXT, (void*)(0x2000 + src + (i >> 1) * nbcols)
73 memcpy_far(SEG_CTEXT, (void*)(dest + (i >> 1) * nbcols)
74 , SEG_CTEXT, (void*)(src + (i >> 1) * nbcols), cols);
78 vgamem_fill_cga(u8 xstart, u8 ystart, u8 cols, u8 nbcols, u8 cheight,
81 u16 dest = ((ystart * cheight * nbcols) >> 1) + xstart;
83 for (i = 0; i < cheight; i++)
85 memset_far(SEG_CTEXT, (void*)(0x2000 + dest + (i >> 1) * nbcols)
88 memset_far(SEG_CTEXT, (void*)(dest + (i >> 1) * nbcols), attr, cols);
92 clear_screen(struct vgamode_s *vmode_g)
94 if (GET_GLOBAL(vmode_g->class) == TEXT) {
95 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0720, 32*1024);
98 if (GET_GLOBAL(vmode_g->svgamode) < 0x0d) {
99 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 32*1024);
102 outb(0x02, VGAREG_SEQU_ADDRESS);
103 u8 mmask = inb(VGAREG_SEQU_DATA);
104 outb(0x0f, VGAREG_SEQU_DATA); // all planes
105 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 64*1024);
106 outb(mmask, VGAREG_SEQU_DATA);
110 biosfn_scroll(u8 nblines, u8 attr, u8 rul, u8 cul, u8 rlr, u8 clr, u8 page,
113 // page == 0xFF if current
120 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
124 // Get the dimensions
125 u16 nbrows = GET_BDA(video_rows) + 1;
126 u16 nbcols = GET_BDA(video_cols);
128 // Get the current page
130 page = GET_BDA(video_page);
136 if (nblines > nbrows)
138 u8 cols = clr - cul + 1;
140 if (GET_GLOBAL(vmode_g->class) == TEXT) {
141 // Compute the address
142 void *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, page));
143 dprintf(3, "Scroll, address %p (%d %d %02x)\n"
144 , address_far, nbrows, nbcols, page);
146 if (nblines == 0 && rul == 0 && cul == 0 && rlr == nbrows - 1
147 && clr == nbcols - 1) {
148 memset16_far(GET_GLOBAL(vmode_g->sstart), address_far
149 , (u16)attr * 0x100 + ' ', nbrows * nbcols * 2);
150 } else { // if Scroll up
151 if (dir == SCROLL_UP) {
153 for (i = rul; i <= rlr; i++)
154 if ((i + nblines > rlr) || (nblines == 0))
155 memset16_far(GET_GLOBAL(vmode_g->sstart)
156 , address_far + (i * nbcols + cul) * 2
157 , (u16)attr * 0x100 + ' ', cols * 2);
159 memcpy16_far(GET_GLOBAL(vmode_g->sstart)
160 , address_far + (i * nbcols + cul) * 2
161 , GET_GLOBAL(vmode_g->sstart)
162 , (void*)(((i + nblines) * nbcols + cul) * 2)
166 for (i = rlr; i >= rul; i--) {
167 if ((i < rul + nblines) || (nblines == 0))
168 memset16_far(GET_GLOBAL(vmode_g->sstart)
169 , address_far + (i * nbcols + cul) * 2
170 , (u16)attr * 0x100 + ' ', cols * 2);
172 memcpy16_far(GET_GLOBAL(vmode_g->sstart)
173 , address_far + (i * nbcols + cul) * 2
174 , GET_GLOBAL(vmode_g->sstart)
175 , (void*)(((i - nblines) * nbcols + cul) * 2)
185 // FIXME gfx mode not complete
186 struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
187 u8 cheight = GET_GLOBAL(vparam_g->cheight);
188 switch (GET_GLOBAL(vmode_g->memmodel)) {
191 if (nblines == 0 && rul == 0 && cul == 0 && rlr == nbrows - 1
192 && clr == nbcols - 1) {
193 outw(0x0205, VGAREG_GRDC_ADDRESS);
194 memset_far(GET_GLOBAL(vmode_g->sstart), 0, attr,
195 nbrows * nbcols * cheight);
196 outw(0x0005, VGAREG_GRDC_ADDRESS);
197 } else { // if Scroll up
198 if (dir == SCROLL_UP) {
200 for (i = rul; i <= rlr; i++)
201 if ((i + nblines > rlr) || (nblines == 0))
202 vgamem_fill_pl4(cul, i, cols, nbcols, cheight,
205 vgamem_copy_pl4(cul, i + nblines, i, cols,
209 for (i = rlr; i >= rul; i--) {
210 if ((i < rul + nblines) || (nblines == 0))
211 vgamem_fill_pl4(cul, i, cols, nbcols, cheight,
214 vgamem_copy_pl4(cul, i, i - nblines, cols,
223 u8 bpp = GET_GLOBAL(vmode_g->pixbits);
224 if (nblines == 0 && rul == 0 && cul == 0 && rlr == nbrows - 1
225 && clr == nbcols - 1) {
226 memset_far(GET_GLOBAL(vmode_g->sstart), 0, attr,
227 nbrows * nbcols * cheight * bpp);
235 if (dir == SCROLL_UP) {
237 for (i = rul; i <= rlr; i++)
238 if ((i + nblines > rlr) || (nblines == 0))
239 vgamem_fill_cga(cul, i, cols, nbcols, cheight,
242 vgamem_copy_cga(cul, i + nblines, i, cols,
246 for (i = rlr; i >= rul; i--) {
247 if ((i < rul + nblines) || (nblines == 0))
248 vgamem_fill_cga(cul, i, cols, nbcols, cheight,
251 vgamem_copy_cga(cul, i, i - nblines, cols,
261 dprintf(1, "Scroll in graphics mode\n");
266 /****************************************************************
267 * Read/write characters to screen
268 ****************************************************************/
271 write_gfx_char_pl4(u8 car, u8 attr, u8 xcurs, u8 ycurs, u8 nbcols,
285 u16 addr = xcurs + ycurs * cheight * nbcols;
286 u16 src = car * cheight;
287 outw(0x0f02, VGAREG_SEQU_ADDRESS);
288 outw(0x0205, VGAREG_GRDC_ADDRESS);
290 outw(0x1803, VGAREG_GRDC_ADDRESS);
292 outw(0x0003, VGAREG_GRDC_ADDRESS);
294 for (i = 0; i < cheight; i++) {
295 u8 *dest_far = (void*)(addr + i * nbcols);
297 for (j = 0; j < 8; j++) {
299 outw((mask << 8) | 0x08, VGAREG_GRDC_ADDRESS);
300 GET_FARVAR(SEG_GRAPH, *dest_far);
301 if (GET_GLOBAL(fdata_g[src + i]) & mask)
302 SET_FARVAR(SEG_GRAPH, *dest_far, attr & 0x0f);
304 SET_FARVAR(SEG_GRAPH, *dest_far, 0x00);
307 outw(0xff08, VGAREG_GRDC_ADDRESS);
308 outw(0x0005, VGAREG_GRDC_ADDRESS);
309 outw(0x0003, VGAREG_GRDC_ADDRESS);
313 write_gfx_char_cga(u8 car, u8 attr, u8 xcurs, u8 ycurs, u8 nbcols, u8 bpp)
315 u8 *fdata_g = vgafont8;
316 u16 addr = (xcurs * bpp) + ycurs * 320;
319 for (i = 0; i < 8; i++) {
320 u8 *dest_far = (void*)(addr + (i >> 1) * 80);
327 data = GET_FARVAR(SEG_CTEXT, *dest_far);
329 for (j = 0; j < 8; j++) {
330 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
332 data ^= (attr & 0x01) << (7 - j);
334 data |= (attr & 0x01) << (7 - j);
338 SET_FARVAR(SEG_CTEXT, *dest_far, data);
343 data = GET_FARVAR(SEG_CTEXT, *dest_far);
345 for (j = 0; j < 4; j++) {
346 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
348 data ^= (attr & 0x03) << ((3 - j) * 2);
350 data |= (attr & 0x03) << ((3 - j) * 2);
354 SET_FARVAR(SEG_CTEXT, *dest_far, data);
362 write_gfx_char_lin(u8 car, u8 attr, u8 xcurs, u8 ycurs, u8 nbcols)
364 u8 *fdata_g = vgafont8;
365 u16 addr = xcurs * 8 + ycurs * nbcols * 64;
368 for (i = 0; i < 8; i++) {
369 u8 *dest_far = (void*)(addr + i * nbcols * 8);
372 for (j = 0; j < 8; j++) {
374 if (GET_GLOBAL(fdata_g[src + i]) & mask)
376 SET_FARVAR(SEG_GRAPH, dest_far[j], data);
383 biosfn_write_char_attr(u8 car, u8 page, u8 attr, u16 count)
386 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
390 // Get the cursor pos for the page
391 u16 cursor = biosfn_get_cursor_pos(page);
392 u8 xcurs = cursor & 0x00ff;
393 u8 ycurs = (cursor & 0xff00) >> 8;
395 // Get the dimensions
396 u16 nbrows = GET_BDA(video_rows) + 1;
397 u16 nbcols = GET_BDA(video_cols);
399 if (GET_GLOBAL(vmode_g->class) == TEXT) {
400 // Compute the address
401 void *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, page)
402 + (xcurs + ycurs * nbcols) * 2);
404 u16 dummy = ((u16)attr << 8) + car;
405 memset16_far(GET_GLOBAL(vmode_g->sstart), address_far, dummy, count * 2);
409 // FIXME gfx mode not complete
410 struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
411 u8 cheight = GET_GLOBAL(vparam_g->cheight);
412 u8 bpp = GET_GLOBAL(vmode_g->pixbits);
413 while ((count-- > 0) && (xcurs < nbcols)) {
414 switch (GET_GLOBAL(vmode_g->memmodel)) {
417 write_gfx_char_pl4(car, attr, xcurs, ycurs, nbcols, cheight);
420 write_gfx_char_cga(car, attr, xcurs, ycurs, nbcols, bpp);
423 write_gfx_char_lin(car, attr, xcurs, ycurs, nbcols);
431 biosfn_write_char_only(u8 car, u8 page, u8 attr, u16 count)
434 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
438 // Get the cursor pos for the page
439 u16 cursor = biosfn_get_cursor_pos(page);
440 u8 xcurs = cursor & 0x00ff;
441 u8 ycurs = (cursor & 0xff00) >> 8;
443 // Get the dimensions
444 u16 nbrows = GET_BDA(video_rows) + 1;
445 u16 nbcols = GET_BDA(video_cols);
447 if (GET_GLOBAL(vmode_g->class) == TEXT) {
448 // Compute the address
449 u8 *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, page)
450 + (xcurs + ycurs * nbcols) * 2);
451 while (count-- > 0) {
452 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *address_far, car);
458 // FIXME gfx mode not complete
459 struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
460 u8 cheight = GET_GLOBAL(vparam_g->cheight);
461 u8 bpp = GET_GLOBAL(vmode_g->pixbits);
462 while ((count-- > 0) && (xcurs < nbcols)) {
463 switch (GET_GLOBAL(vmode_g->memmodel)) {
466 write_gfx_char_pl4(car, attr, xcurs, ycurs, nbcols, cheight);
469 write_gfx_char_cga(car, attr, xcurs, ycurs, nbcols, bpp);
472 write_gfx_char_lin(car, attr, xcurs, ycurs, nbcols);
480 biosfn_read_char_attr(u8 page, u16 *car)
483 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
487 // Get the cursor pos for the page
488 u16 cursor = biosfn_get_cursor_pos(page);
489 u8 xcurs = cursor & 0x00ff;
490 u8 ycurs = (cursor & 0xff00) >> 8;
492 // Get the dimensions
493 u16 nbrows = GET_BDA(video_rows) + 1;
494 u16 nbcols = GET_BDA(video_cols);
496 if (GET_GLOBAL(vmode_g->class) == TEXT) {
497 // Compute the address
498 u16 *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, page)
499 + (xcurs + ycurs * nbcols) * 2);
501 *car = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *address_far);
504 dprintf(1, "Read char in graphics mode\n");
509 /****************************************************************
511 ****************************************************************/
514 biosfn_write_pixel(u8 BH, u8 AL, u16 CX, u16 DX)
517 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
520 if (GET_GLOBAL(vmode_g->class) == TEXT)
523 u8 *addr_far, mask, attr, data;
524 switch (GET_GLOBAL(vmode_g->memmodel)) {
527 addr_far = (void*)(CX / 8 + DX * GET_BDA(video_cols));
528 mask = 0x80 >> (CX & 0x07);
529 outw((mask << 8) | 0x08, VGAREG_GRDC_ADDRESS);
530 outw(0x0205, VGAREG_GRDC_ADDRESS);
531 data = GET_FARVAR(SEG_GRAPH, *addr_far);
533 outw(0x1803, VGAREG_GRDC_ADDRESS);
534 SET_FARVAR(SEG_GRAPH, *addr_far, AL);
535 outw(0xff08, VGAREG_GRDC_ADDRESS);
536 outw(0x0005, VGAREG_GRDC_ADDRESS);
537 outw(0x0003, VGAREG_GRDC_ADDRESS);
540 if (GET_GLOBAL(vmode_g->pixbits) == 2)
541 addr_far = (void*)((CX >> 2) + (DX >> 1) * 80);
543 addr_far = (void*)((CX >> 3) + (DX >> 1) * 80);
546 data = GET_FARVAR(SEG_CTEXT, *addr_far);
547 if (GET_GLOBAL(vmode_g->pixbits) == 2) {
548 attr = (AL & 0x03) << ((3 - (CX & 0x03)) * 2);
549 mask = 0x03 << ((3 - (CX & 0x03)) * 2);
551 attr = (AL & 0x01) << (7 - (CX & 0x07));
552 mask = 0x01 << (7 - (CX & 0x07));
560 SET_FARVAR(SEG_CTEXT, *addr_far, data);
563 addr_far = (void*)(CX + DX * (GET_BDA(video_cols) * 8));
564 SET_FARVAR(SEG_GRAPH, *addr_far, AL);
570 biosfn_read_pixel(u8 BH, u16 CX, u16 DX, u16 *AX)
573 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
576 if (GET_GLOBAL(vmode_g->class) == TEXT)
579 u8 *addr_far, mask, attr=0, data, i;
580 switch (GET_GLOBAL(vmode_g->memmodel)) {
583 addr_far = (void*)(CX / 8 + DX * GET_BDA(video_cols));
584 mask = 0x80 >> (CX & 0x07);
586 for (i = 0; i < 4; i++) {
587 outw((i << 8) | 0x04, VGAREG_GRDC_ADDRESS);
588 data = GET_FARVAR(SEG_GRAPH, *addr_far) & mask;
594 addr_far = (void*)((CX >> 2) + (DX >> 1) * 80);
597 data = GET_FARVAR(SEG_CTEXT, *addr_far);
598 if (GET_GLOBAL(vmode_g->pixbits) == 2)
599 attr = (data >> ((3 - (CX & 0x03)) * 2)) & 0x03;
601 attr = (data >> (7 - (CX & 0x07))) & 0x01;
604 addr_far = (void*)(CX + DX * (GET_BDA(video_cols) * 8));
605 attr = GET_FARVAR(SEG_GRAPH, *addr_far);
608 *AX = (*AX & 0xff00) | attr;
612 /****************************************************************
614 ****************************************************************/
617 vgafb_load_font(u16 seg, void *src_far, u16 count
618 , u16 start, u8 destflags, u8 fontsize)
621 u16 blockaddr = ((destflags & 0x03) << 14) + ((destflags & 0x04) << 11);
622 void *dest_far = (void*)(blockaddr + start*32);
624 for (i = 0; i < count; i++)
625 memcpy_far(SEG_GRAPH, dest_far + i*32
626 , seg, src_far + i*fontsize, fontsize);
627 release_font_access();