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