d988c883ca7ce14c25594194ce3344dfcfec9451
[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     int cols = lr.x - ul.x + 1;
97     int rows = lr.y - ul.y + 1;
98     if (nblines < rows) {
99         memcpy_stride(SEG_CTEXT, dest_far+0x2000, src_far+0x2000, cols * cwidth
100                       , stride, (rows - nblines) * cheight);
101         dest_far = memcpy_stride(SEG_CTEXT, dest_far, src_far, cols * cwidth
102                                  , stride, (rows - nblines) * cheight);
103     }
104     memset_stride(SEG_CTEXT, dest_far + 0x2000, attr, cols * cwidth
105                   , stride, nblines * cheight);
106     memset_stride(SEG_CTEXT, dest_far, attr, cols * cwidth
107                   , stride, nblines * cheight);
108 }
109
110 static void
111 scroll_lin(struct vgamode_s *vmode_g, int nblines, int attr
112            , struct cursorpos ul, struct cursorpos lr)
113 {
114     int cheight = 8;
115     int cwidth = 8;
116     int stride = GET_BDA(video_cols) * cwidth;
117     void *src_far, *dest_far;
118     if (nblines >= 0) {
119         dest_far = (void*)(ul.y * cheight * stride + ul.x * cwidth);
120         src_far = dest_far + nblines * cheight * stride;
121     } else {
122         // Scroll down
123         nblines = -nblines;
124         dest_far = (void*)(lr.y * cheight * stride + ul.x * cwidth);
125         src_far = dest_far - nblines * cheight * stride;
126         stride = -stride;
127     }
128     if (attr < 0)
129         attr = 0;
130     int cols = lr.x - ul.x + 1;
131     int rows = lr.y - ul.y + 1;
132     if (nblines < rows)
133         dest_far = memcpy_stride(SEG_GRAPH, dest_far, src_far, cols * cwidth
134                                  , stride, (rows - nblines) * cheight);
135     memset_stride(SEG_GRAPH, dest_far, attr, cols * cwidth
136                   , stride, nblines * cheight);
137 }
138
139 static void
140 scroll_text(struct vgamode_s *vmode_g, int nblines, int attr
141             , struct cursorpos ul, struct cursorpos lr)
142 {
143     int cheight = 1;
144     int cwidth = 2;
145     int stride = GET_BDA(video_cols) * cwidth;
146     void *src_far, *dest_far = (void*)(GET_BDA(video_pagesize) * ul.page);
147     if (nblines >= 0) {
148         dest_far += ul.y * cheight * stride + ul.x * cwidth;
149         src_far = dest_far + nblines * cheight * stride;
150     } else {
151         // Scroll down
152         nblines = -nblines;
153         dest_far += lr.y * cheight * stride + ul.x * cwidth;
154         src_far = dest_far - nblines * cheight * stride;
155         stride = -stride;
156     }
157     if (attr < 0)
158         attr = 0x07;
159     attr = (attr << 8) | ' ';
160     int cols = lr.x - ul.x + 1;
161     int rows = lr.y - ul.y + 1;
162     u16 seg = GET_GLOBAL(vmode_g->sstart);
163     if (nblines < rows)
164         dest_far = memcpy_stride(seg, dest_far, src_far, cols * cwidth
165                                  , stride, (rows - nblines) * cheight);
166     memset16_stride(seg, dest_far, attr, cols * cwidth
167                     , stride, nblines * cheight);
168 }
169
170 void
171 vgafb_scroll(int nblines, int attr, struct cursorpos ul, struct cursorpos lr)
172 {
173     // Get the mode
174     struct vgamode_s *vmode_g = get_current_mode();
175     if (!vmode_g)
176         return;
177
178     // FIXME gfx mode not complete
179     switch (GET_GLOBAL(vmode_g->memmodel)) {
180     case MM_TEXT:
181         scroll_text(vmode_g, nblines, attr, ul, lr);
182         break;
183     case MM_PLANAR:
184         scroll_pl4(vmode_g, nblines, attr, ul, lr);
185         break;
186     case MM_CGA:
187         scroll_cga(vmode_g, nblines, attr, ul, lr);
188         break;
189     case MM_DIRECT:
190     case MM_PACKED:
191         scroll_lin(vmode_g, nblines, attr, ul, lr);
192         break;
193     }
194 }
195
196
197 /****************************************************************
198  * Read/write characters to screen
199  ****************************************************************/
200
201 static void
202 write_gfx_char_pl4(struct vgamode_s *vmode_g
203                    , struct cursorpos cp, struct carattr ca)
204 {
205     u16 nbcols = GET_BDA(video_cols);
206     if (cp.x >= nbcols)
207         return;
208
209     u8 cheight = GET_GLOBAL(vmode_g->cheight);
210     u8 *fdata_g;
211     switch (cheight) {
212     case 14:
213         fdata_g = vgafont14;
214         break;
215     case 16:
216         fdata_g = vgafont16;
217         break;
218     default:
219         fdata_g = vgafont8;
220     }
221     u16 addr = cp.x + cp.y * cheight * nbcols;
222     u16 src = ca.car * cheight;
223     int i;
224     for (i=0; i<4; i++) {
225         stdvga_planar4_plane(i);
226         u8 colors = ((ca.attr & (1<<i)) ? 0xff : 0x00);
227         int j;
228         for (j = 0; j < cheight; j++) {
229             u8 *dest_far = (void*)(addr + j * nbcols);
230             u8 pixels = colors & GET_GLOBAL(fdata_g[src + j]);
231             if (ca.attr & 0x80)
232                 pixels ^= GET_FARVAR(SEG_GRAPH, *dest_far);
233             SET_FARVAR(SEG_GRAPH, *dest_far, pixels);
234         }
235     }
236     stdvga_planar4_plane(-1);
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->depth);
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         if (bpp == 1) {
257             u8 colors = (ca.attr & 0x01) ? 0xff : 0x00;
258             u8 pixels = colors & GET_GLOBAL(fdata_g[src + i]);
259             if (ca.attr & 0x80)
260                 pixels ^= GET_FARVAR(SEG_GRAPH, *dest_far);
261             SET_FARVAR(SEG_CTEXT, *dest_far, pixels);
262         } else {
263             u16 pixels = 0;
264             u8 fontline = GET_GLOBAL(fdata_g[src + i]);
265             int j;
266             for (j = 0; j < 8; j++)
267                 if (fontline & (1<<j))
268                     pixels |= (ca.attr & 0x03) << (j*2);
269             pixels = htons(pixels);
270             if (ca.attr & 0x80)
271                 pixels ^= GET_FARVAR(SEG_GRAPH, *(u16*)dest_far);
272             SET_FARVAR(SEG_CTEXT, *(u16*)dest_far, pixels);
273         }
274     }
275 }
276
277 static void
278 write_gfx_char_lin(struct vgamode_s *vmode_g
279                    , struct cursorpos cp, struct carattr ca)
280 {
281     // Get the dimensions
282     u16 nbcols = GET_BDA(video_cols);
283     if (cp.x >= nbcols)
284         return;
285
286     u8 *fdata_g = vgafont8;
287     u16 addr = cp.x * 8 + cp.y * nbcols * 64;
288     u16 src = ca.car * 8;
289     u8 i;
290     for (i = 0; i < 8; i++) {
291         u8 *dest_far = (void*)(addr + i * nbcols * 8);
292         u8 fontline = GET_GLOBAL(fdata_g[src + i]);
293         u8 j;
294         for (j = 0; j < 8; j++) {
295             u8 pixel = (fontline & (0x80>>j)) ? ca.attr : 0x00;
296             SET_FARVAR(SEG_GRAPH, dest_far[j], pixel);
297         }
298     }
299 }
300
301 static void
302 write_text_char(struct vgamode_s *vmode_g
303                 , struct cursorpos cp, struct carattr ca)
304 {
305     // Compute the address
306     u16 nbcols = GET_BDA(video_cols);
307     void *address_far = (void*)(GET_BDA(video_pagesize) * cp.page
308                                 + (cp.x + cp.y * nbcols) * 2);
309
310     if (ca.use_attr) {
311         u16 dummy = (ca.attr << 8) | ca.car;
312         SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u16*)address_far, dummy);
313     } else {
314         SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)address_far, ca.car);
315     }
316 }
317
318 void
319 vgafb_write_char(struct cursorpos cp, struct carattr ca)
320 {
321     // Get the mode
322     struct vgamode_s *vmode_g = get_current_mode();
323     if (!vmode_g)
324         return;
325
326     // FIXME gfx mode not complete
327     switch (GET_GLOBAL(vmode_g->memmodel)) {
328     case MM_TEXT:
329         write_text_char(vmode_g, cp, ca);
330         break;
331     case MM_PLANAR:
332         write_gfx_char_pl4(vmode_g, cp, ca);
333         break;
334     case MM_CGA:
335         write_gfx_char_cga(vmode_g, cp, ca);
336         break;
337     case MM_DIRECT:
338     case MM_PACKED:
339         write_gfx_char_lin(vmode_g, cp, ca);
340         break;
341     }
342 }
343
344 struct carattr
345 vgafb_read_char(struct cursorpos cp)
346 {
347     // Get the mode
348     struct vgamode_s *vmode_g = get_current_mode();
349     if (!vmode_g)
350         goto fail;
351
352     if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
353         // FIXME gfx mode
354         dprintf(1, "Read char in graphics mode\n");
355         goto fail;
356     }
357
358     // Compute the address
359     u16 nbcols = GET_BDA(video_cols);
360     u16 *address_far = (void*)(GET_BDA(video_pagesize) * cp.page
361                                + (cp.x + cp.y * nbcols) * 2);
362     u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *address_far);
363     struct carattr ca = {v, v>>8, 0};
364     return ca;
365
366 fail: ;
367     struct carattr ca2 = {0, 0, 0};
368     return ca2;
369 }
370
371
372 /****************************************************************
373  * Read/write pixels
374  ****************************************************************/
375
376 void
377 vgafb_write_pixel(u8 color, u16 x, u16 y)
378 {
379     // Get the mode
380     struct vgamode_s *vmode_g = get_current_mode();
381     if (!vmode_g)
382         return;
383
384     u8 *addr_far, mask, attr, data, i;
385     switch (GET_GLOBAL(vmode_g->memmodel)) {
386     case MM_PLANAR:
387         addr_far = (void*)(x / 8 + y * GET_BDA(video_cols));
388         mask = 0x80 >> (x & 0x07);
389         for (i=0; i<4; i++) {
390             stdvga_planar4_plane(i);
391             u8 colors = (color & (1<<i)) ? 0xff : 0x00;
392             u8 orig = GET_FARVAR(SEG_GRAPH, *addr_far);
393             if (color & 0x80)
394                 colors ^= orig;
395             SET_FARVAR(SEG_GRAPH, *addr_far, (colors & mask) | (orig & ~mask));
396         }
397         stdvga_planar4_plane(-1);
398         break;
399     case MM_CGA:
400         if (GET_GLOBAL(vmode_g->depth) == 2)
401             addr_far = (void*)((x >> 2) + (y >> 1) * 80);
402         else
403             addr_far = (void*)((x >> 3) + (y >> 1) * 80);
404         if (y & 1)
405             addr_far += 0x2000;
406         data = GET_FARVAR(SEG_CTEXT, *addr_far);
407         if (GET_GLOBAL(vmode_g->depth) == 2) {
408             attr = (color & 0x03) << ((3 - (x & 0x03)) * 2);
409             mask = 0x03 << ((3 - (x & 0x03)) * 2);
410         } else {
411             attr = (color & 0x01) << (7 - (x & 0x07));
412             mask = 0x01 << (7 - (x & 0x07));
413         }
414         if (color & 0x80) {
415             data ^= attr;
416         } else {
417             data &= ~mask;
418             data |= attr;
419         }
420         SET_FARVAR(SEG_CTEXT, *addr_far, data);
421         break;
422     case MM_DIRECT:
423     case MM_PACKED:
424         addr_far = (void*)(x + y * (GET_BDA(video_cols) * 8));
425         SET_FARVAR(SEG_GRAPH, *addr_far, color);
426         break;
427     case MM_TEXT:
428         return;
429     }
430 }
431
432 u8
433 vgafb_read_pixel(u16 x, u16 y)
434 {
435     // Get the mode
436     struct vgamode_s *vmode_g = get_current_mode();
437     if (!vmode_g)
438         return 0;
439
440     u8 *addr_far, mask, attr=0, data, i;
441     switch (GET_GLOBAL(vmode_g->memmodel)) {
442     case MM_PLANAR:
443         addr_far = (void*)(x / 8 + y * GET_BDA(video_cols));
444         mask = 0x80 >> (x & 0x07);
445         attr = 0x00;
446         for (i = 0; i < 4; i++) {
447             stdvga_planar4_plane(i);
448             data = GET_FARVAR(SEG_GRAPH, *addr_far) & mask;
449             if (data > 0)
450                 attr |= (0x01 << i);
451         }
452         stdvga_planar4_plane(-1);
453         break;
454     case MM_CGA:
455         addr_far = (void*)((x >> 2) + (y >> 1) * 80);
456         if (y & 1)
457             addr_far += 0x2000;
458         data = GET_FARVAR(SEG_CTEXT, *addr_far);
459         if (GET_GLOBAL(vmode_g->depth) == 2)
460             attr = (data >> ((3 - (x & 0x03)) * 2)) & 0x03;
461         else
462             attr = (data >> (7 - (x & 0x07))) & 0x01;
463         break;
464     case MM_DIRECT:
465     case MM_PACKED:
466         addr_far = (void*)(x + y * (GET_BDA(video_cols) * 8));
467         attr = GET_FARVAR(SEG_GRAPH, *addr_far);
468         break;
469     case MM_TEXT:
470         return 0;
471     }
472     return attr;
473 }