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