fixed vbe
[vidsys] / drv_vbe.c
1 #include <string.h>
2 #include <conio.h>
3 #include <i86.h>
4 #include "vidsys.h"
5 #include "drv.h"
6 #include "vbe.h"
7 #include "vga.h"
8 #include "cdpmi.h"
9
10 #define farptr_to_linear(rmaddr) \
11         ((((intptr_t)(rmaddr) >> 12) & 0xffff0) + ((intptr_t)(rmaddr) & 0xffff))
12
13 static int init(void);
14 static void cleanup(void);
15 static struct vid_modeinfo *find_mode(int mode);
16 static int setmode(int mode);
17 static int getmode(void);
18 static const char *memsize_str(long sz);
19 static int get_mode_info(int mode, struct vbe_mode_info *mi);
20 static int conv_vbeinfo(int mode, struct vid_modeinfo *mi, struct vbe_mode_info *vbemi);
21 static unsigned int calc_mask(int nbits, int pos);
22
23 static void pack(uint32_t *pix, int r, int g, int b);
24 static void unpack(uint32_t pix, int *r, int *g, int *b);
25 static void clear(uint32_t color);
26 static void blitfb_lfb(void *fb, int pitch);
27 static void blitfb_banked(void *fb, int pitch);
28 static void flip(int vsync);
29
30 static struct vid_driver drv;
31 static struct vid_drvops drvops = {init, cleanup, setmode, getmode};
32 static unsigned int vbe_ver;
33
34 static int cur_mode;
35 static struct vid_modeinfo *cur_mi;
36 static int cur_pgsize;
37
38
39 void vid_register_vbe(void)
40 {
41         drv.name = "vbe";
42         drv.prio = 2;
43         drv.ops = &drvops;
44
45         vid_drvlist[vid_numdrv++] = &drv;
46 }
47
48
49 static int init(void)
50 {
51         struct dpmi_regs regs = {0};
52         struct vbe_info *vbe;
53         struct vbe_mode_info vbemi;
54         unsigned short bufseg;
55         uint16_t *vbe_modelist, *modelist;
56         int i, count;
57         struct vid_modeinfo modeinf;
58
59         cur_mode = -1;
60         cur_mi = 0;
61
62         vbe = dpmi_lowbuf();
63         bufseg = (intptr_t)vbe >> 4;
64
65         /* call VBE function 00 (get controller information) */
66         memcpy(vbe->sig, "VBE2", 4);    /* denote we want VBE 2.0 info */
67         regs.eax = 0x4f00;
68         regs.es = bufseg;
69         dpmi_rmint(0x10, &regs);
70         if((regs.eax & 0xffff) != 0x4f || memcmp(vbe->sig, "VESA", 4) != 0) {
71                 fprintf(stderr, "failed to get VBE controller information\n");
72                 return -1;
73         }
74
75         vbe_ver = vbe->ver;
76
77         printf("Found VBE %d.%d\n", VBE_VER_MAJOR(vbe_ver), VBE_VER_MINOR(vbe_ver));
78         printf("OEM: %s\n", (char*)farptr_to_linear(vbe->oem_name));
79         if(vbe_ver >= 0x0200) {
80                 printf("%s - %s (%s)\n", (char*)farptr_to_linear(vbe->vendor),
81                                 (char*)farptr_to_linear(vbe->product),
82                                 (char*)farptr_to_linear(vbe->revstr));
83         }
84         printf("Video RAM: %s\n", memsize_str((long)vbe->vmem_blk * 65536));
85
86         vbe_modelist = (uint16_t*)farptr_to_linear(vbe->modelist_addr);
87         count = 0;
88         for(i=0; i<1024; i++) {
89                 if(vbe_modelist[i] == 0xffff) break;
90                 count++;
91         }
92
93         if(!(modelist = malloc(count * sizeof *modelist))) {
94                 fprintf(stderr, "failed to allocate mode list\n");
95                 return -1;
96         }
97         for(i=0; i<count; i++) {
98                 modelist[i] = vbe_modelist[i];
99         }
100
101         if(!(drv.modes = malloc(count * sizeof *drv.modes))) {
102                 fprintf(stderr, "failed to allocate mode list\n");
103                 free(modelist);
104                 return -1;
105         }
106
107         drv.num_modes = 0;
108         for(i=0; i<count; i++) {
109                 if(get_mode_info(modelist[i], &vbemi) == -1) {
110                         continue;
111                 }
112                 if(conv_vbeinfo(modelist[i], drv.modes + drv.num_modes, &vbemi) == -1) {
113                         continue;
114                 }
115                 drv.num_modes++;
116         }
117
118         free(modelist);
119         return 0;
120 }
121
122 static void cleanup(void)
123 {
124         free(drv.modes);
125         drv.modes = 0;
126         drv.num_modes = 0;
127 }
128
129 static struct vid_modeinfo *find_mode(int mode)
130 {
131         int i;
132         for(i=0; i<drv.num_modes; i++) {
133                 if(drv.modes[i].modeno == mode) {
134                         return drv.modes + i;
135                 }
136         }
137         return 0;
138 }
139
140 static int setmode(int mode)
141 {
142         struct vid_modeinfo *minf;
143         struct dpmi_regs regs = {0};
144
145         if((minf = find_mode(mode)) && minf->lfb) {
146                 mode |= VBE_MODE_LFB;
147         }
148
149 retry:
150         regs.eax = 0x4f02;
151         regs.ebx = mode;
152         dpmi_rmint(0x10, &regs);
153
154         if((regs.eax & 0xffff) != 0x4f) {
155                 if(mode & VBE_MODE_LFB) {
156                         mode &= ~VBE_MODE_LFB;
157                         goto retry;
158                 }
159                 return -1;
160         }
161         cur_mode = mode;
162
163         if(!(cur_mi = minf)) return 0;
164
165         cur_pgsize = minf->height * minf->pitch;
166
167         if(mode & VBE_MODE_LFB) {
168                 minf->ops.blitfb = blitfb_lfb;
169         } else {
170                 minf->ops.blitfb = blitfb_banked;
171         }
172         return 0;
173 }
174
175 static int getmode(void)
176 {
177         return cur_mode;
178 }
179
180 static const char *memsize_str(long sz)
181 {
182         static const char *suffix[] = {"bytes", "kb", "mb", "gb", 0};
183         static int cnt = 0;
184         static char buf[64];
185
186         while(sz > 1024 && suffix[cnt + 1]) {
187                 sz >>= 10;
188                 cnt++;
189         }
190
191         sprintf(buf, "%ld %s", sz, suffix[cnt]);
192         return buf;
193 }
194
195 static int get_mode_info(int mode, struct vbe_mode_info *mi)
196 {
197         struct dpmi_regs regs = {0};
198         struct vbe_mode_info *miptr;
199         uint16_t bufseg;
200
201         miptr = dpmi_lowbuf();
202         bufseg = (intptr_t)miptr >> 4;
203
204         regs.eax = 0x4f01;
205         regs.ecx = mode;
206         regs.es = bufseg;
207         dpmi_rmint(0x10, &regs);
208         if((regs.eax & 0xffff) != 0x4f) {
209                 return -1;
210         }
211
212         *mi = *miptr;
213         return 0;
214 }
215
216 int vid_setwin(int wid, int pos)
217 {
218         struct dpmi_regs regs = {0};
219
220         regs.eax = 0x4f05;
221         regs.ebx = wid;
222         regs.edx = pos;
223         dpmi_rmint(0x10, &regs);
224         if((regs.eax & 0xffff) != 0x4f) {
225                 return -1;
226         }
227         return 0;
228 }
229
230 static int conv_vbeinfo(int mode, struct vid_modeinfo *mi, struct vbe_mode_info *vbemi)
231 {
232         static int gran_shift;
233         static const struct { int width, height, bpp; } stdmode[] = {
234                 {640, 400, 8},          /* 100h */
235                 {640, 480, 8},          /* 101h */
236                 {800, 600, 4}, {800, 600, 8},           /* 102h - 103h */
237                 {1024, 768, 4}, {1024, 768, 8},         /* 104h - 105h */
238                 {1280, 1024, 4}, {1280, 1024, 8},       /* 106h - 107h */
239                 {80, 60, 4}, {132, 25, 4}, {132, 43, 4}, {132, 50, 4}, {132, 60, 4},
240                 {320, 200, 15}, {320, 200, 16}, {320, 200, 24}, /* 10dh - 10fh */
241                 {640, 480, 15}, {640, 480, 16}, {640, 480, 24}, /* 110h - 112h */
242                 {800, 600, 15}, {800, 600, 16}, {800, 600, 24}, /* 113h - 115h */
243                 {1024, 768, 15}, {1024, 768, 16}, {1024, 768, 24}, /* 116h - 118h */
244                 {1280, 1024, 15}, {1280, 1024, 16}, {1280, 1024, 24} /* 119h - 11bh */
245         };
246
247         if(!(vbemi->attr & VBE_ATTR_AVAIL)) {
248                 return -1;      /* ignore unsupported modes */
249         }
250         if(!(vbemi->attr & VBE_ATTR_GFX)) {
251                 return -1;      /* ignore text modes */
252         }
253         if(vbemi->attr & VBE_ATTR_LFB) {
254                 mi->lfb = 1;
255         }
256
257         mi->drv = &drv;
258         mi->modeno = mode;
259         mi->vmem_addr = 0xa0000;
260
261         if(vbe_ver >= 0x0102) {
262                 mi->width = vbemi->xres;
263                 mi->height = vbemi->yres;
264                 mi->bpp = vbemi->bpp;
265                 mi->rshift = vbemi->rpos;
266                 mi->gshift = vbemi->gpos;
267                 mi->bshift = vbemi->bpos;
268                 mi->rmask = calc_mask(vbemi->rsize, vbemi->rpos);
269                 mi->gmask = calc_mask(vbemi->gsize, vbemi->gpos);
270                 mi->bmask = calc_mask(vbemi->bsize, vbemi->bpos);
271                 mi->pages = vbemi->num_img_pages + 1;
272
273                 if(vbe_ver >= 0x0200) {
274                         mi->vmem_addr = vbemi->fb_addr;
275                         mi->vmem_size = vbemi->scanline_bytes * mi->height * mi->pages;
276                 }
277         } else {
278                 if((mode & 0xff) > 7) {
279                         return -1;
280                 }
281                 mi->width = stdmode[mode & 0xff].width;
282                 mi->height = stdmode[mode & 0xff].height;
283                 mi->bpp = stdmode[mode & 0xff].bpp;
284         }
285         mi->ncolors = 1 << mi->bpp;
286         mi->pitch = vbemi->scanline_bytes;
287         mi->win_size = vbemi->win_size;
288         mi->win_gran = vbemi->win_gran;
289
290         gran_shift = 0;
291         mi->win_step = 1;
292         if(mi->win_gran > 0 && mi->win_gran < 64) {
293                 int gran = mi->win_gran;
294                 while(gran < 64) {
295                         gran_shift++;
296                         gran <<= 1;
297                 }
298                 mi->win_step = 1 << gran_shift;
299         }
300
301         mi->ops.pack = pack;
302         mi->ops.unpack = unpack;
303         mi->ops.setpal = vga_setpal;
304         mi->ops.getpal = vga_getpal;
305         mi->ops.vsync = vid_vsync;
306         mi->ops.clear = clear;
307         mi->ops.blitfb = 0;
308         mi->ops.flip = flip;
309         return 0;
310 }
311
312 static unsigned int calc_mask(int nbits, int pos)
313 {
314         int i;
315         unsigned int mask = 0;
316
317         for(i=0; i<nbits; i++) {
318                 mask = (mask << 1) | 1;
319         }
320         return mask << pos;
321 }
322
323
324 static void pack(uint32_t *pix, int r, int g, int b)
325 {
326         *pix = (((uint32_t)r << cur_mi->rshift) & cur_mi->rmask) |
327                 (((uint32_t)g << cur_mi->gshift) & cur_mi->gmask) |
328                 (((uint32_t)b << cur_mi->bshift) & cur_mi->bmask);
329 }
330
331 static void unpack(uint32_t pix, int *r, int *g, int *b)
332 {
333         *r = (pix & cur_mi->rmask) >> cur_mi->rshift;
334         *g = (pix & cur_mi->gmask) >> cur_mi->gshift;
335         *b = (pix & cur_mi->bmask) >> cur_mi->bshift;
336 }
337
338 static void clear(uint32_t color)
339 {
340 }
341
342 static void blitfb_lfb(void *fb, int pitch)
343 {
344         int i;
345         unsigned char *dest, *src;
346
347         dest = vid_vmem;
348         src = fb;
349
350         for(i=0; i<cur_mi->height; i++) {
351                 memcpy(dest, src, cur_mi->pitch);
352                 dest += cur_mi->pitch;
353                 src += pitch;
354         }
355 }
356
357 static void blitfb_banked(void *fb, int pitch)
358 {
359         int sz, offs, pending;
360         unsigned char *pptr = fb;
361
362         /* assume initial window offset at 0 */
363         offs = 0;
364         pending = cur_pgsize;
365         while(pending > 0) {
366                 sz = pending > 65536 ? 65536 : pending;
367                 memcpy((void*)0xa0000, pptr, sz);
368                 pptr += sz;
369                 pending -= sz;
370                 offs += cur_mi->win_step;
371                 vid_setwin(0, offs);
372         }
373         vid_setwin(0, 0);
374 }
375
376 static void flip(int vsync)
377 {
378         /* TODO */
379 }