backporting...
[dosdemo] / src / dos / gfx.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "demo.h"
5 #include "cdpmi.h"
6 #include "gfx.h"
7 #include "vbe.h"
8 #include "vga.h"
9 #include "util.h"
10
11 #define SAME_BPP(a, b)  \
12         ((a) == (b) || ((a) == 16 && (b) == 15) || ((a) == 15 && (b) == 16) || \
13          ((a) == 32 && (b) == 24) || ((a) == 24 && (b) == 32))
14
15 void (*blit_frame)(void*, int);
16
17 static void blit_frame_lfb(void *pixels, int vsync);
18 static void blit_frame_banked(void *pixels, int vsync);
19 static uint32_t calc_mask(int sz, int pos);
20
21 static struct video_mode *vmodes;
22 static int num_vmodes;
23
24 static int vbe_init_ver;
25 static struct vbe_info vbe;
26
27 /* current mode */
28 static struct video_mode *curmode;
29 static void *vpgaddr[2];
30 static int frontidx, backidx;
31 static int pgcount, pgsize, fbsize;
32
33
34 int init_video(void)
35 {
36         int i, num, max_modes;
37         struct video_mode *vmptr;
38
39         if(vbe_info(&vbe) == -1) {
40                 fprintf(stderr, "failed to retrieve VBE information\n");
41                 return -1;
42         }
43         vbe_print_info(stdout, &vbe);
44
45         num_vmodes = 0;
46         max_modes = 64;
47         if(!(vmodes = malloc(max_modes * sizeof *vmodes))) {
48                 fprintf(stderr, "failed to allocate video modes list\n");
49                 return -1;
50         }
51
52         num = vbe_num_modes(&vbe);
53         for(i=0; i<num; i++) {
54                 struct vbe_mode_info minf;
55
56                 if(vbe_mode_info(vbe.modes[i], &minf) == -1) {
57                         continue;
58                 }
59
60                 if(num_vmodes >= max_modes) {
61                         int newmax = max_modes ? (max_modes << 1) : 16;
62                         if(!(vmptr = realloc(vmodes, newmax * sizeof *vmodes))) {
63                                 fprintf(stderr, "failed to grow video mode list (%d)\n", newmax);
64                                 free(vmodes);
65                                 return -1;
66                         }
67                         vmodes = vmptr;
68                         max_modes = newmax;
69                 }
70
71                 vmptr = vmodes + num_vmodes++;
72                 memset(vmptr, 0, sizeof *vmptr);
73                 vmptr->mode = vbe.modes[i];
74                 vmptr->xsz = minf.xres;
75                 vmptr->ysz = minf.yres;
76                 vmptr->bpp = minf.bpp;
77                 vmptr->pitch = minf.scanline_bytes;
78                 if(minf.mem_model == VBE_TYPE_DIRECT) {
79                         vmptr->rbits = minf.rsize;
80                         vmptr->gbits = minf.gsize;
81                         vmptr->bbits = minf.bsize;
82                         vmptr->rshift = minf.rpos;
83                         vmptr->gshift = minf.gpos;
84                         vmptr->bshift = minf.bpos;
85                         vmptr->rmask = calc_mask(minf.rsize, minf.rpos);
86                         vmptr->gmask = calc_mask(minf.gsize, minf.gpos);
87                         vmptr->bmask = calc_mask(minf.bsize, minf.bpos);
88                         vmptr->bpp = vmptr->rbits + vmptr->gbits + vmptr->bbits;
89                 }
90                 if(minf.attr & VBE_ATTR_LFB) {
91                         vmptr->fb_addr = minf.fb_addr;
92                 } else {
93                         vmptr->bank_size = (uint32_t)minf.bank_size * 1024;
94                         if(!vmptr->bank_size) {
95                                 vmptr->bank_size = 65536;
96                         }
97                 }
98                 vmptr->max_pages = minf.num_img_pages;
99
100                 printf("%04x: ", vbe.modes[i]);
101                 vbe_print_mode_info(stdout, &minf);
102         }
103         fflush(stdout);
104
105         vbe_init_ver = VBE_VER_MAJOR(vbe.ver);
106         return 0;
107 }
108
109 void cleanup_video(void)
110 {
111         free(vmodes);
112 }
113
114 struct video_mode *video_modes(void)
115 {
116         return vmodes;
117 }
118
119 int num_video_modes(void)
120 {
121         return num_vmodes;
122 }
123
124 struct video_mode *get_video_mode(int idx)
125 {
126         if(idx == VMODE_CURRENT) {
127                 return curmode;
128         }
129         return vmodes + idx;
130 }
131
132 int match_video_mode(int xsz, int ysz, int bpp)
133 {
134         int i, best = -1;
135         struct video_mode *vm;
136
137         for(i=0; i<num_vmodes; i++) {
138                 vm = vmodes + i;
139                 if(vm->xsz != xsz || vm->ysz != ysz) continue;
140                 if(SAME_BPP(vm->bpp, bpp)) {
141                         best = i;
142                 }
143                 if(vm->bpp == bpp) break;
144         }
145
146         if(best == -1) {
147                 fprintf(stderr, "failed to find video mode %dx%d %d bpp)\n", xsz, ysz, bpp);
148                 return -1;
149         }
150         return best;
151 }
152
153 int find_video_mode(int mode)
154 {
155         int i;
156         struct video_mode *vm;
157
158         vm = vmodes;
159         for(i=0; i<num_vmodes; i++) {
160                 if(vm->mode == mode) return i;
161         }
162         return -1;
163 }
164
165 void *set_video_mode(int idx, int nbuf)
166 {
167         unsigned int mode;
168         struct video_mode *vm = vmodes + idx;
169
170         if(curmode == vm) return vpgaddr[0];
171
172         printf("setting video mode %x (%dx%d %d bpp)\n", (unsigned int)vm->mode,
173                         vm->xsz, vm->ysz, vm->bpp);
174         fflush(stdout);
175
176         mode = vm->mode | VBE_MODE_LFB;
177         if(vbe_setmode(mode) == -1) {
178                 mode = vm->mode;
179                 if(vbe_setmode(mode) == -1) {
180                         fprintf(stderr, "failed to set video mode %x\n", (unsigned int)vm->mode);
181                         return 0;
182                 }
183                 printf("Warning: failed to get a linear framebuffer. falling back to banked mode\n");
184         }
185
186         /* unmap previous video memory mapping, if there was one (switching modes) */
187         if(vpgaddr[0] && vpgaddr[0] != (void*)0xa0000) {
188                 dpmi_munmap(vpgaddr[0]);
189                 vpgaddr[0] = vpgaddr[1] = 0;
190         }
191
192         curmode = vm;
193         if(nbuf < 1) nbuf = 1;
194         if(nbuf > 2) nbuf = 2;
195         pgcount = nbuf > vm->max_pages ? vm->max_pages : nbuf;
196         pgsize = vm->ysz * vm->pitch;
197         fbsize = pgcount * pgsize;
198
199         printf("pgcount: %d, pgsize: %d, fbsize: %d\n", pgcount, pgsize, fbsize);
200         printf("phys addr: %p\n", (void*)vm->fb_addr);
201         fflush(stdout);
202
203         if(vm->fb_addr) {
204                 vpgaddr[0] = (void*)dpmi_mmap(vm->fb_addr, fbsize);
205                 if(!vpgaddr[0]) {
206                         fprintf(stderr, "failed to map framebuffer (phys: %lx, size: %d)\n",
207                                         (unsigned long)vm->fb_addr, fbsize);
208                         set_text_mode();
209                         return 0;
210                 }
211                 memset(vpgaddr[0], 0xaa, pgsize);
212
213                 if(pgcount > 1) {
214                         vpgaddr[1] = (char*)vpgaddr[0] + pgsize;
215                         backidx = 1;
216                         page_flip(FLIP_NOW);    /* start with the second page visible */
217                 } else {
218                         frontidx = backidx = 0;
219                         vpgaddr[1] = 0;
220                 }
221
222                 blit_frame = blit_frame_lfb;
223
224         } else {
225                 vpgaddr[0] = (void*)0xa0000;
226                 vpgaddr[1] = 0;
227
228                 blit_frame = blit_frame_banked;
229         }
230
231         /* allocate main memory framebuffer */
232         if(demo_resizefb(vm->xsz, vm->ysz, vm->bpp) == -1) {
233                 fprintf(stderr, "failed to allocate %dx%d (%d bpp) framebuffer\n", vm->xsz,
234                                 vm->ysz, vm->bpp);
235                 set_text_mode();
236                 return 0;
237         }
238
239         return vpgaddr[0];
240 }
241
242 int set_text_mode(void)
243 {
244         /* unmap previous video memory mapping, if there was one (switching modes) */
245         if(vpgaddr[0] && vpgaddr[0] != (void*)0xa0000) {
246                 dpmi_munmap(vpgaddr[0]);
247                 vpgaddr[0] = vpgaddr[1] = 0;
248         }
249
250         vga_setmode(3);
251         curmode = 0;
252         return 0;
253 }
254
255 void *page_flip(int vsync)
256 {
257         if(!vpgaddr[1]) {
258                 /* page flipping not supported */
259                 return vpgaddr[0];
260         }
261
262         vbe_swap(backidx ? pgsize : 0, vsync ? VBE_SWAP_VBLANK : VBE_SWAP_NOW);
263         frontidx = backidx;
264         backidx = (backidx + 1) & 1;
265
266         return vpgaddr[backidx];
267 }
268
269
270 static void blit_frame_lfb(void *pixels, int vsync)
271 {
272         demo_post_draw(pixels);
273
274         if(vsync) wait_vsync();
275         memcpy64(vpgaddr[frontidx], pixels, pgsize >> 3);
276 }
277
278 static void blit_frame_banked(void *pixels, int vsync)
279 {
280         int i, sz, offs;
281         unsigned int pending;
282         unsigned char *pptr = pixels;
283
284         demo_post_draw(pixels);
285
286         if(vsync) wait_vsync();
287
288         /* assume initial window offset at 0 */
289         offs = 0;
290         pending = pgsize;
291         while(pending > 0) {
292                 sz = pending > curmode->bank_size ? curmode->bank_size : pending;
293                 //memcpy64((void*)0xa0000, pptr, sz >> 3);
294                 memcpy((void*)0xa0000, pptr, sz);
295                 pptr += sz;
296                 pending -= sz;
297                 vbe_setwin(0, ++offs);
298         }
299
300         vbe_setwin(0, 0);
301 }
302
303 static uint32_t calc_mask(int sz, int pos)
304 {
305         int i;
306         uint32_t mask = 0;
307         while(sz-- > 0) {
308                 mask = (mask << 1) | 1;
309         }
310         return mask << pos;
311 }