added page flipping/scrolling VBE calls
[dosrtxon] / src / dos / gfx.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <limits.h>
5 #include "gfx.h"
6 #include "vbe.h"
7 #include "cdpmi.h"
8
9 #ifdef __DJGPP__
10 #include <sys/nearptr.h>
11
12 #define REALPTR(s, o)   (void*)(((uint32_t)(s) << 4) - __djgpp_base_address + (uint32_t)(o))
13 #else
14 #define REALPTR(s, o)   (void*)(((uint32_t)(s) << 4) + (uint32_t)(o))
15 #endif
16
17 #define VBEPTR(x)               REALPTR(((x) & 0xffff0000) >> 16, (x) & 0xffff)
18 #define VBEPTR_SEG(x)   (((x) & 0xffff0000) >> 16)
19 #define VBEPTR_OFF(x)   ((x) & 0xffff)
20
21 #define SAME_BPP(a, b)  \
22         ((a) == (b) || ((a) == 16 && (b) == 15) || ((a) == 15 && (b) == 16) || \
23          ((a) == 32 && (b) == 24) || ((a) == 24 && (b) == 32))
24
25 static unsigned int make_mask(int sz, int pos);
26
27 static struct vbe_info *vbe_info;
28 static struct vbe_mode_info *mode_info;
29 static int pal_bits = 6;
30
31 static void *vpgaddr[2];
32
33
34 void *set_video_mode(int xsz, int ysz, int bpp)
35 {
36         int i;
37         static uint16_t *modes;
38         uint16_t best = 0;
39         unsigned int fbsize, pgsize;
40
41 #ifdef __DJGPP__
42         __djgpp_nearptr_enable();
43 #endif
44
45         /* check for VBE2 support and output some info */
46         if(!vbe_info) {
47                 if(!(vbe_info = vbe_get_info())) {
48                         fprintf(stderr, "VESA BIOS Extensions not available\n");
49                         return 0;
50                 }
51
52                 printf("VBE Version: %x.%x\n", vbe_info->version >> 8, vbe_info->version & 0xff);
53                 if(vbe_info->version < 0x200) {
54                         fprintf(stderr, "This program requires VBE 2.0 or greater. Try running UniVBE\n");
55                         return 0;
56                 }
57
58                 printf("Graphics adapter: %s, %s (%s)\n", (char*)VBEPTR(vbe_info->oem_vendor_name_ptr),
59                                 (char*)VBEPTR(vbe_info->oem_product_name_ptr), (char*)VBEPTR(vbe_info->oem_product_rev_ptr));
60                 printf("Video memory: %dkb\n", vbe_info->total_mem << 6);
61
62                 modes = VBEPTR(vbe_info->vid_mode_ptr);
63         }
64
65         for(i=0; i<1024; i++) { /* impose an upper limit to avoid inf-loops */
66                 if(modes[i] == 0xffff) {
67                         break;  /* reached the end */
68                 }
69
70                 mode_info = vbe_get_mode_info(modes[i] | VBE_MODE_LFB);
71                 if(!mode_info || mode_info->xres != xsz || mode_info->yres != ysz) {
72                         continue;
73                 }
74                 if(SAME_BPP(mode_info->bpp, bpp)) {
75                         best = modes[i];
76                 }
77         }
78
79         if(best) {
80                 mode_info = vbe_get_mode_info(best);
81         } else {
82                 fprintf(stderr, "Requested video mode (%dx%d %dbpp) is unavailable\n", xsz, ysz, bpp);
83                 return 0;
84         }
85
86         if(vbe_set_mode(best | VBE_MODE_LFB) == -1) {
87                 fprintf(stderr, "Failed to set video mode %dx%d %dbpp\n", mode_info->xres, mode_info->yres, mode_info->bpp);
88                 return 0;
89         }
90
91         /* attempt to set 8 bits of color per component in palettized modes */
92         /*if(bpp <= 8) {
93                 pal_bits = vbe_set_palette_bits(8);
94                 printf("palette bits per color primary: %d\n", pal_bits);
95         }
96         */
97
98         printf("avail video pages: %d\n", mode_info->num_img_pages);
99         printf("bytes per scanline: %d (%d pixels)\n", vbe_get_scanlen(VBE_SCANLEN_BYTES),
100                         vbe_get_scanlen(VBE_SCANLEN_PIXELS));
101
102         pgsize = xsz * ysz * (bpp / CHAR_BIT);
103         fbsize = mode_info->num_img_pages * pgsize;
104         vpgaddr[0] = (void*)dpmi_mmap(mode_info->fb_addr, fbsize);
105
106         if(mode_info->num_img_pages > 1) {
107                 vpgaddr[1] = (char*)vpgaddr[0] + pgsize;
108         } else {
109                 vpgaddr[1] = 0;
110         }
111         return vpgaddr[0];
112 }
113
114 int set_text_mode(void)
115 {
116         vbe_set_mode(0x3);
117         return 0;
118 }
119
120 int get_color_depth(void)
121 {
122         if(!mode_info) {
123                 return -1;
124         }
125         return mode_info->bpp;
126 }
127
128 int get_color_bits(int *rbits, int *gbits, int *bbits)
129 {
130         if(!mode_info) {
131                 return -1;
132         }
133         *rbits = mode_info->rmask_size;
134         *gbits = mode_info->gmask_size;
135         *bbits = mode_info->bmask_size;
136         return 0;
137 }
138
139 int get_color_mask(unsigned int *rmask, unsigned int *gmask, unsigned int *bmask)
140 {
141         if(!mode_info) {
142                 return -1;
143         }
144         *rmask = make_mask(mode_info->rmask_size, mode_info->rpos);
145         *gmask = make_mask(mode_info->gmask_size, mode_info->gpos);
146         *bmask = make_mask(mode_info->bmask_size, mode_info->bpos);
147         return 0;
148 }
149
150 int get_color_shift(int *rshift, int *gshift, int *bshift)
151 {
152         if(!mode_info) {
153                 return -1;
154         }
155         *rshift = mode_info->rpos;
156         *gshift = mode_info->gpos;
157         *bshift = mode_info->bpos;
158         return 0;
159 }
160
161 void set_palette(int idx, int r, int g, int b)
162 {
163         int col[3];
164         col[0] = r;
165         col[1] = g;
166         col[2] = b;
167         vbe_set_palette(idx, col, 1, pal_bits);
168 }
169
170 void *page_flip(int vsync)
171 {
172         static int frame;
173         void *nextaddr;
174         int y, when, bufidx;
175
176         if(!vpgaddr[1]) {
177                 /* page flipping not supported */
178                 return 0;
179         }
180
181         bufidx = ++frame & 1;
182
183         y = bufidx ? mode_info->yres : 0;
184         nextaddr = vpgaddr[bufidx];
185
186         when = vsync ? VBE_SET_DISP_START_VBLANK : VBE_SET_DISP_START_NOW;
187         if(vbe_set_disp_start(0, y, when) == -1) {
188                 return 0;
189         }
190
191         if(vsync == FLIP_VBLANK_WAIT) {
192                 wait_vsync();
193         }
194         return nextaddr;
195 }
196
197 static unsigned int make_mask(int sz, int pos)
198 {
199         unsigned int i, mask = 0;
200
201         for(i=0; i<sz; i++) {
202                 mask |= 1 << i;
203         }
204         return mask << pos;
205 }