vgabios: Extract out current mode finding into new function.
[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 "stdvga.h" // stdvga_planar4_plane
11
12
13 /****************************************************************
14  * Screen scrolling
15  ****************************************************************/
16
17 static inline void *
18 memcpy_stride(u16 seg, void *dst, void *src, int copylen, int stride, int lines)
19 {
20     for (; lines; lines--, dst+=stride, src+=stride)
21         memcpy_far(seg, dst, seg, src, copylen);
22     return dst;
23 }
24
25 static inline void
26 memset_stride(u16 seg, void *dst, u8 val, int setlen, int stride, int lines)
27 {
28     for (; lines; lines--, dst+=stride)
29         memset_far(seg, dst, val, setlen);
30 }
31
32 static inline void
33 memset16_stride(u16 seg, void *dst, u16 val, int setlen, int stride, int lines)
34 {
35     for (; lines; lines--, dst+=stride)
36         memset16_far(seg, dst, val, setlen);
37 }
38
39 static void
40 scroll_pl4(struct vgamode_s *vmode_g, int nblines, int attr
41            , struct cursorpos ul, struct cursorpos lr)
42 {
43     int cheight = GET_GLOBAL(vmode_g->cheight);
44     int cwidth = 1;
45     int stride = GET_BDA(video_cols) * cwidth;
46     void *src_far, *dest_far;
47     if (nblines >= 0) {
48         dest_far = (void*)(ul.y * cheight * stride + ul.x * cwidth);
49         src_far = dest_far + nblines * cheight * stride;
50     } else {
51         // Scroll down
52         nblines = -nblines;
53         dest_far = (void*)(lr.y * cheight * stride + ul.x * cwidth);
54         src_far = dest_far - nblines * cheight * stride;
55         stride = -stride;
56     }
57     if (attr < 0)
58         attr = 0;
59     int cols = lr.x - ul.x + 1;
60     int rows = lr.y - ul.y + 1;
61     int i;
62     for (i=0; i<4; i++) {
63         stdvga_planar4_plane(i);
64         void *dest = dest_far;
65         if (nblines < rows)
66             dest = memcpy_stride(SEG_GRAPH, dest, src_far, cols * cwidth
67                                  , stride, (rows - nblines) * cheight);
68         u8 pixels = (attr & (1<<i)) ? 0xff : 0x00;
69         memset_stride(SEG_GRAPH, dest, pixels, cols * cwidth
70                       , stride, nblines * cheight);
71     }
72     stdvga_planar4_plane(-1);
73 }
74
75 static void
76 scroll_cga(struct vgamode_s *vmode_g, int nblines, int attr
77             , struct cursorpos ul, struct cursorpos lr)
78 {
79     int cheight = GET_GLOBAL(vmode_g->cheight) / 2;
80     int cwidth = GET_GLOBAL(vmode_g->depth);
81     int stride = GET_BDA(video_cols) * cwidth;
82     void *src_far, *dest_far;
83     if (nblines >= 0) {
84         dest_far = (void*)(ul.y * cheight * stride + ul.x * cwidth);
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 * cwidth);
90         src_far = dest_far - nblines * cheight * stride;
91         stride = -stride;
92     }
93     if (attr < 0)
94         attr = 0;
95     int cols = lr.x - ul.x + 1;
96     int rows = lr.y - ul.y + 1;
97     if (nblines < rows) {
98         memcpy_stride(SEG_CTEXT, dest_far+0x2000, src_far+0x2000, cols * cwidth
99                       , stride, (rows - nblines) * cheight);
100         dest_far = memcpy_stride(SEG_CTEXT, dest_far, src_far, cols * cwidth
101                                  , stride, (rows - nblines) * cheight);
102     }
103     memset_stride(SEG_CTEXT, dest_far + 0x2000, attr, cols * cwidth
104                   , stride, nblines * cheight);
105     memset_stride(SEG_CTEXT, dest_far, attr, cols * cwidth
106                   , stride, nblines * cheight);
107 }
108
109 static void
110 scroll_lin(struct vgamode_s *vmode_g, int nblines, int attr
111            , struct cursorpos ul, struct cursorpos lr)
112 {
113     int cheight = 8;
114     int cwidth = 8;
115     int stride = GET_BDA(video_cols) * cwidth;
116     void *src_far, *dest_far;
117     if (nblines >= 0) {
118         dest_far = (void*)(ul.y * cheight * stride + ul.x * cwidth);
119         src_far = dest_far + nblines * cheight * stride;
120     } else {
121         // Scroll down
122         nblines = -nblines;
123         dest_far = (void*)(lr.y * cheight * stride + ul.x * cwidth);
124         src_far = dest_far - nblines * cheight * stride;
125         stride = -stride;
126     }
127     if (attr < 0)
128         attr = 0;
129     int cols = lr.x - ul.x + 1;
130     int rows = lr.y - ul.y + 1;
131     if (nblines < rows)
132         dest_far = memcpy_stride(SEG_GRAPH, dest_far, src_far, cols * cwidth
133                                  , stride, (rows - nblines) * cheight);
134     memset_stride(SEG_GRAPH, dest_far, attr, cols * cwidth
135                   , stride, nblines * cheight);
136 }
137
138 static void
139 scroll_text(struct vgamode_s *vmode_g, int nblines, int attr
140             , struct cursorpos ul, struct cursorpos lr)
141 {
142     int cheight = 1;
143     int cwidth = 2;
144     int stride = GET_BDA(video_cols) * cwidth;
145     void *src_far, *dest_far = (void*)(GET_BDA(video_pagesize) * ul.page);
146     if (nblines >= 0) {
147         dest_far += ul.y * cheight * stride + ul.x * cwidth;
148         src_far = dest_far + nblines * cheight * stride;
149     } else {
150         // Scroll down
151         nblines = -nblines;
152         dest_far += lr.y * cheight * stride + ul.x * cwidth;
153         src_far = dest_far - nblines * cheight * stride;
154         stride = -stride;
155     }
156     if (attr < 0)
157         attr = 0x07;
158     attr = (attr << 8) | ' ';
159     int cols = lr.x - ul.x + 1;
160     int rows = lr.y - ul.y + 1;
161     u16 seg = GET_GLOBAL(vmode_g->sstart);
162     if (nblines < rows)
163         dest_far = memcpy_stride(seg, dest_far, src_far, cols * cwidth
164                                  , stride, (rows - nblines) * cheight);
165     memset16_stride(seg, dest_far, attr, cols * cwidth
166                     , stride, nblines * cheight);
167 }
168
169 void
170 vgafb_scroll(int nblines, int attr, struct cursorpos ul, struct cursorpos lr)
171 {
172     // Get the mode
173     struct vgamode_s *vmode_g = get_current_mode();
174     if (!vmode_g)
175         return;
176
177     // FIXME gfx mode not complete
178     switch (GET_GLOBAL(vmode_g->memmodel)) {
179     case MM_TEXT:
180         scroll_text(vmode_g, nblines, attr, ul, lr);
181         break;
182     case MM_PLANAR:
183         scroll_pl4(vmode_g, nblines, attr, ul, lr);
184         break;
185     case MM_CGA:
186         scroll_cga(vmode_g, nblines, attr, ul, lr);
187         break;
188     case MM_DIRECT:
189     case MM_PACKED:
190         scroll_lin(vmode_g, nblines, attr, ul, lr);
191         break;
192     }
193 }
194
195
196 /****************************************************************
197  * Read/write characters to screen
198  ****************************************************************/
199
200 static void
201 write_gfx_char_pl4(struct vgamode_s *vmode_g
202                    , struct cursorpos cp, struct carattr ca)
203 {
204     u16 nbcols = GET_BDA(video_cols);
205     if (cp.x >= nbcols)
206         return;
207
208     u8 cheight = GET_GLOBAL(vmode_g->cheight);
209     u8 *fdata_g;
210     switch (cheight) {
211     case 14:
212         fdata_g = vgafont14;
213         break;
214     case 16:
215         fdata_g = vgafont16;
216         break;
217     default:
218         fdata_g = vgafont8;
219     }
220     u16 addr = cp.x + cp.y * cheight * nbcols;
221     u16 src = ca.car * cheight;
222     int i;
223     for (i=0; i<4; i++) {
224         stdvga_planar4_plane(i);
225         u8 colors = ((ca.attr & (1<<i)) ? 0xff : 0x00);
226         int j;
227         for (j = 0; j < cheight; j++) {
228             u8 *dest_far = (void*)(addr + j * nbcols);
229             u8 pixels = colors & GET_GLOBAL(fdata_g[src + j]);
230             if (ca.attr & 0x80)
231                 pixels ^= GET_FARVAR(SEG_GRAPH, *dest_far);
232             SET_FARVAR(SEG_GRAPH, *dest_far, pixels);
233         }
234     }
235     stdvga_planar4_plane(-1);
236 }
237
238 static void
239 write_gfx_char_cga(struct vgamode_s *vmode_g
240                    , struct cursorpos cp, struct carattr ca)
241 {
242     u16 nbcols = GET_BDA(video_cols);
243     if (cp.x >= nbcols)
244         return;
245
246     u8 *fdata_g = vgafont8;
247     u8 bpp = GET_GLOBAL(vmode_g->depth);
248     u16 addr = (cp.x * bpp) + cp.y * 320;
249     u16 src = ca.car * 8;
250     u8 i;
251     for (i = 0; i < 8; i++) {
252         u8 *dest_far = (void*)(addr + (i >> 1) * 80);
253         if (i & 1)
254             dest_far += 0x2000;
255         u8 mask = 0x80;
256         if (bpp == 1) {
257             u8 data = 0;
258             if (ca.attr & 0x80)
259                 data = GET_FARVAR(SEG_CTEXT, *dest_far);
260             u8 j;
261             for (j = 0; j < 8; j++) {
262                 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
263                     if (ca.attr & 0x80)
264                         data ^= (ca.attr & 0x01) << (7 - j);
265                     else
266                         data |= (ca.attr & 0x01) << (7 - j);
267                 }
268                 mask >>= 1;
269             }
270             SET_FARVAR(SEG_CTEXT, *dest_far, data);
271         } else {
272             while (mask > 0) {
273                 u8 data = 0;
274                 if (ca.attr & 0x80)
275                     data = GET_FARVAR(SEG_CTEXT, *dest_far);
276                 u8 j;
277                 for (j = 0; j < 4; j++) {
278                     if (GET_GLOBAL(fdata_g[src + i]) & mask) {
279                         if (ca.attr & 0x80)
280                             data ^= (ca.attr & 0x03) << ((3 - j) * 2);
281                         else
282                             data |= (ca.attr & 0x03) << ((3 - j) * 2);
283                     }
284                     mask >>= 1;
285                 }
286                 SET_FARVAR(SEG_CTEXT, *dest_far, data);
287                 dest_far += 1;
288             }
289         }
290     }
291 }
292
293 static void
294 write_gfx_char_lin(struct vgamode_s *vmode_g
295                    , struct cursorpos cp, struct carattr ca)
296 {
297     // Get the dimensions
298     u16 nbcols = GET_BDA(video_cols);
299     if (cp.x >= nbcols)
300         return;
301
302     u8 *fdata_g = vgafont8;
303     u16 addr = cp.x * 8 + cp.y * nbcols * 64;
304     u16 src = ca.car * 8;
305     u8 i;
306     for (i = 0; i < 8; i++) {
307         u8 *dest_far = (void*)(addr + i * nbcols * 8);
308         u8 mask = 0x80;
309         u8 j;
310         for (j = 0; j < 8; j++) {
311             u8 data = 0x00;
312             if (GET_GLOBAL(fdata_g[src + i]) & mask)
313                 data = ca.attr;
314             SET_FARVAR(SEG_GRAPH, dest_far[j], data);
315             mask >>= 1;
316         }
317     }
318 }
319
320 static void
321 write_text_char(struct vgamode_s *vmode_g
322                 , struct cursorpos cp, struct carattr ca)
323 {
324     // Compute the address
325     u16 nbcols = GET_BDA(video_cols);
326     void *address_far = (void*)(GET_BDA(video_pagesize) * cp.page
327                                 + (cp.x + cp.y * nbcols) * 2);
328
329     if (ca.use_attr) {
330         u16 dummy = (ca.attr << 8) | ca.car;
331         SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u16*)address_far, dummy);
332     } else {
333         SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)address_far, ca.car);
334     }
335 }
336
337 void
338 vgafb_write_char(struct cursorpos cp, struct carattr ca)
339 {
340     // Get the mode
341     struct vgamode_s *vmode_g = get_current_mode();
342     if (!vmode_g)
343         return;
344
345     // FIXME gfx mode not complete
346     switch (GET_GLOBAL(vmode_g->memmodel)) {
347     case MM_TEXT:
348         write_text_char(vmode_g, cp, ca);
349         break;
350     case MM_PLANAR:
351         write_gfx_char_pl4(vmode_g, cp, ca);
352         break;
353     case MM_CGA:
354         write_gfx_char_cga(vmode_g, cp, ca);
355         break;
356     case MM_DIRECT:
357     case MM_PACKED:
358         write_gfx_char_lin(vmode_g, cp, ca);
359         break;
360     }
361 }
362
363 struct carattr
364 vgafb_read_char(struct cursorpos cp)
365 {
366     // Get the mode
367     struct vgamode_s *vmode_g = get_current_mode();
368     if (!vmode_g)
369         goto fail;
370
371     if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
372         // FIXME gfx mode
373         dprintf(1, "Read char in graphics mode\n");
374         goto fail;
375     }
376
377     // Compute the address
378     u16 nbcols = GET_BDA(video_cols);
379     u16 *address_far = (void*)(GET_BDA(video_pagesize) * cp.page
380                                + (cp.x + cp.y * nbcols) * 2);
381     u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *address_far);
382     struct carattr ca = {v, v>>8, 0};
383     return ca;
384
385 fail: ;
386     struct carattr ca2 = {0, 0, 0};
387     return ca2;
388 }
389
390
391 /****************************************************************
392  * Read/write pixels
393  ****************************************************************/
394
395 void
396 vgafb_write_pixel(u8 color, u16 x, u16 y)
397 {
398     // Get the mode
399     struct vgamode_s *vmode_g = get_current_mode();
400     if (!vmode_g)
401         return;
402
403     u8 *addr_far, mask, attr, data, i;
404     switch (GET_GLOBAL(vmode_g->memmodel)) {
405     case MM_PLANAR:
406         addr_far = (void*)(x / 8 + y * GET_BDA(video_cols));
407         mask = 0x80 >> (x & 0x07);
408         for (i=0; i<4; i++) {
409             stdvga_planar4_plane(i);
410             u8 colors = (color & (1<<i)) ? 0xff : 0x00;
411             u8 orig = GET_FARVAR(SEG_GRAPH, *addr_far);
412             if (color & 0x80)
413                 colors ^= orig;
414             SET_FARVAR(SEG_GRAPH, *addr_far, (colors & mask) | (orig & ~mask));
415         }
416         stdvga_planar4_plane(-1);
417         break;
418     case MM_CGA:
419         if (GET_GLOBAL(vmode_g->depth) == 2)
420             addr_far = (void*)((x >> 2) + (y >> 1) * 80);
421         else
422             addr_far = (void*)((x >> 3) + (y >> 1) * 80);
423         if (y & 1)
424             addr_far += 0x2000;
425         data = GET_FARVAR(SEG_CTEXT, *addr_far);
426         if (GET_GLOBAL(vmode_g->depth) == 2) {
427             attr = (color & 0x03) << ((3 - (x & 0x03)) * 2);
428             mask = 0x03 << ((3 - (x & 0x03)) * 2);
429         } else {
430             attr = (color & 0x01) << (7 - (x & 0x07));
431             mask = 0x01 << (7 - (x & 0x07));
432         }
433         if (color & 0x80) {
434             data ^= attr;
435         } else {
436             data &= ~mask;
437             data |= attr;
438         }
439         SET_FARVAR(SEG_CTEXT, *addr_far, data);
440         break;
441     case MM_DIRECT:
442     case MM_PACKED:
443         addr_far = (void*)(x + y * (GET_BDA(video_cols) * 8));
444         SET_FARVAR(SEG_GRAPH, *addr_far, color);
445         break;
446     case MM_TEXT:
447         return;
448     }
449 }
450
451 u8
452 vgafb_read_pixel(u16 x, u16 y)
453 {
454     // Get the mode
455     struct vgamode_s *vmode_g = get_current_mode();
456     if (!vmode_g)
457         return 0;
458
459     u8 *addr_far, mask, attr=0, data, i;
460     switch (GET_GLOBAL(vmode_g->memmodel)) {
461     case MM_PLANAR:
462         addr_far = (void*)(x / 8 + y * GET_BDA(video_cols));
463         mask = 0x80 >> (x & 0x07);
464         attr = 0x00;
465         for (i = 0; i < 4; i++) {
466             stdvga_planar4_plane(i);
467             data = GET_FARVAR(SEG_GRAPH, *addr_far) & mask;
468             if (data > 0)
469                 attr |= (0x01 << i);
470         }
471         stdvga_planar4_plane(-1);
472         break;
473     case MM_CGA:
474         addr_far = (void*)((x >> 2) + (y >> 1) * 80);
475         if (y & 1)
476             addr_far += 0x2000;
477         data = GET_FARVAR(SEG_CTEXT, *addr_far);
478         if (GET_GLOBAL(vmode_g->depth) == 2)
479             attr = (data >> ((3 - (x & 0x03)) * 2)) & 0x03;
480         else
481             attr = (data >> (7 - (x & 0x07))) & 0x01;
482         break;
483     case MM_DIRECT:
484     case MM_PACKED:
485         addr_far = (void*)(x + y * (GET_BDA(video_cols) * 8));
486         attr = GET_FARVAR(SEG_GRAPH, *addr_far);
487         break;
488     case MM_TEXT:
489         return 0;
490     }
491     return attr;
492 }