1 /*
2  * -----------------------------------------------------------------------
3  *
4  *   Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
9  *   Boston MA 02111-1307, USA; either version 2 of the License, or
10  *   (at your option) any later version; incorporated herein by reference.
11  *
12  * -----------------------------------------------------------------------
13  *
14  * -----------------------------------------------------------------------
15  *  VGA splash screen code
16  * -----------------------------------------------------------------------
17  */
18 
19 #include <stddef.h>
20 #include "core.h"
21 #include <sys/io.h>
22 #include <hw/vga.h>
23 #include "fs.h"
24 
25 #include "bios.h"
26 #include "graphics.h"
27 #include <syslinux/video.h>
28 
29 __export uint8_t UsingVGA = 0;
30 uint16_t VGAPos;		/* Pointer into VGA memory */
31 __export uint16_t *VGAFilePtr;	/* Pointer into VGAFileBuf */
32 __export uint16_t VGAFontSize = 16;	/* Defaults to 16 byte font */
33 
34 __export char VGAFileBuf[VGA_FILE_BUF_SIZE];	/* Unmangled VGA image name */
35 __export char VGAFileMBuf[FILENAME_MAX];	/* Mangled VGA image name */
36 
37 static uint8_t VGARowBuffer[640 + 80];	/* Decompression buffer */
38 static uint8_t VGAPlaneBuffer[(640/8) * 4]; /* Plane buffers */
39 
40 extern uint16_t GXPixCols;
41 extern uint16_t GXPixRows;
42 
43 /* Maps colors to consecutive DAC registers */
44 static uint8_t linear_color[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8,
45 				  9, 10, 11, 12, 13, 14, 15, 0 };
46 
47 static FILE *fd;
48 
49 typedef struct {
50 	uint32_t LSSMagic;	/* Magic number */
51 	uint16_t GraphXSize;	/* Width of splash screen file */
52 	uint16_t GraphYSize;	/* Height of splash screen file */
53 	uint8_t GraphColorMap[3*16];
54 } lssheader_t;
55 
56 static lssheader_t LSSHeader;
57 
58 #define LSSMagic	LSSHeader.LSSMagic
59 #define GraphXSize	LSSHeader.GraphXSize
60 #define GraphYSize	LSSHeader.GraphYSize
61 
62 /*
63  * Enable VGA graphics, if possible. Return 0 on success.
64  */
65 static int vgasetmode(void)
66 {
67 	com32sys_t ireg, oreg;
68 
69 	if (UsingVGA)
70 		return 0;		/* Nothing to do... */
71 
72 	memset(&ireg, 0, sizeof(ireg));
73 	memset(&oreg, 0, sizeof(oreg));
74 
75 	if (UsingVGA & 0x4) {
76 		/*
77 		 * We're in VESA mode, which means VGA; use VESA call
78 		 * to revert the mode, and then call the conventional
79 		 * mode-setting for good measure...
80 		 */
81 		ireg.eax.w[0] = 0x4F02;
82 		ireg.ebx.w[0] = 0x0012;
83 		__intcall(0x10, &ireg, &oreg);
84 	} else {
85 		/* Get video card and monitor */
86 		ireg.eax.w[0] = 0x1A00;
87 		__intcall(0x10, &ireg, &oreg);
88 		oreg.ebx.b[0] -= 7; /* BL=07h and BL=08h OK */
89 
90 		if (oreg.ebx.b[0] > 1)
91 			return -1;
92 	}
93 
94 	/*
95 	 * Set mode.
96 	 */
97 	memset(&ireg, 0, sizeof(ireg));
98 	ireg.eax.w[0] = 0x0012;	/* Set mode = 640x480 VGA 16 colors */
99 	__intcall(0x10, &ireg, &oreg);
100 
101 	memset(&ireg, 0, sizeof(ireg));
102 	ireg.edx.w[0] = (uint32_t)linear_color;
103 	ireg.eax.w[0] = 0x1002;	/* Write color registers */
104 	__intcall(0x10, &ireg, &oreg);
105 
106 	UsingVGA = 1;
107 
108 	/* Set GXPixCols and GXPixRows */
109 	GXPixCols = 640;
110 	GXPixRows = 480;
111 
112 	use_font();
113 	ScrollAttribute = 0;
114 
115 	return 0;
116 }
117 
118 static inline char getnybble(void)
119 {
120 	char data = getc(fd);
121 
122 	if (data & 0x10) {
123 		data &= 0x0F;
124 		return data;
125 	}
126 
127 	data = getc(fd);
128 	return (data & 0x0F);
129 }
130 
131 /*
132  * rledecode:
133  *	Decode a pixel row in RLE16 format.
134  *
135  * 'in': input (RLE16 encoded) buffer
136  * 'out': output (decoded) buffer
137  * 'count': pixel count
138  */
139 static void rledecode(uint8_t *out, size_t count)
140 {
141 	uint8_t prev_pixel = 0;
142 	size_t size = count;
143 	uint8_t data;
144 	int i;
145 
146 again:
147 	for (i = 0; i < size; i++) {
148 
149 		data = getnybble();
150 		if (data == prev_pixel)
151 			break;
152 
153 		*out++ = data;
154 		prev_pixel = data;
155 	}
156 
157 	size -= i;
158 	if (!size)
159 		return;
160 
161 	/* Start of run sequence */
162 	data = getnybble();
163 	if (data == 0) {
164 		/* long run */
165 		uint8_t hi;
166 
167 		data = getnybble();
168 		hi = getnybble();
169 		hi <<= 4;
170 		data |= hi;
171 		data += 16;
172 	}
173 
174 	/* dorun */
175 	for (i = 0; i < data; i++)
176 		*out++ = prev_pixel;
177 
178 	size -= i;
179 	if (size)
180 		goto again;
181 }
182 
183 /*
184  * packedpixel2vga:
185  *	Convert packed-pixel to VGA bitplanes
186  *
187  * 'in': packed pixel string (640 pixels)
188  * 'out': output (four planes @ 640/8 = 80 bytes)
189  * 'count': pixel count (multiple of 8)
190  */
191 static void packedpixel2vga(const uint8_t *in, uint8_t *out)
192 {
193 	int i, j, k;
194 
195 	for (i = 0; i < 4; i++) {
196 		const uint8_t *ip = in;
197 
198 		for (j = 0; j < 640/8; j++) {
199 			uint8_t ob = 0;
200 
201 			for (k = 0; k < 8; k++) {
202 				uint8_t px = *ip++;
203 				ob = (ob << 1) | ((px >> i) & 1);
204 			}
205 
206 			*out++ = ob;
207 		}
208 	}
209 }
210 
211 /*
212  * outputvga:
213  *	Output four subsequent lines of VGA data
214  *
215  * 'in': four planes @ 640/8=80 bytes
216  * 'out': pointer into VGA memory
217  */
218 static void outputvga(const void *in, void *out)
219 {
220 	int i;
221 
222 	/* Select the sequencer mask */
223 	outb(VGA_SEQ_IX_MAP_MASK, VGA_SEQ_ADDR);
224 
225 	for (i = 1; i <= 8; i <<= 1) {
226 		/* Select the bit plane to write */
227 		outb(i, VGA_SEQ_DATA);
228 		memcpy(out, in, 640/8);
229 		in = (const char *)in + 640/8;
230 	}
231 }
232 
233 /*
234  * Display a graphical splash screen.
235  */
236 __export void vgadisplayfile(FILE *_fd)
237 {
238 	char *p;
239 	int size;
240 
241 	fd = _fd;
242 
243 	/*
244 	 * This is a cheap and easy way to make sure the screen is
245 	 * cleared in case we were in graphics mode aready.
246 	 */
247 	syslinux_force_text_mode();
248 	vgasetmode();
249 
250 	size = 4+2*2+16*3;
251 	p = (char *)&LSSHeader;
252 
253 	/* Load the header */
254 	while (size--)
255 		*p = getc(fd);
256 
257 	if (*p != EOF) {
258 		com32sys_t ireg, oreg;
259 		uint16_t rows;
260 		int i;
261 
262 		/* The header WILL be in the first chunk. */
263 		if (LSSMagic != 0x1413f33d)
264 			return;
265 
266 		memset(&ireg, 0, sizeof(ireg));
267 
268 		/* Color map offset */
269 		ireg.edx.w[0] = offsetof(lssheader_t, GraphColorMap);
270 
271 		ireg.eax.w[0] = 0x1012;	       /* Set RGB registers */
272 		ireg.ebx.w[0] = 0;	       /* First register number */
273 		ireg.ecx.w[0] = 16;	       /* 16 registers */
274 		__intcall(0x10, &ireg, &oreg);
275 
276 		/* Number of pixel rows */
277 		rows = (GraphYSize + VGAFontSize) - 1;
278 		rows = rows / VGAFontSize;
279 		if (rows >= VidRows)
280 			rows = VidRows - 1;
281 
282 		memset(&ireg, 0, sizeof(ireg));
283 
284 		ireg.edx.b[1] = rows;
285 		ireg.eax.b[1] = 2;
286 		ireg.ebx.w[0] = 0;
287 
288 		/* Set cursor below image */
289 		__intcall(0x10, &ireg, &oreg);
290 
291 		rows = GraphYSize; /* Number of graphics rows */
292 		VGAPos = 0;
293 
294 		for (i = 0; i < rows; i++) {
295 			/* Pre-clear the row buffer */
296 			memset(VGARowBuffer, 0, 640);
297 
298 			/* Decode one row */
299 			rledecode(VGARowBuffer, GraphXSize);
300 
301 			packedpixel2vga(VGARowBuffer, VGAPlaneBuffer);
302 			outputvga(VGAPlaneBuffer, MK_PTR(0xA000, VGAPos));
303 			VGAPos += 640/8;
304 		}
305 	}
306 }
307 
308 /*
309  * Disable VGA graphics.
310  */
311 __export void syslinux_force_text_mode(void)
312 {
313 	com32sys_t ireg, oreg;
314 
315 	/* Already in text mode? */
316 	if (!UsingVGA)
317 		return;
318 
319 	if (UsingVGA & 0x4) {
320 		/* VESA return to normal video mode */
321 		memset(&ireg, 0, sizeof(ireg));
322 
323 		ireg.eax.w[0] = 0x4F02; /* Set SuperVGA video mode */
324 		ireg.ebx.w[0] = 0x0003;
325 		__intcall(0x10, &ireg, &oreg);
326 	}
327 
328 	/* Return to normal video mode */
329 	memset(&ireg, 0, sizeof(ireg));
330 	ireg.eax.w[0] = 0x0003;
331 	__intcall(0x10, &ireg, &oreg);
332 
333 	UsingVGA = 0;
334 
335 	ScrollAttribute = 0x7;
336 	/* Restore text font/data */
337 	use_font();
338 }
339 
340 static void vgacursorcommon(char data)
341 {
342 	if (UsingVGA) {
343 		com32sys_t ireg;
344                 memset(&ireg, 0, sizeof(ireg));
345 
346 		ireg.eax.b[0] = data;
347 		ireg.eax.b[1] = 0x09;
348 		ireg.ebx.w[0] = 0x0007;
349 		ireg.ecx.w[0] = 1;
350 		__intcall(0x10, &ireg, NULL);
351 	}
352 }
353 
354 void vgahidecursor(void)
355 {
356 	vgacursorcommon(' ');
357 }
358 
359 void vgashowcursor(void)
360 {
361 	vgacursorcommon('_');
362 }
363 
364 __export void using_vga(uint8_t vga, uint16_t pix_cols, uint16_t pix_rows)
365 {
366     UsingVGA = vga;
367     GXPixCols = pix_cols;
368     GXPixRows = pix_rows;
369 
370     if (!(UsingVGA & 0x08))
371         adjust_screen();
372 }
373 
374 void pm_using_vga(com32sys_t *regs)
375 {
376     using_vga(regs->eax.b[0], regs->ecx.w[0], regs->edx.w[0]);
377 }
378