Home | History | Annotate | Download | only in core
      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