c588742e2624d26f79834a07e531cf7796fc4574
[eradicate] / src / dos / gfx.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "game.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 int match_video_mode(int xsz, int ysz, int bpp)
125 {
126         int i, best = -1;
127         struct video_mode *vm;
128
129         for(i=0; i<num_vmodes; i++) {
130                 vm = vmodes + i;
131                 if(vm->xsz != xsz || vm->ysz != ysz) continue;
132                 if(SAME_BPP(vm->bpp, bpp)) {
133                         best = i;
134                 }
135                 if(vm->bpp == bpp) break;
136         }
137
138         if(best == -1) {
139                 fprintf(stderr, "failed to find video mode %dx%d %d bpp)\n", xsz, ysz, bpp);
140                 return -1;
141         }
142         return best;
143 }
144
145 int find_video_mode(int mode)
146 {
147         int i;
148         struct video_mode *vm;
149
150         vm = vmodes;
151         for(i=0; i<num_vmodes; i++) {
152                 if(vm->mode == mode) return i;
153         }
154         return -1;
155 }
156
157 void *set_video_mode(int idx, int nbuf)
158 {
159         unsigned int mode;
160         struct video_mode *vm = vmodes + idx;
161
162         printf("setting video mode %x (%dx%d %d bpp)\n", (unsigned int)vm->mode,
163                         vm->xsz, vm->ysz, vm->bpp);
164         fflush(stdout);
165
166         mode = vm->mode | VBE_MODE_LFB;
167         if(vbe_setmode(mode) == -1) {
168                 mode = vm->mode;
169                 if(vbe_setmode(mode) == -1) {
170                         fprintf(stderr, "failed to set video mode %x\n", (unsigned int)vm->mode);
171                         return 0;
172                 }
173                 printf("Warning: failed to get a linear framebuffer. falling back to banked mode\n");
174         }
175
176         curmode = vm;
177         if(nbuf < 1) nbuf = 1;
178         if(nbuf > 2) nbuf = 2;
179         pgcount = nbuf > vm->max_pages ? vm->max_pages : nbuf;
180         pgsize = vm->ysz * vm->pitch;
181         fbsize = pgcount * pgsize;
182
183         printf("pgcount: %d, pgsize: %d, fbsize: %d\n", pgcount, pgsize, fbsize);
184         printf("phys addr: %p\n", (void*)vm->fb_addr);
185         fflush(stdout);
186
187         if(vm->fb_addr) {
188                 vpgaddr[0] = (void*)dpmi_mmap(vm->fb_addr, fbsize);
189                 if(!vpgaddr[0]) {
190                         fprintf(stderr, "failed to map framebuffer (phys: %lx, size: %d)\n",
191                                         (unsigned long)vm->fb_addr, fbsize);
192                         set_text_mode();
193                         return 0;
194                 }
195                 memset(vpgaddr[0], 0xaa, pgsize);
196
197                 if(pgcount > 1) {
198                         vpgaddr[1] = (char*)vpgaddr[0] + pgsize;
199                         backidx = 1;
200                         page_flip(FLIP_NOW);    /* start with the second page visible */
201                 } else {
202                         frontidx = backidx = 0;
203                         vpgaddr[1] = 0;
204                 }
205
206                 blit_frame = blit_frame_lfb;
207
208         } else {
209                 vpgaddr[0] = (void*)0xa0000;
210                 vpgaddr[1] = 0;
211
212                 blit_frame = blit_frame_banked;
213         }
214         return vpgaddr[0];
215 }
216
217 int set_text_mode(void)
218 {
219         vga_setmode(3);
220         curmode = 0;
221         return 0;
222 }
223
224 void *page_flip(int vsync)
225 {
226         if(!vpgaddr[1]) {
227                 /* page flipping not supported */
228                 return vpgaddr[0];
229         }
230
231         vbe_swap(backidx ? pgsize : 0, vsync ? VBE_SWAP_VBLANK : VBE_SWAP_NOW);
232         frontidx = backidx;
233         backidx = (backidx + 1) & 1;
234
235         return vpgaddr[backidx];
236 }
237
238
239 static void blit_frame_lfb(void *pixels, int vsync)
240 {
241         dbg_fps(pixels);
242         if(vsync) wait_vsync();
243         memcpy64(vpgaddr[frontidx], pixels, pgsize >> 3);
244 }
245
246 static void blit_frame_banked(void *pixels, int vsync)
247 {
248         int i, sz, offs;
249         unsigned int pending;
250         unsigned char *pptr = pixels;
251
252         dbg_fps(pixels);
253
254         if(vsync) wait_vsync();
255
256         /* assume initial window offset at 0 */
257         offs = 0;
258         pending = pgsize;
259         while(pending > 0) {
260                 sz = pending > curmode->bank_size ? curmode->bank_size : pending;
261                 memcpy64((void*)0xa0000, pptr, sz >> 3);
262                 pptr += sz;
263                 pending -= sz;
264                 vbe_setwin(0, ++offs);
265         }
266
267         vbe_setwin(0, 0);
268 }
269
270 static uint32_t calc_mask(int sz, int pos)
271 {
272         int i;
273         uint32_t mask = 0;
274         while(sz-- > 0) {
275                 mask = (mask << 1) | 1;
276         }
277         return mask << pos;
278 }