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