3ada182faa32171e2dd97f2e0225c043b2aba781
[dosdemo] / src / dos / vbe.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <stddef.h>
4 #include <assert.h>
5 #include "vbe.h"
6 #include "cdpmi.h"
7
8
9 #define FIXPTR(ptr) \
10         do { \
11                 uint32_t paddr = (uint32_t)(ptr); \
12                 uint16_t pseg = paddr >> 16; \
13                 uint16_t poffs = paddr & 0xffff; \
14                 if(pseg == seg && poffs < 512) { \
15                         paddr = ((uint32_t)seg << 4) + poffs; \
16                 } else { \
17                         paddr = ((uint32_t)pseg << 4) + poffs; \
18                 } \
19                 (ptr) = (void*)phys_to_virt(paddr); \
20         } while(0)
21
22 /* hijack the "VESA" sig field, to pre-cache number of modes */
23 #define NMODES(inf) *(uint16_t*)((inf)->sig)
24 #define NACCMODES(inf) *(uint16_t*)((inf)->sig + 2)
25
26 static int cur_pitch;
27 /* TODO update cur_pitch on mode-change and on setscanlen */
28
29
30 int vbe_info(struct vbe_info *info)
31 {
32         int i, num;
33         void *lowbuf;
34         uint16_t seg, sel;
35         uint16_t *modeptr;
36         struct dpmi_regs regs = {0};
37
38         assert(sizeof *info == 512);
39
40         if(!(seg = dpmi_alloc(sizeof *info / 16, &sel))) {
41                 return -1;
42         }
43         lowbuf = (void*)phys_to_virt((uint32_t)seg << 4);
44
45         memcpy(lowbuf, "VBE2", 4);
46
47         regs.eax = 0x4f00;
48         regs.es = seg;
49         regs.edi = 0;
50         dpmi_int(0x10, &regs);
51
52         if((regs.eax & 0xffff) != 0x4f) {
53                 fprintf(stderr, "vbe_get_info (4f00) failed\n");
54                 dpmi_free(sel);
55                 return -1;
56         }
57
58         memcpy(info, lowbuf, sizeof *info);
59         dpmi_free(sel);
60
61         FIXPTR(info->oem_name);
62         FIXPTR(info->vendor);
63         FIXPTR(info->product);
64         FIXPTR(info->revstr);
65         FIXPTR(info->modes);
66         FIXPTR(info->accel_modes);
67
68         modeptr = info->modes;
69         while(*modeptr != 0xffff) {
70                 if(modeptr - info->modes >= 256) {
71                         modeptr = info->modes;
72                         break;
73                 }
74                 modeptr++;
75         }
76         NMODES(info) = modeptr - info->modes;
77
78         if(info->caps & VBE_ACCEL) {
79                 modeptr = info->accel_modes;
80                 while(*modeptr != 0xffff) {
81                         if(modeptr - info->accel_modes >= 256) {
82                                 modeptr = info->accel_modes;
83                                 break;
84                         }
85                         modeptr++;
86                 }
87                 NACCMODES(info) = modeptr - info->accel_modes;
88         }
89         return 0;
90 }
91
92 int vbe_num_modes(struct vbe_info *info)
93 {
94         return NMODES(info);
95 }
96
97 int vbe_mode_info(int mode, struct vbe_mode_info *minf)
98 {
99         int i, num;
100         void *lowbuf;
101         uint16_t seg, sel;
102         struct dpmi_regs regs = {0};
103
104         assert(sizeof *minf == 256);
105         assert(offsetof(struct vbe_mode_info, max_pixel_clock) == 0x3e);
106
107         if(!(seg = dpmi_alloc(sizeof *minf / 16, &sel))) {
108                 return -1;
109         }
110         lowbuf = (void*)phys_to_virt((uint32_t)seg << 4);
111
112         regs.eax = 0x4f01;
113         regs.ecx = mode;
114         regs.es = seg;
115         dpmi_int(0x10, &regs);
116
117         if((regs.eax & 0xffff) != 0x4f) {
118                 fprintf(stderr, "vbe_mode_info (4f01) failed\n");
119                 dpmi_free(sel);
120                 return -1;
121         }
122
123         memcpy(minf, lowbuf, sizeof *minf);
124         dpmi_free(sel);
125         return 0;
126 }
127
128 void vbe_print_info(FILE *fp, struct vbe_info *vinf)
129 {
130         fprintf(fp, "vbe version: %u.%u\n", VBE_VER_MAJOR(vinf->ver), VBE_VER_MINOR(vinf->ver));
131         if(VBE_VER_MAJOR(vinf->ver) >= 2) {
132                 fprintf(fp, "%s - %s (%s)\n", vinf->vendor, vinf->product, vinf->revstr);
133                 if(vinf->caps & VBE_ACCEL) {
134                         fprintf(fp, "vbe/af %d.%d\n", VBE_VER_MAJOR(vinf->accel_ver), VBE_VER_MINOR(vinf->accel_ver));
135                 }
136         } else {
137                 fprintf(fp, "oem: %s\n", vinf->oem_name);
138         }
139         fprintf(fp, "video memory: %dkb\n", vinf->vmem_blk * 64);
140
141         if(vinf->caps) {
142                 fprintf(fp, "caps:");
143                 if(vinf->caps & VBE_8BIT_DAC) fprintf(fp, " dac8");
144                 if(vinf->caps & VBE_NON_VGA) fprintf(fp, " non-vga");
145                 if(vinf->caps & VBE_DAC_BLANK) fprintf(fp, " dac-blank");
146                 if(vinf->caps & VBE_ACCEL) fprintf(fp, " af");
147                 if(vinf->caps & VBE_MUSTLOCK) fprintf(fp, " af-lock");
148                 if(vinf->caps & VBE_HWCURSOR) fprintf(fp, " af-curs");
149                 if(vinf->caps & VBE_HWCLIP) fprintf(fp, " af-clip");
150                 if(vinf->caps & VBE_TRANSP_BLT) fprintf(fp, " af-tblt");
151                 fprintf(fp, "\n");
152         }
153
154         fprintf(fp, "%d video modes available\n", NMODES(vinf));
155         if(vinf->caps & VBE_ACCEL) {
156                 fprintf(fp, "%d accelerated (VBE/AF) modes available\n", NACCMODES(vinf));
157         }
158         fflush(fp);
159 }
160
161 void vbe_print_mode_info(FILE *fp, struct vbe_mode_info *minf)
162 {
163         fprintf(fp, "%dx%d %dbpp", minf->xres, minf->yres, minf->bpp);
164
165         switch(minf->mem_model) {
166         case VBE_TYPE_DIRECT:
167                 fprintf(fp, " (rgb");
168                 if(0) {
169         case VBE_TYPE_YUV:
170                         fprintf(fp, " (yuv");
171                 }
172                 fprintf(fp, " %d%d%d)", minf->rsize, minf->gsize, minf->bsize);
173                 break;
174         case VBE_TYPE_PLANAR:
175                 fprintf(fp, " (%d planes)", minf->num_planes);
176                 break;
177         case VBE_TYPE_PACKED:
178                 fprintf(fp, " (packed)");
179                 break;
180         case VBE_TYPE_TEXT:
181                 fprintf(fp, " (%dx%d cells)", minf->xcharsz, minf->ycharsz);
182                 break;
183         case VBE_TYPE_CGA:
184                 fprintf(fp, " (CGA)");
185                 break;
186         case VBE_TYPE_UNCHAIN:
187                 fprintf(fp, " (unchained-%d)", minf->num_planes);
188                 break;
189         }
190         fprintf(fp, " %dpg", minf->num_img_pages);
191
192         if(minf->attr & VBE_ATTR_LFB) {
193                 fprintf(fp, " lfb@%lx", (unsigned long)minf->fb_addr);
194         }
195
196         fprintf(fp, " [");
197         if(minf->attr & VBE_ATTR_AVAIL) fprintf(fp, " avail");
198         if(minf->attr & VBE_ATTR_OPTINFO) fprintf(fp, " opt");
199         if(minf->attr & VBE_ATTR_TTY) fprintf(fp, " tty");
200         if(minf->attr & VBE_ATTR_COLOR) fprintf(fp, " color");
201         if(minf->attr & VBE_ATTR_GFX) fprintf(fp, " gfx");
202         if(minf->attr & VBE_ATTR_NOTVGA) fprintf(fp, " non-vga");
203         if(minf->attr & VBE_ATTR_BANKED) fprintf(fp, " banked");
204         if(minf->attr & VBE_ATTR_LFB) fprintf(fp, " lfb");
205         if(minf->attr & VBE_ATTR_DBLSCAN) fprintf(fp, " dblscan");
206         if(minf->attr & VBE_ATTR_ILACE) fprintf(fp, " ilace");
207         if(minf->attr & VBE_ATTR_TRIPLEBUF) fprintf(fp, " trplbuf");
208         if(minf->attr & VBE_ATTR_STEREO) fprintf(fp, " stereo");
209         if(minf->attr & VBE_ATTR_STEREO_2FB) fprintf(fp, " stdual");
210         fprintf(fp, " ]\n");
211         fflush(fp);
212 }
213
214 int vbe_setmode(uint16_t mode)
215 {
216         struct dpmi_regs regs = {0};
217
218         regs.eax = 0x4f02;
219         regs.ebx = mode;
220         dpmi_int(0x10, &regs);
221
222         if((regs.eax & 0xffff) != 0x4f) {
223                 return -1;
224         }
225
226         cur_pitch = vbe_getpitch();
227         return 0;
228 }
229
230 int vbe_setmode_crtc(uint16_t mode, struct vbe_crtc_info *crtc)
231 {
232         void *lowbuf;
233         uint16_t seg, sel;
234         struct dpmi_regs regs = {0};
235
236         assert(sizeof *crtc == 59);
237
238         if(!(seg = dpmi_alloc((sizeof *crtc + 15) / 16, &sel))) {
239                 return -1;
240         }
241         lowbuf = (void*)phys_to_virt((uint32_t)seg << 4);
242
243         memcpy(lowbuf, crtc, sizeof *crtc);
244
245         regs.eax = 0x4f02;
246         regs.ebx = mode;
247         regs.es = seg;
248         dpmi_int(0x10, &regs);
249
250         dpmi_free(sel);
251
252         if((regs.eax & 0xffff) != 0x4f) {
253                 return -1;
254         }
255
256         cur_pitch = vbe_getpitch();
257         return 0;
258 }
259
260 int vbe_getmode(void)
261 {
262         struct dpmi_regs regs = {0};
263
264         regs.eax = 0x4f03;
265         dpmi_int(0x10, &regs);
266
267         if((regs.eax & 0xffff) != 0x4f) {
268                 return -1;
269         }
270         return regs.ebx & 0xffff;
271 }
272
273 int vbe_state_size(unsigned int flags)
274 {
275         struct dpmi_regs regs = {0};
276
277         regs.eax = 0x4f04;
278         regs.edx = 0;
279         regs.ecx = flags;
280         dpmi_int(0x10, &regs);
281         if((regs.eax & 0xffff) != 0x4f) {
282                 return -1;
283         }
284         return (regs.ebx & 0xffff) * 64;
285 }
286
287 int vbe_save(void *stbuf, int sz, unsigned int flags)
288 {
289         void *lowbuf;
290         uint16_t seg, sel;
291         struct dpmi_regs regs = {0};
292
293         if(!(seg = dpmi_alloc((sz + 15) / 16, &sel))) {
294                 return -1;
295         }
296         lowbuf = (void*)phys_to_virt((uint32_t)seg << 4);
297
298         regs.eax = 0x4f04;
299         regs.edx = 1;   /* save */
300         regs.ecx = flags;
301         regs.es = seg;
302         dpmi_int(0x10, &regs);
303         if((regs.eax & 0xffff) != 0x4f) {
304                 dpmi_free(sel);
305                 return -1;
306         }
307
308         memcpy(stbuf, lowbuf, sz);
309         dpmi_free(sel);
310         return 0;
311 }
312
313 int vbe_restore(void *stbuf, int sz, unsigned int flags)
314 {
315         void *lowbuf;
316         uint16_t seg, sel;
317         struct dpmi_regs regs = {0};
318
319         if(!(seg = dpmi_alloc((sz + 15) / 16, &sel))) {
320                 return -1;
321         }
322         lowbuf = (void*)phys_to_virt((uint32_t)seg << 4);
323
324         memcpy(lowbuf, stbuf, sz);
325
326         regs.eax = 0x4f04;
327         regs.edx = 2;   /* restore */
328         regs.ecx = flags;
329         regs.es = seg;
330         dpmi_int(0x10, &regs);
331         dpmi_free(sel);
332         if((regs.eax & 0xffff) != 0x4f) {
333                 return -1;
334         }
335         return 0;
336 }
337
338 int vbe_setwin(int wid, int pos)
339 {
340         struct dpmi_regs regs;
341
342         if(wid & ~1) return -1;
343
344         regs.eax = 0x4f05;
345         regs.ebx = wid;
346         regs.edx = pos;
347         dpmi_int(0x10, &regs);
348
349         if((regs.eax & 0xffff) != 0x4f) {
350                 return -1;
351         }
352         return 0;
353 }
354
355 int vbe_getwin(int wid)
356 {
357         struct dpmi_regs regs;
358
359         if(wid & ~1) return -1;
360
361         regs.eax = 0x4f05;
362         regs.ebx = wid;
363         dpmi_int(0x10, &regs);
364
365         if((regs.eax & 0xffff) != 0x4f) {
366                 return -1;
367         }
368
369         return regs.edx & 0xffff;
370 }
371
372 int vbe_setscanlen(int len_pix)
373 {
374         struct dpmi_regs regs;
375
376         regs.eax = 0x4f06;
377         regs.ebx = 0;   /* set scanline length in pixels */
378         regs.ecx = len_pix;
379         dpmi_int(0x10, &regs);
380
381         if((regs.eax & 0xffff) != 0x4f) {
382                 return -1;
383         }
384
385         cur_pitch = vbe_getpitch();
386         return regs.ecx;
387 }
388
389 int vbe_getscanlen(void)
390 {
391         struct dpmi_regs regs;
392
393         regs.eax = 0x4f06;
394         regs.ebx = 1;   /* get scanline length */
395         dpmi_int(0x10, &regs);
396
397         if((regs.eax & 0xffff) != 0x4f) {
398                 return -1;
399         }
400         return regs.ecx;
401 }
402
403 int vbe_getpitch(void)
404 {
405         struct dpmi_regs regs;
406
407         regs.eax = 0x4f06;
408         regs.ebx = 1;   /* get scanline length */
409         dpmi_int(0x10, &regs);
410
411         if((regs.eax & 0xffff) != 0x4f) {
412                 return -1;
413         }
414         return regs.ebx;
415 }
416
417 int vbe_scanline_info(struct vbe_scanline_info *sinf)
418 {
419         struct dpmi_regs regs;
420
421         regs.eax = 0x4f06;
422         regs.ebx = 1;   /* get scanline length */
423         dpmi_int(0x10, &regs);
424
425         if((regs.eax & 0xffff) != 0x4f) {
426                 return -1;
427         }
428
429         sinf->size = regs.ebx & 0xffff;
430         sinf->num_pixels = regs.ecx & 0xffff;
431         sinf->max_scanlines = regs.edx & 0xffff;
432         return 0;
433 }
434
435 enum {
436         SDISP_SET                       = 0x00,
437         SDISP_GET                       = 0x01,
438         SDISP_ALTSET            = 0x02,
439         SDISP_SET_STEREO        = 0x03,
440         SDISP_GETSCHED          = 0x04,
441         SDISP_STEREO_ON         = 0x05,
442         SDISP_STEREO_OFF        = 0x06,
443         SDISP_SET_VBLANK        = 0x80,
444         SDISP_ALTSET_VBLANK     = 0x82,
445         SDISP_SET_STEREO_VBLANK = 0x83
446 };
447
448 int vbe_setdisp(int x, int y, int when)
449 {
450         struct dpmi_regs regs;
451
452         regs.eax = 0x4f07;
453         regs.ebx = (when == VBE_SWAP_VBLANK) ? SDISP_SET_VBLANK : SDISP_SET;
454         regs.ecx = x;
455         regs.edx = y;
456         dpmi_int(0x10, &regs);
457
458         if((regs.eax & 0xffff) != 0x4f) {
459                 return -1;
460         }
461         return 0;
462 }
463
464 int vbe_swap(uint32_t voffs, int when)
465 {
466         struct dpmi_regs regs;
467         int op;
468
469         switch(when) {
470         case VBE_SWAP_ASYNC:
471                 op = SDISP_ALTSET;
472                 break;
473
474         case VBE_SWAP_NOW:
475                 /* XXX is this the only way? */
476                 return vbe_setdisp(voffs % cur_pitch, voffs / cur_pitch, when);
477
478         case VBE_SWAP_VBLANK:
479         default:
480                 op = SDISP_ALTSET_VBLANK;
481                 break;
482         }
483
484
485         regs.eax = 0x4f07;
486         regs.ebx = op;
487         regs.ecx = voffs;
488         dpmi_int(0x10, &regs);
489
490         if((regs.eax & 0xffff) != 0x4f) {
491                 return -1;
492         }
493         return 0;
494 }
495
496 int vbe_swap_pending(void)
497 {
498         struct dpmi_regs regs;
499
500         regs.eax = 0x4f07;
501         regs.ebx = SDISP_GETSCHED;
502         dpmi_int(0x10, &regs);
503
504         if((regs.eax & 0xffff) != 0x4f) {
505                 return 0;
506         }
507         return regs.ecx;
508 }