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 // * normalize params (don't use AX/BX/CX/etc.)
19 memcpy16_far(u16 d_seg, void *d_far, u16 s_seg, const void *s_far, size_t len)
21 memcpy_far(d_seg, d_far, s_seg, s_far, len);
25 /****************************************************************
27 ****************************************************************/
30 vgamem_copy_pl4(u8 xstart, u8 ysrc, u8 ydest, u8 cols, u8 nbcols,
33 u16 src = ysrc * cheight * nbcols + xstart;
34 u16 dest = ydest * cheight * nbcols + xstart;
35 outw(0x0105, VGAREG_GRDC_ADDRESS);
37 for (i = 0; i < cheight; i++)
38 memcpy_far(SEG_GRAPH, (void*)(dest + i * nbcols)
39 , SEG_GRAPH, (void*)(src + i * nbcols), cols);
40 outw(0x0005, VGAREG_GRDC_ADDRESS);
44 vgamem_fill_pl4(u8 xstart, u8 ystart, u8 cols, u8 nbcols, u8 cheight,
47 u16 dest = ystart * cheight * nbcols + xstart;
48 outw(0x0205, VGAREG_GRDC_ADDRESS);
50 for (i = 0; i < cheight; i++)
51 memset_far(SEG_GRAPH, (void*)(dest + i * nbcols), attr, cols);
52 outw(0x0005, VGAREG_GRDC_ADDRESS);
56 scroll_pl4(struct vgamode_s *vmode_g, int nblines, int attr
57 , struct cursorpos ul, struct cursorpos lr)
67 u16 nbrows = GET_BDA(video_rows) + 1;
68 u16 nbcols = GET_BDA(video_cols);
71 u8 cols = lr.x - ul.x + 1;
73 struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
74 u8 cheight = GET_GLOBAL(vparam_g->cheight);
75 if (nblines == 0 && ul.y == 0 && ul.x == 0 && lr.y == nbrows - 1
76 && lr.x == nbcols - 1) {
77 outw(0x0205, VGAREG_GRDC_ADDRESS);
78 memset_far(GET_GLOBAL(vmode_g->sstart), 0, attr,
79 nbrows * nbcols * cheight);
80 outw(0x0005, VGAREG_GRDC_ADDRESS);
83 if (dir == SCROLL_UP) {
85 for (i = ul.y; i <= lr.y; i++)
86 if ((i + nblines > lr.y) || (nblines == 0))
87 vgamem_fill_pl4(ul.x, i, cols, nbcols, cheight,
90 vgamem_copy_pl4(ul.x, i + nblines, i, cols,
95 for (i = lr.y; i >= ul.y; i--) {
96 if ((i < ul.y + nblines) || (nblines == 0))
97 vgamem_fill_pl4(ul.x, i, cols, nbcols, cheight,
100 vgamem_copy_pl4(ul.x, i, i - nblines, cols,
108 vgamem_copy_cga(u8 xstart, u8 ysrc, u8 ydest, u8 cols, u8 nbcols,
111 u16 src = ((ysrc * cheight * nbcols) >> 1) + xstart;
112 u16 dest = ((ydest * cheight * nbcols) >> 1) + xstart;
114 for (i = 0; i < cheight; i++)
116 memcpy_far(SEG_CTEXT, (void*)(0x2000 + dest + (i >> 1) * nbcols)
117 , SEG_CTEXT, (void*)(0x2000 + src + (i >> 1) * nbcols)
120 memcpy_far(SEG_CTEXT, (void*)(dest + (i >> 1) * nbcols)
121 , SEG_CTEXT, (void*)(src + (i >> 1) * nbcols), cols);
125 vgamem_fill_cga(u8 xstart, u8 ystart, u8 cols, u8 nbcols, u8 cheight,
128 u16 dest = ((ystart * cheight * nbcols) >> 1) + xstart;
130 for (i = 0; i < cheight; i++)
132 memset_far(SEG_CTEXT, (void*)(0x2000 + dest + (i >> 1) * nbcols)
135 memset_far(SEG_CTEXT, (void*)(dest + (i >> 1) * nbcols), attr, cols);
139 scroll_cga(struct vgamode_s *vmode_g, int nblines, int attr
140 , struct cursorpos ul, struct cursorpos lr)
149 // Get the dimensions
150 u16 nbrows = GET_BDA(video_rows) + 1;
151 u16 nbcols = GET_BDA(video_cols);
152 if (nblines > nbrows)
154 u8 cols = lr.x - ul.x + 1;
156 struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
157 u8 cheight = GET_GLOBAL(vparam_g->cheight);
158 u8 bpp = GET_GLOBAL(vmode_g->pixbits);
159 if (nblines == 0 && ul.y == 0 && ul.x == 0 && lr.y == nbrows - 1
160 && lr.x == nbcols - 1) {
161 memset_far(GET_GLOBAL(vmode_g->sstart), 0, attr,
162 nbrows * nbcols * cheight * bpp);
171 if (dir == SCROLL_UP) {
173 for (i = ul.y; i <= lr.y; i++)
174 if ((i + nblines > lr.y) || (nblines == 0))
175 vgamem_fill_cga(ul.x, i, cols, nbcols, cheight,
178 vgamem_copy_cga(ul.x, i + nblines, i, cols,
183 for (i = lr.y; i >= ul.y; i--) {
184 if ((i < ul.y + nblines) || (nblines == 0))
185 vgamem_fill_cga(ul.x, i, cols, nbcols, cheight,
188 vgamem_copy_cga(ul.x, i, i - nblines, cols,
196 scroll_text(struct vgamode_s *vmode_g, int nblines, int attr
197 , struct cursorpos ul, struct cursorpos lr)
206 // Get the dimensions
207 u16 nbrows = GET_BDA(video_rows) + 1;
208 u16 nbcols = GET_BDA(video_cols);
210 if (nblines > nbrows)
212 u8 cols = lr.x - ul.x + 1;
214 // Compute the address
215 void *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, ul.page));
216 dprintf(3, "Scroll, address %p (%d %d %02x)\n"
217 , address_far, nbrows, nbcols, ul.page);
219 if (nblines == 0 && ul.y == 0 && ul.x == 0 && lr.y == nbrows - 1
220 && lr.x == nbcols - 1) {
221 memset16_far(GET_GLOBAL(vmode_g->sstart), address_far
222 , (u16)attr * 0x100 + ' ', nbrows * nbcols * 2);
225 if (dir == SCROLL_UP) {
227 for (i = ul.y; i <= lr.y; i++)
228 if ((i + nblines > lr.y) || (nblines == 0))
229 memset16_far(GET_GLOBAL(vmode_g->sstart)
230 , address_far + (i * nbcols + ul.x) * 2
231 , (u16)attr * 0x100 + ' ', cols * 2);
233 memcpy16_far(GET_GLOBAL(vmode_g->sstart)
234 , address_far + (i * nbcols + ul.x) * 2
235 , GET_GLOBAL(vmode_g->sstart)
236 , (void*)(((i + nblines) * nbcols + ul.x) * 2)
242 for (i = lr.y; i >= ul.y; i--) {
243 if ((i < ul.y + nblines) || (nblines == 0))
244 memset16_far(GET_GLOBAL(vmode_g->sstart)
245 , address_far + (i * nbcols + ul.x) * 2
246 , (u16)attr * 0x100 + ' ', cols * 2);
248 memcpy16_far(GET_GLOBAL(vmode_g->sstart)
249 , address_far + (i * nbcols + ul.x) * 2
250 , GET_GLOBAL(vmode_g->sstart)
251 , (void*)(((i - nblines) * nbcols + ul.x) * 2)
259 vgafb_scroll(int nblines, int attr, struct cursorpos ul, struct cursorpos lr)
262 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
266 // FIXME gfx mode not complete
267 switch (GET_GLOBAL(vmode_g->memmodel)) {
270 scroll_text(vmode_g, nblines, attr, ul, lr);
274 scroll_pl4(vmode_g, nblines, attr, ul, lr);
277 scroll_cga(vmode_g, nblines, attr, ul, lr);
280 dprintf(1, "Scroll in graphics mode\n");
285 clear_screen(struct vgamode_s *vmode_g)
287 if (GET_GLOBAL(vmode_g->memmodel) & TEXT) {
288 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0720, 32*1024);
291 if (GET_GLOBAL(vmode_g->svgamode) < 0x0d) {
292 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 32*1024);
295 outb(0x02, VGAREG_SEQU_ADDRESS);
296 u8 mmask = inb(VGAREG_SEQU_DATA);
297 outb(0x0f, VGAREG_SEQU_DATA); // all planes
298 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 64*1024);
299 outb(mmask, VGAREG_SEQU_DATA);
303 /****************************************************************
304 * Read/write characters to screen
305 ****************************************************************/
308 write_gfx_char_pl4(struct vgamode_s *vmode_g
309 , struct cursorpos cp, struct carattr ca)
311 u16 nbcols = GET_BDA(video_cols);
315 struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
316 u8 cheight = GET_GLOBAL(vparam_g->cheight);
328 u16 addr = cp.x + cp.y * cheight * nbcols;
329 u16 src = ca.car * cheight;
330 outw(0x0f02, VGAREG_SEQU_ADDRESS);
331 outw(0x0205, VGAREG_GRDC_ADDRESS);
333 outw(0x1803, VGAREG_GRDC_ADDRESS);
335 outw(0x0003, VGAREG_GRDC_ADDRESS);
337 for (i = 0; i < cheight; i++) {
338 u8 *dest_far = (void*)(addr + i * nbcols);
340 for (j = 0; j < 8; j++) {
342 outw((mask << 8) | 0x08, VGAREG_GRDC_ADDRESS);
343 GET_FARVAR(SEG_GRAPH, *dest_far);
344 if (GET_GLOBAL(fdata_g[src + i]) & mask)
345 SET_FARVAR(SEG_GRAPH, *dest_far, ca.attr & 0x0f);
347 SET_FARVAR(SEG_GRAPH, *dest_far, 0x00);
350 outw(0xff08, VGAREG_GRDC_ADDRESS);
351 outw(0x0005, VGAREG_GRDC_ADDRESS);
352 outw(0x0003, VGAREG_GRDC_ADDRESS);
356 write_gfx_char_cga(struct vgamode_s *vmode_g
357 , struct cursorpos cp, struct carattr ca)
359 u16 nbcols = GET_BDA(video_cols);
363 u8 *fdata_g = vgafont8;
364 u8 bpp = GET_GLOBAL(vmode_g->pixbits);
365 u16 addr = (cp.x * bpp) + cp.y * 320;
366 u16 src = ca.car * 8;
368 for (i = 0; i < 8; i++) {
369 u8 *dest_far = (void*)(addr + (i >> 1) * 80);
376 data = GET_FARVAR(SEG_CTEXT, *dest_far);
378 for (j = 0; j < 8; j++) {
379 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
381 data ^= (ca.attr & 0x01) << (7 - j);
383 data |= (ca.attr & 0x01) << (7 - j);
387 SET_FARVAR(SEG_CTEXT, *dest_far, data);
392 data = GET_FARVAR(SEG_CTEXT, *dest_far);
394 for (j = 0; j < 4; j++) {
395 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
397 data ^= (ca.attr & 0x03) << ((3 - j) * 2);
399 data |= (ca.attr & 0x03) << ((3 - j) * 2);
403 SET_FARVAR(SEG_CTEXT, *dest_far, data);
411 write_gfx_char_lin(struct vgamode_s *vmode_g
412 , struct cursorpos cp, struct carattr ca)
414 // Get the dimensions
415 u16 nbcols = GET_BDA(video_cols);
419 u8 *fdata_g = vgafont8;
420 u16 addr = cp.x * 8 + cp.y * nbcols * 64;
421 u16 src = ca.car * 8;
423 for (i = 0; i < 8; i++) {
424 u8 *dest_far = (void*)(addr + i * nbcols * 8);
427 for (j = 0; j < 8; j++) {
429 if (GET_GLOBAL(fdata_g[src + i]) & mask)
431 SET_FARVAR(SEG_GRAPH, dest_far[j], data);
438 write_text_char(struct vgamode_s *vmode_g
439 , struct cursorpos cp, struct carattr ca)
441 // Get the dimensions
442 u16 nbrows = GET_BDA(video_rows) + 1;
443 u16 nbcols = GET_BDA(video_cols);
445 // Compute the address
446 void *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, cp.page)
447 + (cp.x + cp.y * nbcols) * 2);
450 u16 dummy = (ca.attr << 8) | ca.car;
451 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u16*)address_far, dummy);
453 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)address_far, ca.car);
458 vgafb_write_char(struct cursorpos cp, struct carattr ca)
461 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
465 // FIXME gfx mode not complete
466 switch (GET_GLOBAL(vmode_g->memmodel)) {
469 write_text_char(vmode_g, cp, ca);
473 write_gfx_char_pl4(vmode_g, cp, ca);
476 write_gfx_char_cga(vmode_g, cp, ca);
479 write_gfx_char_lin(vmode_g, cp, ca);
485 vgafb_read_char(struct cursorpos cp)
488 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
492 if (!(GET_GLOBAL(vmode_g->memmodel) & TEXT)) {
494 dprintf(1, "Read char in graphics mode\n");
498 // Get the dimensions
499 u16 nbrows = GET_BDA(video_rows) + 1;
500 u16 nbcols = GET_BDA(video_cols);
502 // Compute the address
503 u16 *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, cp.page)
504 + (cp.x + cp.y * nbcols) * 2);
505 u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *address_far);
506 struct carattr ca = {v, v>>8, 0};
510 struct carattr ca2 = {0, 0, 0};
515 /****************************************************************
517 ****************************************************************/
520 biosfn_write_pixel(u8 BH, u8 AL, u16 CX, u16 DX)
523 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
526 if (GET_GLOBAL(vmode_g->memmodel) & TEXT)
529 u8 *addr_far, mask, attr, data;
530 switch (GET_GLOBAL(vmode_g->memmodel)) {
533 addr_far = (void*)(CX / 8 + DX * GET_BDA(video_cols));
534 mask = 0x80 >> (CX & 0x07);
535 outw((mask << 8) | 0x08, VGAREG_GRDC_ADDRESS);
536 outw(0x0205, VGAREG_GRDC_ADDRESS);
537 data = GET_FARVAR(SEG_GRAPH, *addr_far);
539 outw(0x1803, VGAREG_GRDC_ADDRESS);
540 SET_FARVAR(SEG_GRAPH, *addr_far, AL);
541 outw(0xff08, VGAREG_GRDC_ADDRESS);
542 outw(0x0005, VGAREG_GRDC_ADDRESS);
543 outw(0x0003, VGAREG_GRDC_ADDRESS);
546 if (GET_GLOBAL(vmode_g->pixbits) == 2)
547 addr_far = (void*)((CX >> 2) + (DX >> 1) * 80);
549 addr_far = (void*)((CX >> 3) + (DX >> 1) * 80);
552 data = GET_FARVAR(SEG_CTEXT, *addr_far);
553 if (GET_GLOBAL(vmode_g->pixbits) == 2) {
554 attr = (AL & 0x03) << ((3 - (CX & 0x03)) * 2);
555 mask = 0x03 << ((3 - (CX & 0x03)) * 2);
557 attr = (AL & 0x01) << (7 - (CX & 0x07));
558 mask = 0x01 << (7 - (CX & 0x07));
566 SET_FARVAR(SEG_CTEXT, *addr_far, data);
569 addr_far = (void*)(CX + DX * (GET_BDA(video_cols) * 8));
570 SET_FARVAR(SEG_GRAPH, *addr_far, AL);
576 biosfn_read_pixel(u8 BH, u16 CX, u16 DX, u16 *AX)
579 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
582 if (GET_GLOBAL(vmode_g->memmodel) & TEXT)
585 u8 *addr_far, mask, attr=0, data, i;
586 switch (GET_GLOBAL(vmode_g->memmodel)) {
589 addr_far = (void*)(CX / 8 + DX * GET_BDA(video_cols));
590 mask = 0x80 >> (CX & 0x07);
592 for (i = 0; i < 4; i++) {
593 outw((i << 8) | 0x04, VGAREG_GRDC_ADDRESS);
594 data = GET_FARVAR(SEG_GRAPH, *addr_far) & mask;
600 addr_far = (void*)((CX >> 2) + (DX >> 1) * 80);
603 data = GET_FARVAR(SEG_CTEXT, *addr_far);
604 if (GET_GLOBAL(vmode_g->pixbits) == 2)
605 attr = (data >> ((3 - (CX & 0x03)) * 2)) & 0x03;
607 attr = (data >> (7 - (CX & 0x07))) & 0x01;
610 addr_far = (void*)(CX + DX * (GET_BDA(video_cols) * 8));
611 attr = GET_FARVAR(SEG_GRAPH, *addr_far);
614 *AX = (*AX & 0xff00) | attr;
618 /****************************************************************
620 ****************************************************************/
623 vgafb_load_font(u16 seg, void *src_far, u16 count
624 , u16 start, u8 destflags, u8 fontsize)
627 u16 blockaddr = ((destflags & 0x03) << 14) + ((destflags & 0x04) << 11);
628 void *dest_far = (void*)(blockaddr + start*32);
630 for (i = 0; i < count; i++)
631 memcpy_far(SEG_GRAPH, dest_far + i*32
632 , seg, src_far + i*fontsize, fontsize);
633 release_font_access();