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 nbcols = GET_BDA(video_cols);
68 u8 cols = lr.x - ul.x + 1;
70 struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
71 u8 cheight = GET_GLOBAL(vparam_g->cheight);
72 if (dir == SCROLL_UP) {
74 for (i = ul.y; i <= lr.y; i++)
75 if (i + nblines > lr.y)
76 vgamem_fill_pl4(ul.x, i, cols, nbcols, cheight,
79 vgamem_copy_pl4(ul.x, i + nblines, i, cols,
84 for (i = lr.y; i >= ul.y; i--)
85 if (i < ul.y + nblines)
86 vgamem_fill_pl4(ul.x, i, cols, nbcols, cheight,
89 vgamem_copy_pl4(ul.x, i, i - nblines, cols,
94 vgamem_copy_cga(u8 xstart, u8 ysrc, u8 ydest, u8 cols, u8 nbcols,
97 u16 src = ((ysrc * cheight * nbcols) >> 1) + xstart;
98 u16 dest = ((ydest * cheight * nbcols) >> 1) + xstart;
100 for (i = 0; i < cheight; i++)
102 memcpy_far(SEG_CTEXT, (void*)(0x2000 + dest + (i >> 1) * nbcols)
103 , SEG_CTEXT, (void*)(0x2000 + src + (i >> 1) * nbcols)
106 memcpy_far(SEG_CTEXT, (void*)(dest + (i >> 1) * nbcols)
107 , SEG_CTEXT, (void*)(src + (i >> 1) * nbcols), cols);
111 vgamem_fill_cga(u8 xstart, u8 ystart, u8 cols, u8 nbcols, u8 cheight,
114 u16 dest = ((ystart * cheight * nbcols) >> 1) + xstart;
116 for (i = 0; i < cheight; i++)
118 memset_far(SEG_CTEXT, (void*)(0x2000 + dest + (i >> 1) * nbcols)
121 memset_far(SEG_CTEXT, (void*)(dest + (i >> 1) * nbcols), attr, cols);
125 scroll_cga(struct vgamode_s *vmode_g, int nblines, int attr
126 , struct cursorpos ul, struct cursorpos lr)
135 // Get the dimensions
136 u16 nbcols = GET_BDA(video_cols);
137 u8 cols = lr.x - ul.x + 1;
139 struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
140 u8 cheight = GET_GLOBAL(vparam_g->cheight);
141 u8 bpp = GET_GLOBAL(vmode_g->pixbits);
148 if (dir == SCROLL_UP) {
150 for (i = ul.y; i <= lr.y; i++)
151 if (i + nblines > lr.y)
152 vgamem_fill_cga(ul.x, i, cols, nbcols, cheight,
155 vgamem_copy_cga(ul.x, i + nblines, i, cols,
160 for (i = lr.y; i >= ul.y; i--)
161 if (i < ul.y + nblines)
162 vgamem_fill_cga(ul.x, i, cols, nbcols, cheight,
165 vgamem_copy_cga(ul.x, i, i - nblines, cols,
170 scroll_text(struct vgamode_s *vmode_g, int nblines, int attr
171 , struct cursorpos ul, struct cursorpos lr)
180 // Get the dimensions
181 u16 nbrows = GET_BDA(video_rows) + 1;
182 u16 nbcols = GET_BDA(video_cols);
183 u8 cols = lr.x - ul.x + 1;
185 // Compute the address
186 void *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, ul.page));
187 dprintf(3, "Scroll, address %p (%d %d %02x)\n"
188 , address_far, nbrows, nbcols, ul.page);
190 if (dir == SCROLL_UP) {
192 for (i = ul.y; i <= lr.y; i++)
193 if (i + nblines > lr.y)
194 memset16_far(GET_GLOBAL(vmode_g->sstart)
195 , address_far + (i * nbcols + ul.x) * 2
196 , (u16)attr * 0x100 + ' ', cols * 2);
198 memcpy16_far(GET_GLOBAL(vmode_g->sstart)
199 , address_far + (i * nbcols + ul.x) * 2
200 , GET_GLOBAL(vmode_g->sstart)
201 , (void*)(((i + nblines) * nbcols + ul.x) * 2)
206 for (i = lr.y; i >= ul.y; i--)
207 if (i < ul.y + nblines)
208 memset16_far(GET_GLOBAL(vmode_g->sstart)
209 , address_far + (i * nbcols + ul.x) * 2
210 , (u16)attr * 0x100 + ' ', cols * 2);
212 memcpy16_far(GET_GLOBAL(vmode_g->sstart)
213 , address_far + (i * nbcols + ul.x) * 2
214 , GET_GLOBAL(vmode_g->sstart)
215 , (void*)(((i - nblines) * nbcols + ul.x) * 2)
220 vgafb_scroll(int nblines, int attr, struct cursorpos ul, struct cursorpos lr)
223 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
227 // FIXME gfx mode not complete
228 switch (GET_GLOBAL(vmode_g->memmodel)) {
231 scroll_text(vmode_g, nblines, attr, ul, lr);
235 scroll_pl4(vmode_g, nblines, attr, ul, lr);
238 scroll_cga(vmode_g, nblines, attr, ul, lr);
241 dprintf(1, "Scroll in graphics mode\n");
246 clear_screen(struct vgamode_s *vmode_g)
248 switch (GET_GLOBAL(vmode_g->memmodel)) {
251 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0720, 32*1024);
254 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 32*1024);
257 outb(0x02, VGAREG_SEQU_ADDRESS);
258 u8 mmask = inb(VGAREG_SEQU_DATA);
259 outb(0x0f, VGAREG_SEQU_DATA); // all planes
260 memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 64*1024);
261 outb(mmask, VGAREG_SEQU_DATA);
267 /****************************************************************
268 * Read/write characters to screen
269 ****************************************************************/
272 write_gfx_char_pl4(struct vgamode_s *vmode_g
273 , struct cursorpos cp, struct carattr ca)
275 u16 nbcols = GET_BDA(video_cols);
279 struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
280 u8 cheight = GET_GLOBAL(vparam_g->cheight);
292 u16 addr = cp.x + cp.y * cheight * nbcols;
293 u16 src = ca.car * cheight;
294 outw(0x0f02, VGAREG_SEQU_ADDRESS);
295 outw(0x0205, VGAREG_GRDC_ADDRESS);
297 outw(0x1803, VGAREG_GRDC_ADDRESS);
299 outw(0x0003, VGAREG_GRDC_ADDRESS);
301 for (i = 0; i < cheight; i++) {
302 u8 *dest_far = (void*)(addr + i * nbcols);
304 for (j = 0; j < 8; j++) {
306 outw((mask << 8) | 0x08, VGAREG_GRDC_ADDRESS);
307 GET_FARVAR(SEG_GRAPH, *dest_far);
308 if (GET_GLOBAL(fdata_g[src + i]) & mask)
309 SET_FARVAR(SEG_GRAPH, *dest_far, ca.attr & 0x0f);
311 SET_FARVAR(SEG_GRAPH, *dest_far, 0x00);
314 outw(0xff08, VGAREG_GRDC_ADDRESS);
315 outw(0x0005, VGAREG_GRDC_ADDRESS);
316 outw(0x0003, VGAREG_GRDC_ADDRESS);
320 write_gfx_char_cga(struct vgamode_s *vmode_g
321 , struct cursorpos cp, struct carattr ca)
323 u16 nbcols = GET_BDA(video_cols);
327 u8 *fdata_g = vgafont8;
328 u8 bpp = GET_GLOBAL(vmode_g->pixbits);
329 u16 addr = (cp.x * bpp) + cp.y * 320;
330 u16 src = ca.car * 8;
332 for (i = 0; i < 8; i++) {
333 u8 *dest_far = (void*)(addr + (i >> 1) * 80);
340 data = GET_FARVAR(SEG_CTEXT, *dest_far);
342 for (j = 0; j < 8; j++) {
343 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
345 data ^= (ca.attr & 0x01) << (7 - j);
347 data |= (ca.attr & 0x01) << (7 - j);
351 SET_FARVAR(SEG_CTEXT, *dest_far, data);
356 data = GET_FARVAR(SEG_CTEXT, *dest_far);
358 for (j = 0; j < 4; j++) {
359 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
361 data ^= (ca.attr & 0x03) << ((3 - j) * 2);
363 data |= (ca.attr & 0x03) << ((3 - j) * 2);
367 SET_FARVAR(SEG_CTEXT, *dest_far, data);
375 write_gfx_char_lin(struct vgamode_s *vmode_g
376 , struct cursorpos cp, struct carattr ca)
378 // Get the dimensions
379 u16 nbcols = GET_BDA(video_cols);
383 u8 *fdata_g = vgafont8;
384 u16 addr = cp.x * 8 + cp.y * nbcols * 64;
385 u16 src = ca.car * 8;
387 for (i = 0; i < 8; i++) {
388 u8 *dest_far = (void*)(addr + i * nbcols * 8);
391 for (j = 0; j < 8; j++) {
393 if (GET_GLOBAL(fdata_g[src + i]) & mask)
395 SET_FARVAR(SEG_GRAPH, dest_far[j], data);
402 write_text_char(struct vgamode_s *vmode_g
403 , struct cursorpos cp, struct carattr ca)
405 // Get the dimensions
406 u16 nbrows = GET_BDA(video_rows) + 1;
407 u16 nbcols = GET_BDA(video_cols);
409 // Compute the address
410 void *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, cp.page)
411 + (cp.x + cp.y * nbcols) * 2);
414 u16 dummy = (ca.attr << 8) | ca.car;
415 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u16*)address_far, dummy);
417 SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)address_far, ca.car);
422 vgafb_write_char(struct cursorpos cp, struct carattr ca)
425 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
429 // FIXME gfx mode not complete
430 switch (GET_GLOBAL(vmode_g->memmodel)) {
433 write_text_char(vmode_g, cp, ca);
437 write_gfx_char_pl4(vmode_g, cp, ca);
440 write_gfx_char_cga(vmode_g, cp, ca);
443 write_gfx_char_lin(vmode_g, cp, ca);
449 vgafb_read_char(struct cursorpos cp)
452 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
456 if (!(GET_GLOBAL(vmode_g->memmodel) & TEXT)) {
458 dprintf(1, "Read char in graphics mode\n");
462 // Get the dimensions
463 u16 nbrows = GET_BDA(video_rows) + 1;
464 u16 nbcols = GET_BDA(video_cols);
466 // Compute the address
467 u16 *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, cp.page)
468 + (cp.x + cp.y * nbcols) * 2);
469 u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *address_far);
470 struct carattr ca = {v, v>>8, 0};
474 struct carattr ca2 = {0, 0, 0};
479 /****************************************************************
481 ****************************************************************/
484 biosfn_write_pixel(u8 BH, u8 AL, u16 CX, u16 DX)
487 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
490 if (GET_GLOBAL(vmode_g->memmodel) & TEXT)
493 u8 *addr_far, mask, attr, data;
494 switch (GET_GLOBAL(vmode_g->memmodel)) {
497 addr_far = (void*)(CX / 8 + DX * GET_BDA(video_cols));
498 mask = 0x80 >> (CX & 0x07);
499 outw((mask << 8) | 0x08, VGAREG_GRDC_ADDRESS);
500 outw(0x0205, VGAREG_GRDC_ADDRESS);
501 data = GET_FARVAR(SEG_GRAPH, *addr_far);
503 outw(0x1803, VGAREG_GRDC_ADDRESS);
504 SET_FARVAR(SEG_GRAPH, *addr_far, AL);
505 outw(0xff08, VGAREG_GRDC_ADDRESS);
506 outw(0x0005, VGAREG_GRDC_ADDRESS);
507 outw(0x0003, VGAREG_GRDC_ADDRESS);
510 if (GET_GLOBAL(vmode_g->pixbits) == 2)
511 addr_far = (void*)((CX >> 2) + (DX >> 1) * 80);
513 addr_far = (void*)((CX >> 3) + (DX >> 1) * 80);
516 data = GET_FARVAR(SEG_CTEXT, *addr_far);
517 if (GET_GLOBAL(vmode_g->pixbits) == 2) {
518 attr = (AL & 0x03) << ((3 - (CX & 0x03)) * 2);
519 mask = 0x03 << ((3 - (CX & 0x03)) * 2);
521 attr = (AL & 0x01) << (7 - (CX & 0x07));
522 mask = 0x01 << (7 - (CX & 0x07));
530 SET_FARVAR(SEG_CTEXT, *addr_far, data);
533 addr_far = (void*)(CX + DX * (GET_BDA(video_cols) * 8));
534 SET_FARVAR(SEG_GRAPH, *addr_far, AL);
540 biosfn_read_pixel(u8 BH, u16 CX, u16 DX, u16 *AX)
543 struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
546 if (GET_GLOBAL(vmode_g->memmodel) & TEXT)
549 u8 *addr_far, mask, attr=0, data, i;
550 switch (GET_GLOBAL(vmode_g->memmodel)) {
553 addr_far = (void*)(CX / 8 + DX * GET_BDA(video_cols));
554 mask = 0x80 >> (CX & 0x07);
556 for (i = 0; i < 4; i++) {
557 outw((i << 8) | 0x04, VGAREG_GRDC_ADDRESS);
558 data = GET_FARVAR(SEG_GRAPH, *addr_far) & mask;
564 addr_far = (void*)((CX >> 2) + (DX >> 1) * 80);
567 data = GET_FARVAR(SEG_CTEXT, *addr_far);
568 if (GET_GLOBAL(vmode_g->pixbits) == 2)
569 attr = (data >> ((3 - (CX & 0x03)) * 2)) & 0x03;
571 attr = (data >> (7 - (CX & 0x07))) & 0x01;
574 addr_far = (void*)(CX + DX * (GET_BDA(video_cols) * 8));
575 attr = GET_FARVAR(SEG_GRAPH, *addr_far);
578 *AX = (*AX & 0xff00) | attr;
582 /****************************************************************
584 ****************************************************************/
587 vgafb_load_font(u16 seg, void *src_far, u16 count
588 , u16 start, u8 destflags, u8 fontsize)
591 u16 blockaddr = ((destflags & 0x03) << 14) + ((destflags & 0x04) << 11);
592 void *dest_far = (void*)(blockaddr + start*32);
594 for (i = 0; i < count; i++)
595 memcpy_far(SEG_GRAPH, dest_far + i*32
596 , seg, src_far + i*fontsize, fontsize);
597 release_font_access();