invalidations and dirty bits, trying to minimize screen updates
[retroray] / src / dos / vidsys.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <limits.h>
4 #include <assert.h>
5 #include "vidsys.h"
6 #include "vga.h"
7 #include "drv.h"
8 #include "cdpmi.h"
9 #include "logger.h"
10
11 struct vid_driver *vid_drvlist[MAX_DRV];
12 int vid_numdrv;
13
14 void *vid_vmem;
15 int vid_vmem_size;
16
17 static struct vid_modeinfo **modes, *cur_mode;
18 static int num_modes, max_modes;
19
20
21 int vid_init(void)
22 {
23         int i, j, len;
24         struct vid_modeinfo *vm;
25
26         vid_numdrv = 0;
27         free(modes);
28         modes = 0;
29         cur_mode = 0;
30         num_modes = max_modes = 0;
31
32         vid_vmem = 0;
33         vid_vmem_size = 0;
34
35         if(dpmi_init() == -1) {
36                 return -1;
37         }
38
39         vid_register_vga();
40         vid_register_vbe();
41         vid_register_s3();
42
43         for(i=0; i<vid_numdrv; i++) {
44                 struct vid_driver *drv = vid_drvlist[i];
45
46                 drv->ops->init();
47
48                 for(j=0; j<drv->num_modes; j++) {
49                         if(num_modes >= max_modes) {
50                                 int newsz = max_modes ? max_modes * 2 : 128;
51                                 void *tmp = realloc(modes, newsz * sizeof *modes);
52                                 if(!tmp) {
53                                         errormsg("failed to allocate modes list\n");
54                                         return -1;
55                                 }
56                                 modes = tmp;
57                                 max_modes = newsz;
58                         }
59
60                         modes[num_modes++] = drv->modes + j;
61                 }
62         }
63
64         infomsg("found %d modes:\n", num_modes);
65         for(i=0; i<num_modes; i+=2) {
66                 vm = modes[i];
67                 len = infomsg("[%4s] %04x: %dx%d %dbpp", vm->drv->name, vm->modeno,
68                                 vm->width, vm->height, vm->bpp);
69                 if(i + 1 >= num_modes) {
70                         infomsg("\n");
71                         break;
72                 }
73                 for(j=len; j<40; j++) infomsg(" ");
74                 vm = modes[i + 1];
75                 infomsg("[%4s] %04x: %dx%d %dbpp\n", vm->drv->name, vm->modeno,
76                                 vm->width, vm->height, vm->bpp);
77         }
78
79         return 0;
80 }
81
82 void vid_cleanup(void)
83 {
84         int i;
85
86         if(cur_mode && cur_mode->modeno != 3) {
87                 vid_setmode(3);
88         }
89
90         if(vid_vmem >= (void*)0x100000) {
91                 dpmi_munmap(vid_vmem);
92         }
93
94         for(i=0; i<vid_numdrv; i++) {
95                 struct vid_driver *drv = vid_drvlist[i];
96                 drv->ops->cleanup();
97         }
98
99         free(modes);
100         dpmi_cleanup();
101 }
102
103
104 int vid_curmode(void)
105 {
106         if(cur_mode) {
107                 return cur_mode->modeno;
108         }
109         return -1;
110 }
111
112 void *vid_setmode(int mode)
113 {
114         int i;
115         struct vid_driver *drv;
116
117         for(i=0; i<num_modes; i++) {
118                 if(modes[i]->modeno == mode) {
119                         drv = modes[i]->drv;
120                         if(drv->ops->setmode(mode) == 0) {
121                                 cur_mode = modes[i];
122
123                                 if(vid_vmem >= (void*)0x100000) {
124                                         assert(vid_vmem_size);
125                                         dpmi_munmap(vid_vmem);
126                                 }
127
128                                 if(modes[i]->vmem_addr < 0x100000) {
129                                         vid_vmem = (void*)modes[i]->vmem_addr;
130                                         vid_vmem_size = 0;
131                                 } else {
132                                         vid_vmem = dpmi_mmap(modes[i]->vmem_addr, modes[i]->vmem_size);
133                                         vid_vmem_size = modes[i]->vmem_size;
134                                 }
135                                 return vid_vmem;
136                         }
137                 }
138         }
139         return 0;
140 }
141
142 #define EQUIV_BPP(a, b) \
143         ((a) == (b) || ((a) == 16 && (b) == 15) || ((a) == 15 && (b) == 16) || \
144          ((a) == 24 && (b) == 32) || ((a) == 32 && (b) == 24))
145
146 int vid_findmode(int xsz, int ysz, int bpp)
147 {
148         int i;
149
150         for(i=0; i<num_modes; i++) {
151                 if(modes[i]->width == xsz && modes[i]->height == ysz && modes[i]->bpp == bpp) {
152                         return modes[i]->modeno;
153                 }
154         }
155
156         /* try fuzzy bpp matching */
157         for(i=0; i<num_modes; i++) {
158                 if(modes[i]->width == xsz && modes[i]->height == ysz &&
159                                 EQUIV_BPP(modes[i]->bpp, bpp)) {
160                         return modes[i]->modeno;
161                 }
162         }
163
164         return -1;
165 }
166
167
168 struct vid_modeinfo *vid_modeinfo(int mode)
169 {
170         int i;
171
172         for(i=0; i<num_modes; i++) {
173                 if(modes[i]->modeno == mode) {
174                         return modes[i];
175                 }
176         }
177         return 0;
178 }
179
180 int vid_islinear(void)
181 {
182         return !vid_isbanked();
183 }
184
185 int vid_isbanked(void)
186 {
187         return cur_mode->win_size && vid_vmem < (void*)0x100000;
188 }
189
190 void vid_setpal(int idx, int count, const struct vid_color *col)
191 {
192         cur_mode->ops.setpal(idx, count, col);
193 }
194
195 void vid_getpal(int idx, int count, struct vid_color *col)
196 {
197         cur_mode->ops.getpal(idx, count, col);
198 }
199
200 void vid_blit(int x, int y, int w, int h, void *src, int pitch)
201 {
202         if(pitch <= 0) {
203                 pitch = cur_mode->width << 2;
204         }
205         cur_mode->ops.blit(x, y, w, h, src, pitch);
206 }
207
208 void vid_blitfb(void *fb, int pitch)
209 {
210         if(pitch <= 0) {
211                 pitch = cur_mode->width << 2;
212         }
213         cur_mode->ops.blitfb(fb, pitch);
214 }
215
216 void vid_blit32(int x, int y, int w, int h, uint32_t *src, int pitch)
217 {
218         if(cur_mode->bpp == 32) {
219                 vid_blit(x, y, w, h, src, pitch);
220                 return;
221         }
222
223         if(pitch <= 0) {
224                 pitch = cur_mode->width << 2;
225         }
226         /* XXX */
227 }
228
229 void vid_blitfb32(uint32_t *src, int pitch)
230 {
231         int i, j, winpos, winleft, endskip;
232         unsigned char *dest;
233         uint16_t *dest16;
234
235         if(cur_mode->bpp == 32) {
236                 vid_blitfb(src, pitch);
237                 return;
238         }
239
240         if(pitch <= 0) {
241                 pitch = cur_mode->width << 2;
242         }
243
244         if(vid_islinear()) {
245                 winleft = INT_MAX;
246         } else {
247                 winleft = cur_mode->win_size << 10;
248                 winpos = 0;
249                 vid_setwin(0, 0);
250         }
251
252         switch(cur_mode->bpp) {
253         case 8:
254                 /* TODO */
255                 break;
256
257         case 15:
258                 /* TODO */
259                 break;
260         case 16:
261                 /* TODO */
262                 break;
263
264         case 24:
265                 dest = vid_vmem;
266                 endskip = cur_mode->pitch - cur_mode->width * 3;
267
268                 for(i=0; i<cur_mode->height; i++) {
269                         for(j=0; j<cur_mode->width; j++) {
270                                 uint32_t pixel = src[j];
271                                 if(winleft <= 0) {
272                                         winpos += cur_mode->win_step;
273                                         vid_setwin(0, winpos);
274                                         winleft = cur_mode->win_size << 10;
275                                         dest = vid_vmem;
276                                 }
277                                 dest[0] = pixel & 0xff;
278                                 dest[1] = (pixel >> 8) & 0xff;
279                                 dest[2] = (pixel >> 16) & 0xff;
280                                 dest += 3;
281                                 winleft -= 3;
282                         }
283                         src = (uint32_t*)((char*)src + pitch);
284                         dest += endskip;
285                         winleft -= endskip;
286                 }
287                 break;
288
289         default:
290                 break;
291         }
292 }