started on the rope sim
[dosdemo] / src / dos / gfx.c
1 #include <stdio.h>
2 #include <string.h>
3 #include "gfx.h"
4 #include "vbe.h"
5 #include "vga.h"
6 #include "cdpmi.h"
7
8 #define SAME_BPP(a, b)  \
9     ((a) == (b) || ((a) == 16 && (b) == 15) || ((a) == 15 && (b) == 16) || \
10      ((a) == 32 && (b) == 24) || ((a) == 24 && (b) == 32))
11
12 static int vbe_init_ver;
13 static struct vbe_info vbe;
14 static int mode, pgsize, fbsize;
15 static struct vbe_mode_info mode_info;
16
17 static void *vpgaddr[2];
18 static int fbidx;
19 static int pgcount;
20
21 static int init_vbe(void)
22 {
23         int i, num;
24
25         if(vbe_info(&vbe) == -1) {
26                 fprintf(stderr, "failed to retrieve VBE information\n");
27                 return -1;
28         }
29
30         vbe_print_info(stdout, &vbe);
31
32         num = vbe_num_modes(&vbe);
33         for(i=0; i<num; i++) {
34                 struct vbe_mode_info minf;
35
36                 if(vbe_mode_info(vbe.modes[i], &minf) == -1) {
37                         continue;
38                 }
39                 printf("%04x: ", vbe.modes[i]);
40                 vbe_print_mode_info(stdout, &minf);
41         }
42         fflush(stdout);
43
44         vbe_init_ver = VBE_VER_MAJOR(vbe.ver);
45         return 0;
46 }
47
48 void *set_video_mode(int xsz, int ysz, int bpp, int nbuf)
49 {
50         int i, nmodes;
51         int best_match_mode = -1;
52         struct vbe_mode_info minf;
53
54         if(!vbe_init_ver) {
55                 if(init_vbe() == -1) {
56                         fprintf(stderr, "failed to initialize VBE\n");
57                         return 0;
58                 }
59                 if(vbe_init_ver < 2) {
60                         fprintf(stderr, "VBE >= 2.0 required\n");
61                         return 0;
62                 }
63         }
64
65         mode = -1;
66         nmodes = vbe_num_modes(&vbe);
67         for(i=0; i<nmodes; i++) {
68                 if(vbe_mode_info(vbe.modes[i], &minf) == -1) {
69                         continue;
70                 }
71                 if(minf.xres != xsz || minf.yres != ysz) continue;
72                 if(minf.bpp == bpp) {
73                         mode = vbe.modes[i];
74                         break;
75                 }
76                 if(SAME_BPP(minf.bpp, bpp)) {
77                         best_match_mode = mode;
78                 }
79         }
80
81         if(mode == -1) {
82                 if(best_match_mode == -1) {
83                         fprintf(stderr, "failed to find requested video mode (%dx%d %d bpp)\n", xsz, ysz, bpp);
84                         return 0;
85                 }
86                 mode = best_match_mode;
87         }
88
89         vbe_mode_info(mode, &mode_info);
90         printf("setting video mode %x: (%dx%d %d)\n", (unsigned int)mode, mode_info.xres,
91                         mode_info.yres, mode_info.bpp);
92
93         if(vbe_setmode(mode | VBE_MODE_LFB) == -1) {
94                 fprintf(stderr, "failed to set video mode\n");
95                 return 0;
96         }
97
98         if(nbuf < 1) nbuf = 1;
99         if(nbuf > 2) nbuf = 2;
100         pgcount = nbuf > mode_info.num_img_pages ? mode_info.num_img_pages : nbuf;
101
102         pgsize = mode_info.xres * mode_info.yres * (bpp / 8);
103         fbsize = pgcount * pgsize;
104
105         vpgaddr[0] = (void*)dpmi_mmap(mode_info.fb_addr, fbsize);
106         memset(vpgaddr[0], 0xaa, fbsize);
107
108         if(pgcount > 1) {
109                 vpgaddr[1] = (char*)vpgaddr[0] + pgsize;
110                 fbidx = 1;
111                 page_flip(FLIP_NOW);    /* start with the second page visible */
112         } else {
113                 fbidx = 0;
114                 vpgaddr[1] = 0;
115         }
116
117         return vpgaddr[0];
118 }
119
120 int set_text_mode(void)
121 {
122         vga_setmode(3);
123         return 0;
124 }
125
126 void *page_flip(int vsync)
127 {
128         if(!vpgaddr[1]) {
129                 /* page flipping not supported */
130                 return vpgaddr[0];
131         }
132
133         vbe_swap(fbidx ? pgsize : 0, vsync ? VBE_SWAP_VBLANK : VBE_SWAP_NOW);
134         fbidx = (fbidx + 1) & 1;
135
136         return vpgaddr[fbidx];
137 }