backported fixes from 256boss
[bootcensus] / src / video.c
1 /*
2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <stdio.h>
19 #include <string.h>
20 #include "video.h"
21 #include "vbe.h"
22 #include "int86.h"
23
24 #define REALPTR(s, o)   (void*)(((uint32_t)(s) << 4) + (uint32_t)(o))
25 #define VBEPTR(x)               REALPTR(((x) & 0xffff0000) >> 16, (x) & 0xffff)
26 #define VBEPTR_SEG(x)   (((x) & 0xffff0000) >> 16)
27 #define VBEPTR_OFF(x)   ((x) & 0xffff)
28
29
30 #define SAME_BPP(a, b)  \
31         ((a) == (b) || ((a) == 16 && (b) == 15) || ((a) == 15 && (b) == 16) || \
32          ((a) == 32 && (b) == 24) || ((a) == 24 && (b) == 32))
33
34
35 unsigned int color_mask(int nbits, int pos);
36
37 static struct vbe_info *vbe_info;
38 static uint16_t *modes;
39 static int mode_count;
40 static struct vbe_mode_info *mode_info;
41
42 void set_vga_mode(int mode)
43 {
44         struct int86regs regs;
45
46         memset(&regs, 0, sizeof regs);
47         regs.eax = mode;
48         int86(0x10, &regs);
49 }
50
51 static int init_once(void)
52 {
53         int i;
54         static int done_init;
55
56         if(done_init) {
57                 return vbe_info && modes ? 0 : -1;
58         }
59         done_init = 1;
60
61         /* check for VBE2 support and output some info */
62         if(!(vbe_info = vbe_get_info())) {
63                 printf("VESA BIOS Extensions not available\n");
64                 return -1;
65         }
66
67         printf("VBE Version: %x.%x\n", vbe_info->version >> 8, vbe_info->version & 0xff);
68         if(vbe_info->version < 0x200) {
69                 printf("VBE >=2.0 not available. VBE 1.x support not implemented yet.");
70                 return -1;
71         }
72
73         printf("Graphics adapter: %s, %s (%s)\n", (char*)VBEPTR(vbe_info->oem_vendor_name_ptr),
74                         (char*)VBEPTR(vbe_info->oem_product_name_ptr), (char*)VBEPTR(vbe_info->oem_product_rev_ptr));
75         printf("Video memory: %dkb\n", vbe_info->total_mem << 6);
76
77         modes = VBEPTR(vbe_info->vid_mode_ptr);
78
79         mode_count = 0;
80         for(i=0; i<1024; i++) { /* impose an upper limit to avoid inf-loops */
81                 if(modes[i] == 0xffff) {
82                         break;  /* reached the end */
83                 }
84                 mode_count++;
85         }
86         return 0;
87 }
88
89 void *set_video_mode(int mode)
90 {
91         if(init_once() == -1) return 0;
92         if(mode < 0) return 0;
93
94         if(!(mode_info = vbe_get_mode_info(mode))) {
95                 return 0;
96         }
97
98         if(vbe_set_mode(mode | VBE_MODE_LFB) == -1) {
99                 printf("Failed to set video mode %dx%d %dbpp\n", mode_info->xres, mode_info->yres, mode_info->bpp);
100                 return 0;
101         }
102
103         return (void*)mode_info->fb_addr;
104 }
105
106 int find_video_mode_idx(int xsz, int ysz, int bpp)
107 {
108         int i, best = -1, best_bpp = 0;
109         struct vbe_mode_info *inf;
110
111         if(init_once() == -1) return -1;
112
113         for(i=0; i<mode_count; i++) {
114                 inf = vbe_get_mode_info(modes[i] | VBE_MODE_LFB);
115                 if(!inf || inf->xres != xsz || inf->yres != ysz) {
116                         continue;
117                 }
118                 if((bpp <= 0 && inf->bpp > best_bpp) || SAME_BPP(inf->bpp, bpp)) {
119                         best = i;
120                         best_bpp = inf->bpp;
121                 }
122         }
123
124         if(best == -1) {
125                 printf("Requested video mode (%dx%d %dbpp) is unavailable\n", xsz, ysz, bpp);
126                 return -1;
127         }
128         return best;
129 }
130
131 int video_mode_count(void)
132 {
133         if(init_once() == -1) return 0;
134         return mode_count;
135 }
136
137 int video_mode_info(int n, struct video_mode *vid)
138 {
139         struct vbe_mode_info *inf;
140
141         if(init_once() == -1) return -1;
142
143         if(!(inf = vbe_get_mode_info(modes[n] | VBE_MODE_LFB))) {
144                 return -1;
145         }
146         vid->mode = modes[n];
147         vid->width = inf->xres;
148         vid->height = inf->yres;
149         vid->bpp = inf->bpp;
150         vid->rbits = inf->rmask_size;
151         vid->gbits = inf->gmask_size;
152         vid->bbits = inf->bmask_size;
153         vid->rshift = inf->rpos;
154         vid->gshift = inf->gpos;
155         vid->bshift = inf->bpos;
156         vid->rmask = color_mask(inf->rmask_size, inf->rpos);
157         vid->gmask = color_mask(inf->gmask_size, inf->gpos);
158         vid->bmask = color_mask(inf->bmask_size, inf->bpos);
159         return 0;
160 }
161
162 int get_color_bits(int *rbits, int *gbits, int *bbits)
163 {
164         if(!mode_info) {
165                 return -1;
166         }
167         *rbits = mode_info->rmask_size;
168         *gbits = mode_info->gmask_size;
169         *bbits = mode_info->bmask_size;
170         return 0;
171 }
172
173 int get_color_mask(unsigned int *rmask, unsigned int *gmask, unsigned int *bmask)
174 {
175         if(!mode_info) {
176                 return -1;
177         }
178         *rmask = color_mask(mode_info->rmask_size, mode_info->rpos);
179         *gmask = color_mask(mode_info->gmask_size, mode_info->gpos);
180         *bmask = color_mask(mode_info->bmask_size, mode_info->bpos);
181         return 0;
182 }
183
184 int get_color_shift(int *rshift, int *gshift, int *bshift)
185 {
186         if(!mode_info) {
187                 return -1;
188         }
189         *rshift = mode_info->rpos;
190         *gshift = mode_info->gpos;
191         *bshift = mode_info->bpos;
192         return 0;
193 }
194
195 unsigned int color_mask(int nbits, int pos)
196 {
197         static unsigned int maskbits[] = {0, 1, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0xff};
198         return maskbits[nbits] << pos;
199 }
200
201 const char *get_video_vendor(void)
202 {
203         if(init_once() == -1) return 0;
204         return (char*)VBEPTR(vbe_info->oem_vendor_name_ptr);
205 }
206
207 int get_video_mem_size(void)
208 {
209         if(init_once() == -1) return 0;
210         return vbe_info->total_mem << 6;
211 }