VGA: Comment and name update.
[seabios.git] / vgasrc / vgafb.c
1 // Code for manipulating VGA framebuffers.
2 //
3 // Copyright (C) 2009  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2001-2008 the LGPL VGABios developers Team
5 //
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
7
8 #include "biosvar.h" // GET_BDA
9 #include "util.h" // memset_far
10 #include "vgatables.h" // find_vga_entry
11
12 // TODO
13 //  * extract hw code from framebuffer code
14
15
16 /****************************************************************
17  * Screen scrolling
18  ****************************************************************/
19
20 static inline void *
21 memcpy_stride(u16 seg, void *dst, void *src, int copylen, int stride, int lines)
22 {
23     for (; lines; lines--, dst+=stride, src+=stride)
24         memcpy_far(seg, dst, seg, src, copylen);
25     return dst;
26 }
27
28 static inline void
29 memset_stride(u16 seg, void *dst, u8 val, int setlen, int stride, int lines)
30 {
31     for (; lines; lines--, dst+=stride)
32         memset_far(seg, dst, val, setlen);
33 }
34
35 static inline void
36 memset16_stride(u16 seg, void *dst, u16 val, int setlen, int stride, int lines)
37 {
38     for (; lines; lines--, dst+=stride)
39         memset16_far(seg, dst, val, setlen);
40 }
41
42 static void
43 scroll_pl4(struct vgamode_s *vmode_g, int nblines, int attr
44            , struct cursorpos ul, struct cursorpos lr)
45 {
46     struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
47     u8 cheight = GET_GLOBAL(vparam_g->cheight);
48     int stride = GET_BDA(video_cols);
49     void *src_far, *dest_far;
50     if (nblines >= 0) {
51         dest_far = (void*)(ul.y * cheight * stride + ul.x);
52         src_far = dest_far + nblines * cheight * stride;
53     } else {
54         // Scroll down
55         nblines = -nblines;
56         dest_far = (void*)(lr.y * cheight * stride + ul.x);
57         src_far = dest_far - nblines * cheight * stride;
58         stride = -stride;
59     }
60     int cols = lr.x - ul.x + 1;
61     int rows = lr.y - ul.y + 1;
62     if (nblines < rows) {
63         outw(0x0105, VGAREG_GRDC_ADDRESS);
64         dest_far = memcpy_stride(SEG_GRAPH, dest_far, src_far, cols, stride
65                                  , (rows - nblines) * cheight);
66     }
67     if (attr < 0)
68         attr = 0;
69     outw(0x0205, VGAREG_GRDC_ADDRESS);
70     memset_stride(SEG_GRAPH, dest_far, attr, cols, stride, nblines * cheight);
71     outw(0x0005, VGAREG_GRDC_ADDRESS);
72 }
73
74 static void
75 scroll_cga(struct vgamode_s *vmode_g, int nblines, int attr
76             , struct cursorpos ul, struct cursorpos lr)
77 {
78     struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
79     u8 cheight = GET_GLOBAL(vparam_g->cheight);
80     u8 bpp = GET_GLOBAL(vmode_g->pixbits);
81     int stride = GET_BDA(video_cols) * bpp;
82     void *src_far, *dest_far;
83     if (nblines >= 0) {
84         dest_far = (void*)(ul.y * cheight * stride + ul.x * bpp);
85         src_far = dest_far + nblines * cheight * stride;
86     } else {
87         // Scroll down
88         nblines = -nblines;
89         dest_far = (void*)(lr.y * cheight * stride + ul.x * bpp);
90         src_far = dest_far - nblines * cheight * stride;
91         stride = -stride;
92     }
93     int cols = (lr.x - ul.x + 1) * bpp;
94     int rows = lr.y - ul.y + 1;
95     if (nblines < rows) {
96         memcpy_stride(SEG_CTEXT, dest_far + 0x2000, src_far + 0x2000, cols
97                       , stride, (rows - nblines) * cheight / 2);
98         dest_far = memcpy_stride(SEG_CTEXT, dest_far, src_far, cols
99                                  , stride, (rows - nblines) * cheight / 2);
100     }
101     if (attr < 0)
102         attr = 0;
103     memset_stride(SEG_CTEXT, dest_far + 0x2000, attr, cols
104                   , stride, nblines * cheight / 2);
105     memset_stride(SEG_CTEXT, dest_far, attr, cols
106                   , stride, nblines * cheight / 2);
107 }
108
109 static void
110 scroll_text(struct vgamode_s *vmode_g, int nblines, int attr
111             , struct cursorpos ul, struct cursorpos lr)
112 {
113     u16 nbrows = GET_BDA(video_rows) + 1;
114     u16 nbcols = GET_BDA(video_cols);
115     void *src_far, *dest_far = (void*)SCREEN_MEM_START(nbcols, nbrows, ul.page);
116     int stride = nbcols * 2;
117     if (nblines >= 0) {
118         dest_far += ul.y * stride + ul.x * 2;
119         src_far = dest_far + nblines * stride;
120     } else {
121         // Scroll down
122         nblines = -nblines;
123         dest_far += lr.y * stride + ul.x * 2;
124         src_far = dest_far - nblines * stride;
125         stride = -stride;
126     }
127     int cols = (lr.x - ul.x + 1) * 2;
128     int rows = lr.y - ul.y + 1;
129     u16 seg = GET_GLOBAL(vmode_g->sstart);
130     if (nblines < rows)
131         dest_far = memcpy_stride(seg, dest_far, src_far, cols, stride
132                                  , (rows - nblines));
133     if (attr < 0)
134         attr = 0x07;
135     attr = (attr << 8) | ' ';
136     memset16_stride(seg, dest_far, attr, cols, stride, nblines);
137 }
138
139 void
140 vgafb_scroll(int nblines, int attr, struct cursorpos ul, struct cursorpos lr)
141 {
142     // Get the mode
143     struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
144     if (!vmode_g)
145         return;
146
147     // FIXME gfx mode not complete
148     switch (GET_GLOBAL(vmode_g->memmodel)) {
149     case CTEXT:
150     case MTEXT:
151         scroll_text(vmode_g, nblines, attr, ul, lr);
152         break;
153     case PLANAR4:
154     case PLANAR1:
155         scroll_pl4(vmode_g, nblines, attr, ul, lr);
156         break;
157     case CGA:
158         scroll_cga(vmode_g, nblines, attr, ul, lr);
159         break;
160     default:
161         dprintf(1, "Scroll in graphics mode\n");
162     }
163 }
164
165 void
166 clear_screen(struct vgamode_s *vmode_g)
167 {
168     switch (GET_GLOBAL(vmode_g->memmodel)) {
169     case CTEXT:
170     case MTEXT:
171         memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0720, 32*1024);
172         break;
173     case CGA:
174         memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 32*1024);
175         break;
176     default: {
177         outb(0x02, VGAREG_SEQU_ADDRESS);
178         u8 mmask = inb(VGAREG_SEQU_DATA);
179         outb(0x0f, VGAREG_SEQU_DATA);   // all planes
180         memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 64*1024);
181         outb(mmask, VGAREG_SEQU_DATA);
182     }
183     }
184 }
185
186
187 /****************************************************************
188  * Read/write characters to screen
189  ****************************************************************/
190
191 static void
192 write_gfx_char_pl4(struct vgamode_s *vmode_g
193                    , struct cursorpos cp, struct carattr ca)
194 {
195     u16 nbcols = GET_BDA(video_cols);
196     if (cp.x >= nbcols)
197         return;
198
199     struct VideoParam_s *vparam_g = GET_GLOBAL(vmode_g->vparam);
200     u8 cheight = GET_GLOBAL(vparam_g->cheight);
201     u8 *fdata_g;
202     switch (cheight) {
203     case 14:
204         fdata_g = vgafont14;
205         break;
206     case 16:
207         fdata_g = vgafont16;
208         break;
209     default:
210         fdata_g = vgafont8;
211     }
212     u16 addr = cp.x + cp.y * cheight * nbcols;
213     u16 src = ca.car * cheight;
214     outw(0x0f02, VGAREG_SEQU_ADDRESS);
215     outw(0x0205, VGAREG_GRDC_ADDRESS);
216     if (ca.attr & 0x80)
217         outw(0x1803, VGAREG_GRDC_ADDRESS);
218     else
219         outw(0x0003, VGAREG_GRDC_ADDRESS);
220     u8 i;
221     for (i = 0; i < cheight; i++) {
222         u8 *dest_far = (void*)(addr + i * nbcols);
223         u8 j;
224         for (j = 0; j < 8; j++) {
225             u8 mask = 0x80 >> j;
226             outw((mask << 8) | 0x08, VGAREG_GRDC_ADDRESS);
227             GET_FARVAR(SEG_GRAPH, *dest_far);
228             if (GET_GLOBAL(fdata_g[src + i]) & mask)
229                 SET_FARVAR(SEG_GRAPH, *dest_far, ca.attr & 0x0f);
230             else
231                 SET_FARVAR(SEG_GRAPH, *dest_far, 0x00);
232         }
233     }
234     outw(0xff08, VGAREG_GRDC_ADDRESS);
235     outw(0x0005, VGAREG_GRDC_ADDRESS);
236     outw(0x0003, VGAREG_GRDC_ADDRESS);
237 }
238
239 static void
240 write_gfx_char_cga(struct vgamode_s *vmode_g
241                    , struct cursorpos cp, struct carattr ca)
242 {
243     u16 nbcols = GET_BDA(video_cols);
244     if (cp.x >= nbcols)
245         return;
246
247     u8 *fdata_g = vgafont8;
248     u8 bpp = GET_GLOBAL(vmode_g->pixbits);
249     u16 addr = (cp.x * bpp) + cp.y * 320;
250     u16 src = ca.car * 8;
251     u8 i;
252     for (i = 0; i < 8; i++) {
253         u8 *dest_far = (void*)(addr + (i >> 1) * 80);
254         if (i & 1)
255             dest_far += 0x2000;
256         u8 mask = 0x80;
257         if (bpp == 1) {
258             u8 data = 0;
259             if (ca.attr & 0x80)
260                 data = GET_FARVAR(SEG_CTEXT, *dest_far);
261             u8 j;
262             for (j = 0; j < 8; j++) {
263                 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
264                     if (ca.attr & 0x80)
265                         data ^= (ca.attr & 0x01) << (7 - j);
266                     else
267                         data |= (ca.attr & 0x01) << (7 - j);
268                 }
269                 mask >>= 1;
270             }
271             SET_FARVAR(SEG_CTEXT, *dest_far, data);
272         } else {
273             while (mask > 0) {
274                 u8 data = 0;
275                 if (ca.attr & 0x80)
276                     data = GET_FARVAR(SEG_CTEXT, *dest_far);
277                 u8 j;
278                 for (j = 0; j < 4; j++) {
279                     if (GET_GLOBAL(fdata_g[src + i]) & mask) {
280                         if (ca.attr & 0x80)
281                             data ^= (ca.attr & 0x03) << ((3 - j) * 2);
282                         else
283                             data |= (ca.attr & 0x03) << ((3 - j) * 2);
284                     }
285                     mask >>= 1;
286                 }
287                 SET_FARVAR(SEG_CTEXT, *dest_far, data);
288                 dest_far += 1;
289             }
290         }
291     }
292 }
293
294 static void
295 write_gfx_char_lin(struct vgamode_s *vmode_g
296                    , struct cursorpos cp, struct carattr ca)
297 {
298     // Get the dimensions
299     u16 nbcols = GET_BDA(video_cols);
300     if (cp.x >= nbcols)
301         return;
302
303     u8 *fdata_g = vgafont8;
304     u16 addr = cp.x * 8 + cp.y * nbcols * 64;
305     u16 src = ca.car * 8;
306     u8 i;
307     for (i = 0; i < 8; i++) {
308         u8 *dest_far = (void*)(addr + i * nbcols * 8);
309         u8 mask = 0x80;
310         u8 j;
311         for (j = 0; j < 8; j++) {
312             u8 data = 0x00;
313             if (GET_GLOBAL(fdata_g[src + i]) & mask)
314                 data = ca.attr;
315             SET_FARVAR(SEG_GRAPH, dest_far[j], data);
316             mask >>= 1;
317         }
318     }
319 }
320
321 static void
322 write_text_char(struct vgamode_s *vmode_g
323                 , struct cursorpos cp, struct carattr ca)
324 {
325     // Get the dimensions
326     u16 nbrows = GET_BDA(video_rows) + 1;
327     u16 nbcols = GET_BDA(video_cols);
328
329     // Compute the address
330     void *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, cp.page)
331                                 + (cp.x + cp.y * nbcols) * 2);
332
333     if (ca.use_attr) {
334         u16 dummy = (ca.attr << 8) | ca.car;
335         SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u16*)address_far, dummy);
336     } else {
337         SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)address_far, ca.car);
338     }
339 }
340
341 void
342 vgafb_write_char(struct cursorpos cp, struct carattr ca)
343 {
344     // Get the mode
345     struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
346     if (!vmode_g)
347         return;
348
349     // FIXME gfx mode not complete
350     switch (GET_GLOBAL(vmode_g->memmodel)) {
351     case CTEXT:
352     case MTEXT:
353         write_text_char(vmode_g, cp, ca);
354         break;
355     case PLANAR4:
356     case PLANAR1:
357         write_gfx_char_pl4(vmode_g, cp, ca);
358         break;
359     case CGA:
360         write_gfx_char_cga(vmode_g, cp, ca);
361         break;
362     case LINEAR8:
363         write_gfx_char_lin(vmode_g, cp, ca);
364         break;
365     }
366 }
367
368 struct carattr
369 vgafb_read_char(struct cursorpos cp)
370 {
371     // Get the mode
372     struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
373     if (!vmode_g)
374         goto fail;
375
376     if (!(GET_GLOBAL(vmode_g->memmodel) & TEXT)) {
377         // FIXME gfx mode
378         dprintf(1, "Read char in graphics mode\n");
379         goto fail;
380     }
381
382     // Get the dimensions
383     u16 nbrows = GET_BDA(video_rows) + 1;
384     u16 nbcols = GET_BDA(video_cols);
385
386     // Compute the address
387     u16 *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, cp.page)
388                                + (cp.x + cp.y * nbcols) * 2);
389     u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *address_far);
390     struct carattr ca = {v, v>>8, 0};
391     return ca;
392
393 fail: ;
394     struct carattr ca2 = {0, 0, 0};
395     return ca2;
396 }
397
398
399 /****************************************************************
400  * Read/write pixels
401  ****************************************************************/
402
403 void
404 vgafb_write_pixel(u8 color, u16 x, u16 y)
405 {
406     // Get the mode
407     struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
408     if (!vmode_g)
409         return;
410     if (GET_GLOBAL(vmode_g->memmodel) & TEXT)
411         return;
412
413     u8 *addr_far, mask, attr, data;
414     switch (GET_GLOBAL(vmode_g->memmodel)) {
415     case PLANAR4:
416     case PLANAR1:
417         addr_far = (void*)(x / 8 + y * GET_BDA(video_cols));
418         mask = 0x80 >> (x & 0x07);
419         outw((mask << 8) | 0x08, VGAREG_GRDC_ADDRESS);
420         outw(0x0205, VGAREG_GRDC_ADDRESS);
421         data = GET_FARVAR(SEG_GRAPH, *addr_far);
422         if (color & 0x80)
423             outw(0x1803, VGAREG_GRDC_ADDRESS);
424         SET_FARVAR(SEG_GRAPH, *addr_far, color);
425         outw(0xff08, VGAREG_GRDC_ADDRESS);
426         outw(0x0005, VGAREG_GRDC_ADDRESS);
427         outw(0x0003, VGAREG_GRDC_ADDRESS);
428         break;
429     case CGA:
430         if (GET_GLOBAL(vmode_g->pixbits) == 2)
431             addr_far = (void*)((x >> 2) + (y >> 1) * 80);
432         else
433             addr_far = (void*)((x >> 3) + (y >> 1) * 80);
434         if (y & 1)
435             addr_far += 0x2000;
436         data = GET_FARVAR(SEG_CTEXT, *addr_far);
437         if (GET_GLOBAL(vmode_g->pixbits) == 2) {
438             attr = (color & 0x03) << ((3 - (x & 0x03)) * 2);
439             mask = 0x03 << ((3 - (x & 0x03)) * 2);
440         } else {
441             attr = (color & 0x01) << (7 - (x & 0x07));
442             mask = 0x01 << (7 - (x & 0x07));
443         }
444         if (color & 0x80) {
445             data ^= attr;
446         } else {
447             data &= ~mask;
448             data |= attr;
449         }
450         SET_FARVAR(SEG_CTEXT, *addr_far, data);
451         break;
452     case LINEAR8:
453         addr_far = (void*)(x + y * (GET_BDA(video_cols) * 8));
454         SET_FARVAR(SEG_GRAPH, *addr_far, color);
455         break;
456     }
457 }
458
459 u8
460 vgafb_read_pixel(u16 x, u16 y)
461 {
462     // Get the mode
463     struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
464     if (!vmode_g)
465         return 0;
466     if (GET_GLOBAL(vmode_g->memmodel) & TEXT)
467         return 0;
468
469     u8 *addr_far, mask, attr=0, data, i;
470     switch (GET_GLOBAL(vmode_g->memmodel)) {
471     case PLANAR4:
472     case PLANAR1:
473         addr_far = (void*)(x / 8 + y * GET_BDA(video_cols));
474         mask = 0x80 >> (x & 0x07);
475         attr = 0x00;
476         for (i = 0; i < 4; i++) {
477             outw((i << 8) | 0x04, VGAREG_GRDC_ADDRESS);
478             data = GET_FARVAR(SEG_GRAPH, *addr_far) & mask;
479             if (data > 0)
480                 attr |= (0x01 << i);
481         }
482         break;
483     case CGA:
484         addr_far = (void*)((x >> 2) + (y >> 1) * 80);
485         if (y & 1)
486             addr_far += 0x2000;
487         data = GET_FARVAR(SEG_CTEXT, *addr_far);
488         if (GET_GLOBAL(vmode_g->pixbits) == 2)
489             attr = (data >> ((3 - (x & 0x03)) * 2)) & 0x03;
490         else
491             attr = (data >> (7 - (x & 0x07))) & 0x01;
492         break;
493     case LINEAR8:
494         addr_far = (void*)(x + y * (GET_BDA(video_cols) * 8));
495         attr = GET_FARVAR(SEG_GRAPH, *addr_far);
496         break;
497     }
498     return attr;
499 }
500
501
502 /****************************************************************
503  * Font loading
504  ****************************************************************/
505
506 void
507 vgafb_load_font(u16 seg, void *src_far, u16 count
508                 , u16 start, u8 destflags, u8 fontsize)
509 {
510     get_font_access();
511     u16 blockaddr = ((destflags & 0x03) << 14) + ((destflags & 0x04) << 11);
512     void *dest_far = (void*)(blockaddr + start*32);
513     u16 i;
514     for (i = 0; i < count; i++)
515         memcpy_far(SEG_GRAPH, dest_far + i*32
516                    , seg, src_far + i*fontsize, fontsize);
517     release_font_access();
518 }