initial hasty attempt at porting over my VBE code
[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
36 static struct vbe_info *vbe_info;
37 static struct vbe_mode_info *mode_info;
38
39 void set_vga_mode(int mode)
40 {
41         struct int86regs regs;
42
43         memset(&regs, 0, sizeof regs);
44         regs.eax = mode;
45         int86(0x10, &regs);
46 }
47
48 void *set_video_mode(int xsz, int ysz, int bpp)
49 {
50         int i;
51         static uint16_t *modes;
52         uint16_t best = 0;
53
54         /* check for VBE2 support and output some info */
55         if(!vbe_info) {
56                 if(!(vbe_info = vbe_get_info())) {
57                         printf("VESA BIOS Extensions not available\n");
58                         return 0;
59                 }
60
61                 printf("VBE Version: %x.%x\n", vbe_info->version >> 8, vbe_info->version & 0xff);
62                 if(vbe_info->version < 0x200) {
63                         printf("VBE >=2.0 not available. VBE 1.x support not implemented yet.");
64                         return 0;
65                 }
66
67                 printf("Graphics adapter: %s, %s (%s)\n", (char*)VBEPTR(vbe_info->oem_vendor_name_ptr),
68                                 (char*)VBEPTR(vbe_info->oem_product_name_ptr), (char*)VBEPTR(vbe_info->oem_product_rev_ptr));
69                 printf("Video memory: %dkb\n", vbe_info->total_mem << 6);
70
71                 modes = VBEPTR(vbe_info->vid_mode_ptr);
72         }
73
74         for(i=0; i<1024; i++) { /* impose an upper limit to avoid inf-loops */
75                 if(modes[i] == 0xffff) {
76                         break;  /* reached the end */
77                 }
78
79                 mode_info = vbe_get_mode_info(modes[i] | VBE_MODE_LFB);
80                 if(!mode_info || mode_info->xres != xsz || mode_info->yres != ysz) {
81                         continue;
82                 }
83                 if(SAME_BPP(mode_info->bpp, bpp)) {
84                         best = modes[i];
85                 }
86         }
87
88         if(best) {
89                 mode_info = vbe_get_mode_info(best);
90         } else {
91                 printf("Requested video mode (%dx%d %dbpp) is unavailable\n", xsz, ysz, bpp);
92                 return 0;
93         }
94
95         if(vbe_set_mode(best | VBE_MODE_LFB) == -1) {
96                 printf("Failed to set video mode %dx%d %dbpp\n", mode_info->xres, mode_info->yres, mode_info->bpp);
97                 return 0;
98         }
99
100         return (void*)mode_info->fb_addr;
101 }
102
103 int get_color_bits(int *rbits, int *gbits, int *bbits)
104 {
105         if(!mode_info) {
106                 return -1;
107         }
108         *rbits = mode_info->rmask_size;
109         *gbits = mode_info->gmask_size;
110         *bbits = mode_info->bmask_size;
111         return 0;
112 }
113
114 int get_color_mask(unsigned int *rmask, unsigned int *gmask, unsigned int *bmask)
115 {
116         static unsigned int maskbits[] = {0, 1, 3, 7, 0xf, 0x1f, 0x3f, 0x7f, 0xff};
117         if(!mode_info) {
118                 return -1;
119         }
120         *rmask = maskbits[mode_info->rmask_size] << mode_info->rpos;
121         *gmask = maskbits[mode_info->gmask_size] << mode_info->gpos;
122         *bmask = maskbits[mode_info->bmask_size] << mode_info->bpos;
123         return 0;
124 }
125
126 int get_color_shift(int *rshift, int *gshift, int *bshift)
127 {
128         if(!mode_info) {
129                 return -1;
130         }
131         *rshift = mode_info->rpos;
132         *gshift = mode_info->gpos;
133         *bshift = mode_info->bpos;
134         return 0;
135 }
136