vgabios: Move stdvga_set_mode() to stdvgamodes.c.
[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         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     // Compute the address
326     u16 nbcols = GET_BDA(video_cols);
327     void *address_far = (void*)(GET_BDA(video_pagesize) * cp.page
328                                 + (cp.x + cp.y * nbcols) * 2);
329
330     if (ca.use_attr) {
331         u16 dummy = (ca.attr << 8) | ca.car;
332         SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u16*)address_far, dummy);
333     } else {
334         SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)address_far, ca.car);
335     }
336 }
337
338 void
339 vgafb_write_char(struct cursorpos cp, struct carattr ca)
340 {
341     // Get the mode
342     struct vgamode_s *vmode_g = get_current_mode();
343     if (!vmode_g)
344         return;
345
346     // FIXME gfx mode not complete
347     switch (GET_GLOBAL(vmode_g->memmodel)) {
348     case MM_TEXT:
349         write_text_char(vmode_g, cp, ca);
350         break;
351     case MM_PLANAR:
352         write_gfx_char_pl4(vmode_g, cp, ca);
353         break;
354     case MM_CGA:
355         write_gfx_char_cga(vmode_g, cp, ca);
356         break;
357     case MM_DIRECT:
358     case MM_PACKED:
359         write_gfx_char_lin(vmode_g, cp, ca);
360         break;
361     }
362 }
363
364 struct carattr
365 vgafb_read_char(struct cursorpos cp)
366 {
367     // Get the mode
368     struct vgamode_s *vmode_g = get_current_mode();
369     if (!vmode_g)
370         goto fail;
371
372     if (GET_GLOBAL(vmode_g->memmodel) != MM_TEXT) {
373         // FIXME gfx mode
374         dprintf(1, "Read char in graphics mode\n");
375         goto fail;
376     }
377
378     // Compute the address
379     u16 nbcols = GET_BDA(video_cols);
380     u16 *address_far = (void*)(GET_BDA(video_pagesize) * cp.page
381                                + (cp.x + cp.y * nbcols) * 2);
382     u16 v = GET_FARVAR(GET_GLOBAL(vmode_g->sstart), *address_far);
383     struct carattr ca = {v, v>>8, 0};
384     return ca;
385
386 fail: ;
387     struct carattr ca2 = {0, 0, 0};
388     return ca2;
389 }
390
391
392 /****************************************************************
393  * Read/write pixels
394  ****************************************************************/
395
396 void
397 vgafb_write_pixel(u8 color, u16 x, u16 y)
398 {
399     // Get the mode
400     struct vgamode_s *vmode_g = get_current_mode();
401     if (!vmode_g)
402         return;
403
404     u8 *addr_far, mask, attr, data, i;
405     switch (GET_GLOBAL(vmode_g->memmodel)) {
406     case MM_PLANAR:
407         addr_far = (void*)(x / 8 + y * GET_BDA(video_cols));
408         mask = 0x80 >> (x & 0x07);
409         for (i=0; i<4; i++) {
410             stdvga_planar4_plane(i);
411             u8 colors = (color & (1<<i)) ? 0xff : 0x00;
412             u8 orig = GET_FARVAR(SEG_GRAPH, *addr_far);
413             if (color & 0x80)
414                 colors ^= orig;
415             SET_FARVAR(SEG_GRAPH, *addr_far, (colors & mask) | (orig & ~mask));
416         }
417         stdvga_planar4_plane(-1);
418         break;
419     case MM_CGA:
420         if (GET_GLOBAL(vmode_g->depth) == 2)
421             addr_far = (void*)((x >> 2) + (y >> 1) * 80);
422         else
423             addr_far = (void*)((x >> 3) + (y >> 1) * 80);
424         if (y & 1)
425             addr_far += 0x2000;
426         data = GET_FARVAR(SEG_CTEXT, *addr_far);
427         if (GET_GLOBAL(vmode_g->depth) == 2) {
428             attr = (color & 0x03) << ((3 - (x & 0x03)) * 2);
429             mask = 0x03 << ((3 - (x & 0x03)) * 2);
430         } else {
431             attr = (color & 0x01) << (7 - (x & 0x07));
432             mask = 0x01 << (7 - (x & 0x07));
433         }
434         if (color & 0x80) {
435             data ^= attr;
436         } else {
437             data &= ~mask;
438             data |= attr;
439         }
440         SET_FARVAR(SEG_CTEXT, *addr_far, data);
441         break;
442     case MM_DIRECT:
443     case MM_PACKED:
444         addr_far = (void*)(x + y * (GET_BDA(video_cols) * 8));
445         SET_FARVAR(SEG_GRAPH, *addr_far, color);
446         break;
447     case MM_TEXT:
448         return;
449     }
450 }
451
452 u8
453 vgafb_read_pixel(u16 x, u16 y)
454 {
455     // Get the mode
456     struct vgamode_s *vmode_g = get_current_mode();
457     if (!vmode_g)
458         return 0;
459
460     u8 *addr_far, mask, attr=0, data, i;
461     switch (GET_GLOBAL(vmode_g->memmodel)) {
462     case MM_PLANAR:
463         addr_far = (void*)(x / 8 + y * GET_BDA(video_cols));
464         mask = 0x80 >> (x & 0x07);
465         attr = 0x00;
466         for (i = 0; i < 4; i++) {
467             stdvga_planar4_plane(i);
468             data = GET_FARVAR(SEG_GRAPH, *addr_far) & mask;
469             if (data > 0)
470                 attr |= (0x01 << i);
471         }
472         stdvga_planar4_plane(-1);
473         break;
474     case MM_CGA:
475         addr_far = (void*)((x >> 2) + (y >> 1) * 80);
476         if (y & 1)
477             addr_far += 0x2000;
478         data = GET_FARVAR(SEG_CTEXT, *addr_far);
479         if (GET_GLOBAL(vmode_g->depth) == 2)
480             attr = (data >> ((3 - (x & 0x03)) * 2)) & 0x03;
481         else
482             attr = (data >> (7 - (x & 0x07))) & 0x01;
483         break;
484     case MM_DIRECT:
485     case MM_PACKED:
486         addr_far = (void*)(x + y * (GET_BDA(video_cols) * 8));
487         attr = GET_FARVAR(SEG_GRAPH, *addr_far);
488         break;
489     case MM_TEXT:
490         return 0;
491     }
492     return attr;
493 }