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