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