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