f4fabdf55094beffd0e0293704655f5c2fe88dd5
[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*)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*)((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*)((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         return 0;
226 }
227
228 int vbe_setmode_crtc(uint16_t mode, struct vbe_crtc_info *crtc)
229 {
230         void *lowbuf;
231         uint16_t seg, sel;
232         struct dpmi_regs regs = {0};
233
234         assert(sizeof *crtc == 59);
235
236         if(!(seg = dpmi_alloc((sizeof *crtc + 15) / 16, &sel))) {
237                 return -1;
238         }
239         lowbuf = (void*)((uint32_t)seg << 4);
240
241         memcpy(lowbuf, crtc, sizeof *crtc);
242
243         regs.eax = 0x4f02;
244         regs.ebx = mode;
245         regs.es = seg;
246         dpmi_int(0x10, &regs);
247
248         dpmi_free(sel);
249
250         if((regs.eax & 0xffff) != 0x4f) {
251                 return -1;
252         }
253         return 0;
254 }
255
256 int vbe_getmode(void)
257 {
258         struct dpmi_regs regs = {0};
259
260         regs.eax = 0x4f03;
261         dpmi_int(0x10, &regs);
262
263         if((regs.eax & 0xffff) != 0x4f) {
264                 return -1;
265         }
266         return regs.ebx & 0xffff;
267 }
268
269 int vbe_state_size(unsigned int flags)
270 {
271         struct dpmi_regs regs = {0};
272
273         regs.eax = 0x4f04;
274         regs.edx = 0;
275         regs.ecx = flags;
276         dpmi_int(0x10, &regs);
277         if((regs.eax & 0xffff) != 0x4f) {
278                 return -1;
279         }
280         return (regs.ebx & 0xffff) * 64;
281 }
282
283 int vbe_save(void *stbuf, int sz, unsigned int flags)
284 {
285         void *lowbuf;
286         uint16_t seg, sel;
287         struct dpmi_regs regs = {0};
288
289         if(!(seg = dpmi_alloc((sz + 15) / 16, &sel))) {
290                 return -1;
291         }
292         lowbuf = (void*)((uint32_t)seg << 4);
293
294         regs.eax = 0x4f04;
295         regs.edx = 1;   /* save */
296         regs.ecx = flags;
297         regs.es = seg;
298         dpmi_int(0x10, &regs);
299         if((regs.eax & 0xffff) != 0x4f) {
300                 dpmi_free(sel);
301                 return -1;
302         }
303
304         memcpy(stbuf, lowbuf, sz);
305         dpmi_free(sel);
306         return 0;
307 }
308
309 int vbe_restore(void *stbuf, int sz, unsigned int flags)
310 {
311         void *lowbuf;
312         uint16_t seg, sel;
313         struct dpmi_regs regs = {0};
314
315         if(!(seg = dpmi_alloc((sz + 15) / 16, &sel))) {
316                 return -1;
317         }
318         lowbuf = (void*)((uint32_t)seg << 4);
319
320         memcpy(lowbuf, stbuf, sz);
321
322         regs.eax = 0x4f04;
323         regs.edx = 2;   /* restore */
324         regs.ecx = flags;
325         regs.es = seg;
326         dpmi_int(0x10, &regs);
327         dpmi_free(sel);
328         if((regs.eax & 0xffff) != 0x4f) {
329                 return -1;
330         }
331         return 0;
332 }
333
334 int vbe_setwin(int wid, int pos)
335 {
336         struct dpmi_regs regs;
337
338         if(wid & ~1) return -1;
339
340         regs.eax = 0x4f05;
341         regs.ebx = wid;
342         regs.edx = pos;
343         dpmi_int(0x10, &regs);
344
345         if((regs.eax & 0xffff) != 0x4f) {
346                 return -1;
347         }
348         return 0;
349 }
350
351 int vbe_getwin(int wid)
352 {
353         struct dpmi_regs regs;
354
355         if(wid & ~1) return -1;
356
357         regs.eax = 0x4f05;
358         regs.ebx = wid;
359         dpmi_int(0x10, &regs);
360
361         if((regs.eax & 0xffff) != 0x4f) {
362                 return -1;
363         }
364
365         return regs.edx & 0xffff;
366 }
367
368 int vbe_setscanlen(int len_pix)
369 {
370         struct dpmi_regs regs;
371
372         regs.eax = 0x4f06;
373         regs.ebx = 0;   /* set scanline length in pixels */
374         regs.ecx = len_pix;
375         dpmi_int(0x10, &regs);
376
377         if((regs.eax & 0xffff) != 0x4f) {
378                 return -1;
379         }
380
381         return regs.ecx;
382 }
383
384 int vbe_getscanlen(void)
385 {
386         struct dpmi_regs regs;
387
388         regs.eax = 0x4f06;
389         regs.ebx = 1;   /* get scanline length */
390         dpmi_int(0x10, &regs);
391
392         if((regs.eax & 0xffff) != 0x4f) {
393                 return -1;
394         }
395         return regs.ecx;
396 }
397
398 int vbe_getpitch(void)
399 {
400         struct dpmi_regs regs;
401
402         regs.eax = 0x4f06;
403         regs.ebx = 1;   /* get scanline length */
404         dpmi_int(0x10, &regs);
405
406         if((regs.eax & 0xffff) != 0x4f) {
407                 return -1;
408         }
409         return regs.ebx;
410 }
411
412 int vbe_scanline_info(struct vbe_scanline_info *sinf)
413 {
414         struct dpmi_regs regs;
415
416         regs.eax = 0x4f06;
417         regs.ebx = 1;   /* get scanline length */
418         dpmi_int(0x10, &regs);
419
420         if((regs.eax & 0xffff) != 0x4f) {
421                 return -1;
422         }
423
424         sinf->size = regs.ebx & 0xffff;
425         sinf->num_pixels = regs.ecx & 0xffff;
426         sinf->max_scanlines = regs.edx & 0xffff;
427         return 0;
428 }
429
430 enum {
431         SDISP_SET                       = 0x00,
432         SDISP_GET                       = 0x01,
433         SDISP_ALTSET            = 0x02,
434         SDISP_SET_STEREO        = 0x03,
435         SDISP_GETSCHED  = 0x04,
436         SDISP_STEREO_ON = 0x05,
437         SDISP_STEREO_OFF        = 0x06,
438         SDISP_SET_VBLANK        = 0x80,
439         SDISP_ALTSET_VBLANK     = 0x82,
440         SDISP_SET_STEREO_VBLANK = 0x83
441 };
442
443 int vbe_setdisp(int x, int y, int when)
444 {
445         struct dpmi_regs regs;
446
447         regs.eax = 0x4f07;
448         regs.ebx = (when == VBE_SWAP_VBLANK) ? SDISP_SET_VBLANK : SDISP_SET;
449         regs.ecx = x;
450         regs.edx = y;
451         dpmi_int(0x10, &regs);
452
453         if((regs.eax & 0xffff) != 0x4f) {
454                 return -1;
455         }
456         return 0;
457 }
458
459 int vbe_swap(uint32_t voffs, int when)
460 {
461         struct dpmi_regs regs;
462         int op;
463
464         switch(when) {
465         case VBE_SWAP_ASYNC:
466                 op = SDISP_ALTSET;
467                 break;
468
469         case VBE_SWAP_NOW:
470                 /* XXX is this the only way? */
471                 return vbe_setdisp(voffs % cur_pitch, voffs / cur_pitch, when);
472
473         case VBE_SWAP_VBLANK:
474         default:
475                 op = SDISP_ALTSET_VBLANK;
476                 break;
477         }
478
479
480         regs.eax = 0x4f07;
481         regs.ebx = op;
482         regs.ecx = voffs;
483         dpmi_int(0x10, &regs);
484
485         if((regs.eax & 0xffff) != 0x4f) {
486                 return -1;
487         }
488         return 0;
489 }
490
491 int vbe_swap_pending(void)
492 {
493         struct dpmi_regs regs;
494
495         regs.eax = 0x4f07;
496         regs.ebx = SDISP_GETSCHED;
497         dpmi_int(0x10, &regs);
498
499         if((regs.eax & 0xffff) != 0x4f) {
500                 return 0;
501         }
502         return regs.ecx;
503 }