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