fallback to banked modes if LFB is not available
[eradicate] / src / dos / gfx.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "cdpmi.h"
5 #include "gfx.h"
6 #include "vbe.h"
7 #include "vga.h"
8
9 #define SAME_BPP(a, b)  \
10         ((a) == (b) || ((a) == 16 && (b) == 15) || ((a) == 15 && (b) == 16) || \
11          ((a) == 32 && (b) == 24) || ((a) == 24 && (b) == 32))
12
13 static uint32_t calc_mask(int sz, int pos);
14
15 static struct video_mode *vmodes;
16 static int num_vmodes;
17
18 static int vbe_init_ver;
19 static struct vbe_info vbe;
20
21 /* current mode */
22 static struct video_mode *curmode;
23 static void *vpgaddr[2];
24 static int fbidx;
25 static int pgcount, pgsize, fbsize;
26
27 int init_video(void)
28 {
29         int i, num, max_modes;
30         struct video_mode *vmptr;
31
32         if(vbe_info(&vbe) == -1) {
33                 fprintf(stderr, "failed to retrieve VBE information\n");
34                 return -1;
35         }
36         vbe_print_info(stdout, &vbe);
37
38         num_vmodes = 0;
39         max_modes = 4;  /* TODO change */
40         if(!(vmodes = malloc(max_modes * sizeof *vmodes))) {
41                 fprintf(stderr, "failed to allocate video modes list\n");
42                 return -1;
43         }
44
45         num = vbe_num_modes(&vbe);
46         for(i=0; i<num; i++) {
47                 struct vbe_mode_info minf;
48
49                 if(vbe_mode_info(vbe.modes[i], &minf) == -1) {
50                         continue;
51                 }
52
53                 if(num_vmodes >= max_modes) {
54                         int newmax = max_modes ? (max_modes << 1) : 16;
55                         if(!(vmptr = realloc(vmodes, newmax * sizeof *vmodes))) {
56                                 fprintf(stderr, "failed to grow video mode list (%d)\n", newmax);
57                                 free(vmodes);
58                                 return -1;
59                         }
60                         vmodes = vmptr;
61                         max_modes = newmax;
62                 }
63
64                 vmptr = vmodes + num_vmodes++;
65                 memset(vmptr, 0, sizeof *vmptr);
66                 vmptr->mode = vbe.modes[i];
67                 vmptr->xsz = minf.xres;
68                 vmptr->ysz = minf.yres;
69                 vmptr->bpp = minf.bpp;
70                 vmptr->pitch = minf.scanline_bytes;
71                 if(minf.mem_model == VBE_TYPE_DIRECT) {
72                         vmptr->rbits = minf.rsize;
73                         vmptr->gbits = minf.gsize;
74                         vmptr->bbits = minf.bsize;
75                         vmptr->rshift = minf.rpos;
76                         vmptr->gshift = minf.gpos;
77                         vmptr->bshift = minf.bpos;
78                         vmptr->rmask = calc_mask(minf.rsize, minf.rpos);
79                         vmptr->gmask = calc_mask(minf.gsize, minf.gpos);
80                         vmptr->bmask = calc_mask(minf.bsize, minf.bpos);
81                 }
82                 if(minf.attr & VBE_ATTR_LFB) {
83                         vmptr->fb_addr = minf.fb_addr;
84                 } else {
85                         vmptr->bank_size = (uint32_t)minf.bank_size * 1024;
86                         if(!vmptr->bank_size) {
87                                 vmptr->bank_size = 65536;
88                         }
89                 }
90                 vmptr->max_pages = minf.num_img_pages;
91
92                 printf("%04x: ", vbe.modes[i]);
93                 vbe_print_mode_info(stdout, &minf);
94         }
95         fflush(stdout);
96
97         vbe_init_ver = VBE_VER_MAJOR(vbe.ver);
98         return 0;
99 }
100
101 void cleanup_video(void)
102 {
103         free(vmodes);
104 }
105
106 struct video_mode *video_modes(void)
107 {
108         return vmodes;
109 }
110
111 int num_video_modes(void)
112 {
113         return num_vmodes;
114 }
115
116 int match_video_mode(int xsz, int ysz, int bpp)
117 {
118         int i, best = -1;
119         struct video_mode *vm;
120
121         for(i=0; i<num_vmodes; i++) {
122                 vm = vmodes + i;
123                 if(vm->xsz != xsz || vm->ysz != ysz) continue;
124                 if(SAME_BPP(vm->bpp, bpp)) {
125                         best = i;
126                 }
127                 if(vm->bpp == bpp) break;
128         }
129
130         if(best == -1) {
131                 fprintf(stderr, "failed to find video mode %dx%d %d bpp)\n", xsz, ysz, bpp);
132                 return -1;
133         }
134         return best;
135 }
136
137 int find_video_mode(int mode)
138 {
139         int i;
140         struct video_mode *vm;
141
142         vm = vmodes;
143         for(i=0; i<num_vmodes; i++) {
144                 if(vm->mode == mode) return i;
145         }
146         return -1;
147 }
148
149 void *set_video_mode(int idx, int nbuf)
150 {
151         unsigned int mode;
152         struct video_mode *vm = vmodes + idx;
153
154         printf("setting video mode %x (%dx%d %d bpp)\n", (unsigned int)vm->mode,
155                         vm->xsz, vm->ysz, vm->bpp);
156         fflush(stdout);
157
158         mode = vm->mode | VBE_MODE_LFB;
159         if(vbe_setmode(mode) == -1) {
160                 mode = vm->mode;
161                 if(vbe_setmode(mode) == -1) {
162                         fprintf(stderr, "failed to set video mode %x\n", (unsigned int)vm->mode);
163                         return 0;
164                 }
165                 printf("Warning: failed to get a linear framebuffer. falling back to banked mode\n");
166         }
167
168         curmode = vm;
169         if(nbuf < 1) nbuf = 1;
170         if(nbuf > 2) nbuf = 2;
171         pgcount = nbuf > vm->max_pages ? vm->max_pages : nbuf;
172         pgsize = (vm->xsz * vm->bpp / 8) * vm->ysz;
173         fbsize = pgcount * pgsize;
174
175         printf("pgcount: %d, pgsize: %d, fbsize: %d\n", pgcount, pgsize, fbsize);
176         printf("phys addr: %p\n", (void*)vm->fb_addr);
177         fflush(stdout);
178
179         if(vm->fb_addr) {
180                 vpgaddr[0] = (void*)dpmi_mmap(vm->fb_addr, fbsize);
181                 if(!vpgaddr[0]) {
182                         fprintf(stderr, "failed to map framebuffer (phys: %lx, size: %d)\n",
183                                         (unsigned long)vm->fb_addr, fbsize);
184                         set_text_mode();
185                         return 0;
186                 }
187                 memset(vpgaddr[0], 0xaa, fbsize);
188
189                 if(pgcount > 1) {
190                         vpgaddr[1] = (char*)vpgaddr[0] + pgsize;
191                         fbidx = 1;
192                         page_flip(FLIP_NOW);    /* start with the second page visible */
193                 } else {
194                         fbidx = 0;
195                         vpgaddr[1] = 0;
196                 }
197         } else {
198                 vpgaddr[0] = (void*)0xa0000;
199                 vpgaddr[1] = 0;
200         }
201         return vpgaddr[0];
202 }
203
204 int set_text_mode(void)
205 {
206         vga_setmode(3);
207         curmode = 0;
208         return 0;
209 }
210
211 void *page_flip(int vsync)
212 {
213         if(!vpgaddr[1]) {
214                 /* page flipping not supported */
215                 return vpgaddr[0];
216         }
217
218         vbe_swap(fbidx ? pgsize : 0, vsync ? VBE_SWAP_VBLANK : VBE_SWAP_NOW);
219         fbidx = (fbidx + 1) & 1;
220
221         return vpgaddr[fbidx];
222 }
223
224 static uint32_t calc_mask(int sz, int pos)
225 {
226         int i;
227         uint32_t mask = 0;
228         while(sz-- > 0) {
229                 mask = (mask << 1) | 1;
230         }
231         return mask << pos;
232 }