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