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