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