59ec306b92e63924a35fd8bd8ccd8456c981e97f
[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 "cdpmi.h"
8
9 #define farptr_to_linear(rmaddr) \
10         ((((intptr_t)(rmaddr) >> 12) & 0xffff0) + ((intptr_t)(rmaddr) & 0xffff))
11
12 static int init(void);
13 static void cleanup(void);
14 static int setmode(int mode);
15 static int getmode(void);
16 static const char *memsize_str(long sz);
17 static int get_mode_info(int mode, struct vbe_mode_info *mi);
18 static int conv_vbeinfo(int mode, struct vid_modeinfo *mi, struct vbe_mode_info *vbemi);
19 static unsigned int calc_mask(int nbits, int pos);
20
21 static struct vid_driver drv;
22 static struct vid_drvops drvops = {init, cleanup, setmode, getmode};
23 static unsigned int vbe_ver;
24
25 static int cur_mode;
26
27
28 void vid_register_vbe(void)
29 {
30         drv.name = "vbe";
31         drv.prio = 2;
32         drv.ops = &drvops;
33
34         vid_drvlist[vid_numdrv++] = &drv;
35 }
36
37
38 static int init(void)
39 {
40         struct dpmi_regs regs = {0};
41         struct vbe_info *vbe;
42         struct vbe_mode_info vbemi;
43         unsigned short bufseg;
44         uint16_t *vbe_modelist, *modelist;
45         int i, count;
46         struct vid_modeinfo modeinf;
47
48         cur_mode = -1;
49
50         vbe = dpmi_lowbuf();
51         bufseg = (intptr_t)vbe >> 4;
52
53         /* call VBE function 00 (get controller information) */
54         memcpy(vbe->sig, "VBE2", 4);    /* denote we want VBE 2.0 info */
55         regs.eax = 0x4f00;
56         regs.es = bufseg;
57         dpmi_rmint(0x10, &regs);
58         if(regs.eax != 0x4f || memcmp(vbe->sig, "VESA", 4) != 0) {
59                 fprintf(stderr, "failed to get VBE controller information\n");
60                 return -1;
61         }
62
63         vbe_ver = vbe->ver;
64
65         printf("Found VBE %d.%d\n", VBE_VER_MAJOR(vbe_ver), VBE_VER_MINOR(vbe_ver));
66         printf("OEM: %s\n", (char*)farptr_to_linear(vbe->oem_name));
67         if(vbe_ver >= 0x0200) {
68                 printf("%s - %s (%s)\n", (char*)farptr_to_linear(vbe->vendor),
69                                 (char*)farptr_to_linear(vbe->product),
70                                 (char*)farptr_to_linear(vbe->revstr));
71         }
72         printf("Video RAM: %s\n", memsize_str((long)vbe->vmem_blk * 65536));
73
74         vbe_modelist = (uint16_t*)farptr_to_linear(vbe->modelist_addr);
75         count = 0;
76         for(i=0; i<1024; i++) {
77                 if(vbe_modelist[i] == 0xffff) break;
78                 count++;
79         }
80
81         if(!(modelist = malloc(count * sizeof *modelist))) {
82                 fprintf(stderr, "failed to allocate mode list\n");
83                 return -1;
84         }
85         for(i=0; i<count; i++) {
86                 modelist[i] = vbe_modelist[i];
87         }
88
89         if(!(drv.modes = malloc(count * sizeof *drv.modes))) {
90                 fprintf(stderr, "failed to allocate mode list\n");
91                 free(modelist);
92                 return -1;
93         }
94
95         drv.num_modes = 0;
96         for(i=0; i<count; i++) {
97                 if(get_mode_info(modelist[i], &vbemi) == -1) {
98                         continue;
99                 }
100                 if(conv_vbeinfo(modelist[i], drv.modes + drv.num_modes, &vbemi) == -1) {
101                         continue;
102                 }
103                 drv.num_modes++;
104         }
105
106         free(modelist);
107         return 0;
108 }
109
110 static void cleanup(void)
111 {
112         free(drv.modes);
113         drv.modes = 0;
114         drv.num_modes = 0;
115 }
116
117 static int setmode(int mode)
118 {
119         struct dpmi_regs regs = {0};
120
121         regs.eax = 0x4f02;
122         regs.ebx = mode | VBE_MODE_LFB;
123         dpmi_rmint(0x10, &regs);
124
125         if(regs.eax != 0x4f) {
126                 regs.eax = 0x4f02;
127                 regs.ebx = mode;
128                 dpmi_rmint(0x10, &regs);
129                 if(regs.eax != 0x4f) {
130                         return -1;
131                 }
132                 cur_mode = mode;
133         } else {
134                 cur_mode = mode | VBE_MODE_LFB;
135         }
136         return 0;
137 }
138
139 static int getmode(void)
140 {
141         return cur_mode;
142 }
143
144 static const char *memsize_str(long sz)
145 {
146         static const char *suffix[] = {"bytes", "kb", "mb", "gb", 0};
147         static int cnt = 0;
148         static char buf[64];
149
150         while(sz > 1024 && suffix[cnt + 1]) {
151                 sz >>= 10;
152                 cnt++;
153         }
154
155         sprintf(buf, "%ld %s", sz, suffix[cnt]);
156         return buf;
157 }
158
159 static int get_mode_info(int mode, struct vbe_mode_info *mi)
160 {
161         struct dpmi_regs regs = {0};
162         struct vbe_mode_info *miptr;
163         uint16_t bufseg;
164
165         miptr = dpmi_lowbuf();
166         bufseg = (intptr_t)miptr >> 4;
167
168         regs.eax = 0x4f01;
169         regs.ecx = mode;
170         regs.es = bufseg;
171         dpmi_rmint(0x10, &regs);
172         if(regs.eax != 0x4f) {
173                 return -1;
174         }
175
176         *mi = *miptr;
177         return 0;
178 }
179
180 static int conv_vbeinfo(int mode, struct vid_modeinfo *mi, struct vbe_mode_info *vbemi)
181 {
182         static const struct { int width, height, bpp; } stdmode[] = {
183                 {640, 400, 8},          /* 100h */
184                 {640, 480, 8},          /* 101h */
185                 {800, 600, 4}, {800, 600, 8},           /* 102h - 103h */
186                 {1024, 768, 4}, {1024, 768, 8},         /* 104h - 105h */
187                 {1280, 1024, 4}, {1280, 1024, 8},       /* 106h - 107h */
188                 {80, 60, 4}, {132, 25, 4}, {132, 43, 4}, {132, 50, 4}, {132, 60, 4},
189                 {320, 200, 15}, {320, 200, 16}, {320, 200, 24}, /* 10dh - 10fh */
190                 {640, 480, 15}, {640, 480, 16}, {640, 480, 24}, /* 110h - 112h */
191                 {800, 600, 15}, {800, 600, 16}, {800, 600, 24}, /* 113h - 115h */
192                 {1024, 768, 15}, {1024, 768, 16}, {1024, 768, 24}, /* 116h - 118h */
193                 {1280, 1024, 15}, {1280, 1024, 16}, {1280, 1024, 24} /* 119h - 11bh */
194         };
195
196         if(!(vbemi->attr & VBE_ATTR_AVAIL)) {
197                 return -1;      /* ignore unsupported modes */
198         }
199         if(!(vbemi->attr & VBE_ATTR_GFX)) {
200                 return -1;      /* ignore text modes */
201         }
202         if(vbemi->attr & VBE_ATTR_LFB) {
203                 mi->lfb = 1;
204         }
205
206         mi->drv = &drv;
207         mi->modeno = mode;
208         mi->vmem_addr = 0xa0000;
209
210         if(vbe_ver >= 0x0102) {
211                 mi->width = vbemi->xres;
212                 mi->height = vbemi->yres;
213                 mi->bpp = vbemi->bpp;
214                 mi->rshift = vbemi->rpos;
215                 mi->gshift = vbemi->gpos;
216                 mi->bshift = vbemi->bpos;
217                 mi->rmask = calc_mask(vbemi->rsize, vbemi->rpos);
218                 mi->gmask = calc_mask(vbemi->gsize, vbemi->gpos);
219                 mi->bmask = calc_mask(vbemi->bsize, vbemi->bpos);
220                 mi->pages = vbemi->num_img_pages + 1;
221
222                 if(vbe_ver >= 0x0200) {
223                         mi->vmem_addr = vbemi->fb_addr;
224                         mi->vmem_size = vbemi->scanline_bytes * mi->height * mi->pages;
225                 }
226         } else {
227                 if((mode & 0xff) > 7) {
228                         return -1;
229                 }
230                 mi->width = stdmode[mode & 0xff].width;
231                 mi->height = stdmode[mode & 0xff].height;
232                 mi->bpp = stdmode[mode & 0xff].bpp;
233         }
234         mi->ncolors = 1 << mi->bpp;
235         mi->pitch = vbemi->scanline_bytes;
236         mi->win_size = vbemi->win_size;
237         mi->win_gran = vbemi->win_gran;
238         return 0;
239 }
240
241 static unsigned int calc_mask(int nbits, int pos)
242 {
243         int i;
244         unsigned int mask = 0;
245
246         for(i=0; i<nbits; i++) {
247                 mask = (mask << 1) | 1;
248         }
249         return mask << pos;
250 }