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