vgabios: Rename vgaio.c to stdvga.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 "biosvar.h" // GET_BDA
9 #include "util.h" // memset_far
10 #include "vgabios.h" // find_vga_entry
11 #include "stdvga.h" // vgahw_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         vgahw_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     vgahw_grdc_write(0x05, 0x02);
67     memset_stride(SEG_GRAPH, dest_far, attr, cols, stride, nblines * cheight);
68     vgahw_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 void
162 clear_screen(struct vgamode_s *vmode_g)
163 {
164     switch (GET_GLOBAL(vmode_g->memmodel)) {
165     case CTEXT:
166     case MTEXT:
167         memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0720, 32*1024);
168         break;
169     case CGA:
170         memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 32*1024);
171         break;
172     default:
173         // XXX - old code gets/sets/restores sequ register 2 to 0xf -
174         // but it should always be 0xf anyway.
175         memset16_far(GET_GLOBAL(vmode_g->sstart), 0, 0x0000, 64*1024);
176     }
177 }
178
179
180 /****************************************************************
181  * Read/write characters to screen
182  ****************************************************************/
183
184 static void
185 write_gfx_char_pl4(struct vgamode_s *vmode_g
186                    , struct cursorpos cp, struct carattr ca)
187 {
188     u16 nbcols = GET_BDA(video_cols);
189     if (cp.x >= nbcols)
190         return;
191
192     u8 cheight = GET_GLOBAL(vmode_g->cheight);
193     u8 *fdata_g;
194     switch (cheight) {
195     case 14:
196         fdata_g = vgafont14;
197         break;
198     case 16:
199         fdata_g = vgafont16;
200         break;
201     default:
202         fdata_g = vgafont8;
203     }
204     u16 addr = cp.x + cp.y * cheight * nbcols;
205     u16 src = ca.car * cheight;
206     vgahw_sequ_write(0x02, 0x0f);
207     vgahw_grdc_write(0x05, 0x02);
208     if (ca.attr & 0x80)
209         vgahw_grdc_write(0x03, 0x18);
210     else
211         vgahw_grdc_write(0x03, 0x00);
212     u8 i;
213     for (i = 0; i < cheight; i++) {
214         u8 *dest_far = (void*)(addr + i * nbcols);
215         u8 j;
216         for (j = 0; j < 8; j++) {
217             u8 mask = 0x80 >> j;
218             vgahw_grdc_write(0x08, mask);
219             GET_FARVAR(SEG_GRAPH, *dest_far);
220             if (GET_GLOBAL(fdata_g[src + i]) & mask)
221                 SET_FARVAR(SEG_GRAPH, *dest_far, ca.attr & 0x0f);
222             else
223                 SET_FARVAR(SEG_GRAPH, *dest_far, 0x00);
224         }
225     }
226     vgahw_grdc_write(0x08, 0xff);
227     vgahw_grdc_write(0x05, 0x00);
228     vgahw_grdc_write(0x03, 0x00);
229 }
230
231 static void
232 write_gfx_char_cga(struct vgamode_s *vmode_g
233                    , struct cursorpos cp, struct carattr ca)
234 {
235     u16 nbcols = GET_BDA(video_cols);
236     if (cp.x >= nbcols)
237         return;
238
239     u8 *fdata_g = vgafont8;
240     u8 bpp = GET_GLOBAL(vmode_g->pixbits);
241     u16 addr = (cp.x * bpp) + cp.y * 320;
242     u16 src = ca.car * 8;
243     u8 i;
244     for (i = 0; i < 8; i++) {
245         u8 *dest_far = (void*)(addr + (i >> 1) * 80);
246         if (i & 1)
247             dest_far += 0x2000;
248         u8 mask = 0x80;
249         if (bpp == 1) {
250             u8 data = 0;
251             if (ca.attr & 0x80)
252                 data = GET_FARVAR(SEG_CTEXT, *dest_far);
253             u8 j;
254             for (j = 0; j < 8; j++) {
255                 if (GET_GLOBAL(fdata_g[src + i]) & mask) {
256                     if (ca.attr & 0x80)
257                         data ^= (ca.attr & 0x01) << (7 - j);
258                     else
259                         data |= (ca.attr & 0x01) << (7 - j);
260                 }
261                 mask >>= 1;
262             }
263             SET_FARVAR(SEG_CTEXT, *dest_far, data);
264         } else {
265             while (mask > 0) {
266                 u8 data = 0;
267                 if (ca.attr & 0x80)
268                     data = GET_FARVAR(SEG_CTEXT, *dest_far);
269                 u8 j;
270                 for (j = 0; j < 4; j++) {
271                     if (GET_GLOBAL(fdata_g[src + i]) & mask) {
272                         if (ca.attr & 0x80)
273                             data ^= (ca.attr & 0x03) << ((3 - j) * 2);
274                         else
275                             data |= (ca.attr & 0x03) << ((3 - j) * 2);
276                     }
277                     mask >>= 1;
278                 }
279                 SET_FARVAR(SEG_CTEXT, *dest_far, data);
280                 dest_far += 1;
281             }
282         }
283     }
284 }
285
286 static void
287 write_gfx_char_lin(struct vgamode_s *vmode_g
288                    , struct cursorpos cp, struct carattr ca)
289 {
290     // Get the dimensions
291     u16 nbcols = GET_BDA(video_cols);
292     if (cp.x >= nbcols)
293         return;
294
295     u8 *fdata_g = vgafont8;
296     u16 addr = cp.x * 8 + cp.y * nbcols * 64;
297     u16 src = ca.car * 8;
298     u8 i;
299     for (i = 0; i < 8; i++) {
300         u8 *dest_far = (void*)(addr + i * nbcols * 8);
301         u8 mask = 0x80;
302         u8 j;
303         for (j = 0; j < 8; j++) {
304             u8 data = 0x00;
305             if (GET_GLOBAL(fdata_g[src + i]) & mask)
306                 data = ca.attr;
307             SET_FARVAR(SEG_GRAPH, dest_far[j], data);
308             mask >>= 1;
309         }
310     }
311 }
312
313 static void
314 write_text_char(struct vgamode_s *vmode_g
315                 , struct cursorpos cp, struct carattr ca)
316 {
317     // Get the dimensions
318     u16 nbrows = GET_BDA(video_rows) + 1;
319     u16 nbcols = GET_BDA(video_cols);
320
321     // Compute the address
322     void *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, cp.page)
323                                 + (cp.x + cp.y * nbcols) * 2);
324
325     if (ca.use_attr) {
326         u16 dummy = (ca.attr << 8) | ca.car;
327         SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u16*)address_far, dummy);
328     } else {
329         SET_FARVAR(GET_GLOBAL(vmode_g->sstart), *(u8*)address_far, ca.car);
330     }
331 }
332
333 void
334 vgafb_write_char(struct cursorpos cp, struct carattr ca)
335 {
336     // Get the mode
337     struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
338     if (!vmode_g)
339         return;
340
341     // FIXME gfx mode not complete
342     switch (GET_GLOBAL(vmode_g->memmodel)) {
343     case CTEXT:
344     case MTEXT:
345         write_text_char(vmode_g, cp, ca);
346         break;
347     case PLANAR4:
348     case PLANAR1:
349         write_gfx_char_pl4(vmode_g, cp, ca);
350         break;
351     case CGA:
352         write_gfx_char_cga(vmode_g, cp, ca);
353         break;
354     case LINEAR8:
355         write_gfx_char_lin(vmode_g, cp, ca);
356         break;
357     }
358 }
359
360 struct carattr
361 vgafb_read_char(struct cursorpos cp)
362 {
363     // Get the mode
364     struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
365     if (!vmode_g)
366         goto fail;
367
368     if (!(GET_GLOBAL(vmode_g->memmodel) & TEXT)) {
369         // FIXME gfx mode
370         dprintf(1, "Read char in graphics mode\n");
371         goto fail;
372     }
373
374     // Get the dimensions
375     u16 nbrows = GET_BDA(video_rows) + 1;
376     u16 nbcols = GET_BDA(video_cols);
377
378     // Compute the address
379     u16 *address_far = (void*)(SCREEN_MEM_START(nbcols, nbrows, 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 = find_vga_entry(GET_BDA(video_mode));
400     if (!vmode_g)
401         return;
402     if (GET_GLOBAL(vmode_g->memmodel) & TEXT)
403         return;
404
405     u8 *addr_far, mask, attr, data;
406     switch (GET_GLOBAL(vmode_g->memmodel)) {
407     case PLANAR4:
408     case PLANAR1:
409         addr_far = (void*)(x / 8 + y * GET_BDA(video_cols));
410         mask = 0x80 >> (x & 0x07);
411         vgahw_grdc_write(0x08, mask);
412         vgahw_grdc_write(0x05, 0x02);
413         data = GET_FARVAR(SEG_GRAPH, *addr_far);
414         if (color & 0x80)
415             vgahw_grdc_write(0x03, 0x18);
416         SET_FARVAR(SEG_GRAPH, *addr_far, color);
417         vgahw_grdc_write(0x08, 0xff);
418         vgahw_grdc_write(0x05, 0x00);
419         vgahw_grdc_write(0x03, 0x00);
420         break;
421     case CGA:
422         if (GET_GLOBAL(vmode_g->pixbits) == 2)
423             addr_far = (void*)((x >> 2) + (y >> 1) * 80);
424         else
425             addr_far = (void*)((x >> 3) + (y >> 1) * 80);
426         if (y & 1)
427             addr_far += 0x2000;
428         data = GET_FARVAR(SEG_CTEXT, *addr_far);
429         if (GET_GLOBAL(vmode_g->pixbits) == 2) {
430             attr = (color & 0x03) << ((3 - (x & 0x03)) * 2);
431             mask = 0x03 << ((3 - (x & 0x03)) * 2);
432         } else {
433             attr = (color & 0x01) << (7 - (x & 0x07));
434             mask = 0x01 << (7 - (x & 0x07));
435         }
436         if (color & 0x80) {
437             data ^= attr;
438         } else {
439             data &= ~mask;
440             data |= attr;
441         }
442         SET_FARVAR(SEG_CTEXT, *addr_far, data);
443         break;
444     case LINEAR8:
445         addr_far = (void*)(x + y * (GET_BDA(video_cols) * 8));
446         SET_FARVAR(SEG_GRAPH, *addr_far, color);
447         break;
448     }
449 }
450
451 u8
452 vgafb_read_pixel(u16 x, u16 y)
453 {
454     // Get the mode
455     struct vgamode_s *vmode_g = find_vga_entry(GET_BDA(video_mode));
456     if (!vmode_g)
457         return 0;
458     if (GET_GLOBAL(vmode_g->memmodel) & TEXT)
459         return 0;
460
461     u8 *addr_far, mask, attr=0, data, i;
462     switch (GET_GLOBAL(vmode_g->memmodel)) {
463     case PLANAR4:
464     case PLANAR1:
465         addr_far = (void*)(x / 8 + y * GET_BDA(video_cols));
466         mask = 0x80 >> (x & 0x07);
467         attr = 0x00;
468         for (i = 0; i < 4; i++) {
469             vgahw_grdc_write(0x04, i);
470             data = GET_FARVAR(SEG_GRAPH, *addr_far) & mask;
471             if (data > 0)
472                 attr |= (0x01 << i);
473         }
474         break;
475     case CGA:
476         addr_far = (void*)((x >> 2) + (y >> 1) * 80);
477         if (y & 1)
478             addr_far += 0x2000;
479         data = GET_FARVAR(SEG_CTEXT, *addr_far);
480         if (GET_GLOBAL(vmode_g->pixbits) == 2)
481             attr = (data >> ((3 - (x & 0x03)) * 2)) & 0x03;
482         else
483             attr = (data >> (7 - (x & 0x07))) & 0x01;
484         break;
485     case LINEAR8:
486         addr_far = (void*)(x + y * (GET_BDA(video_cols) * 8));
487         attr = GET_FARVAR(SEG_GRAPH, *addr_far);
488         break;
489     }
490     return attr;
491 }
492
493
494 /****************************************************************
495  * Font loading
496  ****************************************************************/
497
498 void
499 vgafb_load_font(u16 seg, void *src_far, u16 count
500                 , u16 start, u8 destflags, u8 fontsize)
501 {
502     get_font_access();
503     u16 blockaddr = ((destflags & 0x03) << 14) + ((destflags & 0x04) << 11);
504     void *dest_far = (void*)(blockaddr + start*32);
505     u16 i;
506     for (i = 0; i < count; i++)
507         memcpy_far(SEG_GRAPH, dest_far + i*32
508                    , seg, src_far + i*fontsize, fontsize);
509     release_font_access();
510 }