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 vgamem_copy_cga(u8 xstart, u8 ysrc, u8 ydest, u8 cols, u8 nbcols,
59 u16 src = ((ysrc * cheight * nbcols) >> 1) + xstart;
60 u16 dest = ((ydest * cheight * nbcols) >> 1) + xstart;
62 for (i = 0; i < cheight; i++)
64 memcpy_far(SEG_CTEXT, (void*)(0x2000 + dest + (i >> 1) * nbcols)
65 , SEG_CTEXT, (void*)(0x2000 + src + (i >> 1) * nbcols)
68 memcpy_far(SEG_CTEXT, (void*)(dest + (i >> 1) * nbcols)
69 , SEG_CTEXT, (void*)(src + (i >> 1) * nbcols), cols);
73 vgamem_fill_cga(u8 xstart, u8 ystart, u8 cols, u8 nbcols, u8 cheight,
76 u16 dest = ((ystart * cheight * nbcols) >> 1) + xstart;
78 for (i = 0; i < cheight; i++)
80 memset_far(SEG_CTEXT, (void*)(0x2000 + dest + (i >> 1) * nbcols)
83 memset_far(SEG_CTEXT, (void*)(dest + (i >> 1) * nbcols), attr, cols);
87 clear_screen(struct vgamode_s *vmode_g)
89 if (GET_GLOBAL(vmode_g->memmodel) & TEXT) {
90 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0720, 32*1024);
93 if (GET_GLOBAL(vmode_g->svgamode) < 0x0d) {
94 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 32*1024);
97 outb(0x02, VGAREG_SEQU_ADDRESS);
98 u8 mmask = inb(VGAREG_SEQU_DATA);
99 outb(0x0f, VGAREG_SEQU_DATA); // all planes
100 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 64*1024);
101 outb(mmask, VGAREG_SEQU_DATA);
105 biosfn_scroll(u8 nblines, u8 attr, u8 rul, u8 cul, u8 rlr, u8 clr, u8 page,
108 // page == 0xFF if current
115 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
119 // Get the dimensions
120 u16 nbrows = GET_BDA(video_rows) + 1;
121 u16 nbcols = GET_BDA(video_cols);
123 // Get the current page
125 page = GET_BDA(video_page);
131 if (nblines > nbrows)
133 u8 cols = clr - cul + 1;
135 if (GET_GLOBAL(vmode_g->memmodel) & TEXT) {
136 // Compute the address
137 void *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, page));
138 dprintf(3, "Scroll, address %p (%d %d %02x)\n"
139 , address_far, nbrows, nbcols, page);
141 if (nblines == 0 && rul == 0 && cul == 0 && rlr == nbrows - 1
142 && clr == nbcols - 1) {
143 memset16_far(GET_GLOBAL(vmode_g->sstart), address_far
144 , (u16)attr * 0x100 + ' ', nbrows * nbcols * 2);
145 } else { // if Scroll up
146 if (dir == SCROLL_UP) {
148 for (i = rul; i <= rlr; i++)
149 if ((i + nblines > rlr) || (nblines == 0))
150 memset16_far(GET_GLOBAL(vmode_g->sstart)
151 , address_far + (i * nbcols + cul) * 2
152 , (u16)attr * 0x100 + ' ', cols * 2);
154 memcpy16_far(GET_GLOBAL(vmode_g->sstart)
155 , address_far + (i * nbcols + cul) * 2
156 , GET_GLOBAL(vmode_g->sstart)
157 , (void*)(((i + nblines) * nbcols + cul) * 2)
161 for (i = rlr; i >= rul; i--) {
162 if ((i < rul + nblines) || (nblines == 0))
163 memset16_far(GET_GLOBAL(vmode_g->sstart)
164 , address_far + (i * nbcols + cul) * 2
165 , (u16)attr * 0x100 + ' ', cols * 2);
167 memcpy16_far(GET_GLOBAL(vmode_g->sstart)
168 , address_far + (i * nbcols + cul) * 2
169 , GET_GLOBAL(vmode_g->sstart)
170 , (void*)(((i - nblines) * nbcols + cul) * 2)
180 // FIXME gfx mode not complete
181 struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
182 u8 cheight = GET_GLOBAL(vparam_g->cheight);
183 switch (GET_GLOBAL(vmode_g->memmodel)) {
186 if (nblines == 0 && rul == 0 && cul == 0 && rlr == nbrows - 1
187 && clr == nbcols - 1) {
188 outw(0x0205, VGAREG_GRDC_ADDRESS);
189 memset_far(GET_GLOBAL(vmode_g->sstart), 0, attr,
190 nbrows * nbcols * cheight);
191 outw(0x0005, VGAREG_GRDC_ADDRESS);
192 } else { // if Scroll up
193 if (dir == SCROLL_UP) {
195 for (i = rul; i <= rlr; i++)
196 if ((i + nblines > rlr) || (nblines == 0))
197 vgamem_fill_pl4(cul, i, cols, nbcols, cheight,
200 vgamem_copy_pl4(cul, i + nblines, i, cols,
204 for (i = rlr; i >= rul; i--) {
205 if ((i < rul + nblines) || (nblines == 0))
206 vgamem_fill_pl4(cul, i, cols, nbcols, cheight,
209 vgamem_copy_pl4(cul, i, i - nblines, cols,
218 u8 bpp = GET_GLOBAL(vmode_g->pixbits);
219 if (nblines == 0 && rul == 0 && cul == 0 && rlr == nbrows - 1
220 && clr == nbcols - 1) {
221 memset_far(GET_GLOBAL(vmode_g->sstart), 0, attr,
222 nbrows * nbcols * cheight * bpp);
230 if (dir == SCROLL_UP) {
232 for (i = rul; i <= rlr; i++)
233 if ((i + nblines > rlr) || (nblines == 0))
234 vgamem_fill_cga(cul, i, cols, nbcols, cheight,
237 vgamem_copy_cga(cul, i + nblines, i, cols,
241 for (i = rlr; i >= rul; i--) {
242 if ((i < rul + nblines) || (nblines == 0))
243 vgamem_fill_cga(cul, i, cols, nbcols, cheight,
246 vgamem_copy_cga(cul, i, i - nblines, cols,
256 dprintf(1, "Scroll in graphics mode\n");
261 /****************************************************************
262 * Read/write characters to screen
263 ****************************************************************/
266 write_gfx_char_pl4(struct vgamode_s *vmode_g
267 , struct cursorpos cp, struct carattr ca)
269 u16 nbcols = GET_BDA(video_cols);
273 struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
274 u8 cheight = GET_GLOBAL(vparam_g->cheight);
286 u16 addr = cp.x + cp.y * cheight * nbcols;
287 u16 src = ca.car * cheight;
288 outw(0x0f02, VGAREG_SEQU_ADDRESS);
289 outw(0x0205, VGAREG_GRDC_ADDRESS);
291 outw(0x1803, VGAREG_GRDC_ADDRESS);
293 outw(0x0003, VGAREG_GRDC_ADDRESS);
295 for (i = 0; i < cheight; i++) {
296 u8 *dest_far = (void*)(addr + i * nbcols);
298 for (j = 0; j < 8; j++) {
300 outw((mask << 8) | 0x08, VGAREG_GRDC_ADDRESS);
301 GET_FARVAR(SEG_GRAPH, *dest_far);
302 if (GET_GLOBAL(fdata_g[src + i]) & mask)
303 SET_FARVAR(SEG_GRAPH, *dest_far, ca.attr & 0x0f);
305 SET_FARVAR(SEG_GRAPH, *dest_far, 0x00);
308 outw(0xff08, VGAREG_GRDC_ADDRESS);
309 outw(0x0005, VGAREG_GRDC_ADDRESS);
310 outw(0x0003, VGAREG_GRDC_ADDRESS);
314 write_gfx_char_cga(struct vgamode_s *vmode_g
315 , struct cursorpos cp, struct carattr ca)
317 u16 nbcols = GET_BDA(video_cols);
321 u8 *fdata_g = vgafont8;
322 u8 bpp = GET_GLOBAL(vmode_g->pixbits);
323 u16 addr = (cp.x * bpp) + cp.y * 320;
324 u16 src = ca.car * 8;
326 for (i = 0; i < 8; i++) {
327 u8 *dest_far = (void*)(addr + (i >> 1) * 80);
334 data = GET_FARVAR(SEG_CTEXT, *dest_far);
336 for (j = 0; j < 8; j++) {
337 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
339 data ^= (ca.attr & 0x01) << (7 - j);
341 data |= (ca.attr & 0x01) << (7 - j);
345 SET_FARVAR(SEG_CTEXT, *dest_far, data);
350 data = GET_FARVAR(SEG_CTEXT, *dest_far);
352 for (j = 0; j < 4; j++) {
353 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
355 data ^= (ca.attr & 0x03) << ((3 - j) * 2);
357 data |= (ca.attr & 0x03) << ((3 - j) * 2);
361 SET_FARVAR(SEG_CTEXT, *dest_far, data);
369 write_gfx_char_lin(struct vgamode_s *vmode_g
370 , struct cursorpos cp, struct carattr ca)
372 // Get the dimensions
373 u16 nbcols = GET_BDA(video_cols);
377 u8 *fdata_g = vgafont8;
378 u16 addr = cp.x * 8 + cp.y * nbcols * 64;
379 u16 src = ca.car * 8;
381 for (i = 0; i < 8; i++) {
382 u8 *dest_far = (void*)(addr + i * nbcols * 8);
385 for (j = 0; j < 8; j++) {
387 if (GET_GLOBAL(fdata_g[src + i]) & mask)
389 SET_FARVAR(SEG_GRAPH, dest_far[j], data);
396 write_text_char(struct vgamode_s *vmode_g
397 , struct cursorpos cp, struct carattr ca)
399 // Get the dimensions
400 u16 nbrows = GET_BDA(video_rows) + 1;
401 u16 nbcols = GET_BDA(video_cols);
403 // Compute the address
404 void *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, cp.page)
405 + (cp.x + cp.y * nbcols) * 2);
408 u16 dummy = (ca.attr << 8) | ca.car;
409 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u16*)address_far, dummy);
411 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)address_far, ca.car);
416 vgafb_write_char(struct cursorpos cp, struct carattr ca)
419 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
423 // FIXME gfx mode not complete
424 switch (GET_GLOBAL(vmode_g->memmodel)) {
427 write_text_char(vmode_g, cp, ca);
431 write_gfx_char_pl4(vmode_g, cp, ca);
434 write_gfx_char_cga(vmode_g, cp, ca);
437 write_gfx_char_lin(vmode_g, cp, ca);
443 vgafb_read_char(struct cursorpos cp)
446 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
450 if (!(GET_GLOBAL(vmode_g->memmodel) & TEXT)) {
452 dprintf(1, "Read char in graphics mode\n");
456 // Get the dimensions
457 u16 nbrows = GET_BDA(video_rows) + 1;
458 u16 nbcols = GET_BDA(video_cols);
460 // Compute the address
461 u16 *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, cp.page)
462 + (cp.x + cp.y * nbcols) * 2);
463 u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *address_far);
464 struct carattr ca = {v, v>>8, 0};
468 struct carattr ca2 = {0, 0, 0};
473 /****************************************************************
475 ****************************************************************/
478 biosfn_write_pixel(u8 BH, u8 AL, u16 CX, u16 DX)
481 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
484 if (GET_GLOBAL(vmode_g->memmodel) & TEXT)
487 u8 *addr_far, mask, attr, data;
488 switch (GET_GLOBAL(vmode_g->memmodel)) {
491 addr_far = (void*)(CX / 8 + DX * GET_BDA(video_cols));
492 mask = 0x80 >> (CX & 0x07);
493 outw((mask << 8) | 0x08, VGAREG_GRDC_ADDRESS);
494 outw(0x0205, VGAREG_GRDC_ADDRESS);
495 data = GET_FARVAR(SEG_GRAPH, *addr_far);
497 outw(0x1803, VGAREG_GRDC_ADDRESS);
498 SET_FARVAR(SEG_GRAPH, *addr_far, AL);
499 outw(0xff08, VGAREG_GRDC_ADDRESS);
500 outw(0x0005, VGAREG_GRDC_ADDRESS);
501 outw(0x0003, VGAREG_GRDC_ADDRESS);
504 if (GET_GLOBAL(vmode_g->pixbits) == 2)
505 addr_far = (void*)((CX >> 2) + (DX >> 1) * 80);
507 addr_far = (void*)((CX >> 3) + (DX >> 1) * 80);
510 data = GET_FARVAR(SEG_CTEXT, *addr_far);
511 if (GET_GLOBAL(vmode_g->pixbits) == 2) {
512 attr = (AL & 0x03) << ((3 - (CX & 0x03)) * 2);
513 mask = 0x03 << ((3 - (CX & 0x03)) * 2);
515 attr = (AL & 0x01) << (7 - (CX & 0x07));
516 mask = 0x01 << (7 - (CX & 0x07));
524 SET_FARVAR(SEG_CTEXT, *addr_far, data);
527 addr_far = (void*)(CX + DX * (GET_BDA(video_cols) * 8));
528 SET_FARVAR(SEG_GRAPH, *addr_far, AL);
534 biosfn_read_pixel(u8 BH, u16 CX, u16 DX, u16 *AX)
537 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
540 if (GET_GLOBAL(vmode_g->memmodel) & TEXT)
543 u8 *addr_far, mask, attr=0, data, i;
544 switch (GET_GLOBAL(vmode_g->memmodel)) {
547 addr_far = (void*)(CX / 8 + DX * GET_BDA(video_cols));
548 mask = 0x80 >> (CX & 0x07);
550 for (i = 0; i < 4; i++) {
551 outw((i << 8) | 0x04, VGAREG_GRDC_ADDRESS);
552 data = GET_FARVAR(SEG_GRAPH, *addr_far) & mask;
558 addr_far = (void*)((CX >> 2) + (DX >> 1) * 80);
561 data = GET_FARVAR(SEG_CTEXT, *addr_far);
562 if (GET_GLOBAL(vmode_g->pixbits) == 2)
563 attr = (data >> ((3 - (CX & 0x03)) * 2)) & 0x03;
565 attr = (data >> (7 - (CX & 0x07))) & 0x01;
568 addr_far = (void*)(CX + DX * (GET_BDA(video_cols) * 8));
569 attr = GET_FARVAR(SEG_GRAPH, *addr_far);
572 *AX = (*AX & 0xff00) | attr;
576 /****************************************************************
578 ****************************************************************/
581 vgafb_load_font(u16 seg, void *src_far, u16 count
582 , u16 start, u8 destflags, u8 fontsize)
585 u16 blockaddr = ((destflags & 0x03) << 14) + ((destflags & 0x04) << 11);
586 void *dest_far = (void*)(blockaddr + start*32);
588 for (i = 0; i < count; i++)
589 memcpy_far(SEG_GRAPH, dest_far + i*32
590 , seg, src_far + i*fontsize, fontsize);
591 release_font_access();