xref: /haiku/src/system/boot/platform/bios_ia32/video.cpp (revision c9060eb991e10e477ece52478d6743fc7691c143)
1 /*
2  * Copyright 2004-2008, Axel Dörfler, axeld@pinc-software.de.
3  * Copyright 2008, Stephan Aßmus <superstippi@gmx.de>
4  * Copyright 2008, Philippe Saint-Pierre <stpere@gmail.com>
5  * Distributed under the terms of the MIT License.
6  */
7 
8 
9 #include "video.h"
10 #include "bios.h"
11 #include "vesa.h"
12 #include "vesa_info.h"
13 #include "vga.h"
14 #include "mmu.h"
15 
16 #include <edid.h>
17 
18 #include <arch/cpu.h>
19 #include <boot/stage2.h>
20 #include <boot/platform.h>
21 #include <boot/menu.h>
22 #include <boot/kernel_args.h>
23 #include <boot/images.h>
24 #include <util/list.h>
25 #include <drivers/driver_settings.h>
26 
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 
32 #define TRACE_VIDEO
33 #ifdef TRACE_VIDEO
34 #	define TRACE(x) dprintf x
35 #else
36 #	define TRACE(x) ;
37 #endif
38 
39 
40 struct video_mode {
41 	list_link	link;
42 	uint16		mode;
43 	uint16		width, height, bits_per_pixel;
44 	uint32		bytes_per_row;
45 };
46 
47 static vbe_info_block sInfo;
48 static video_mode *sMode, *sDefaultMode;
49 static bool sVesaCompatible;
50 static struct list sModeList;
51 static uint32 sModeCount;
52 static addr_t sFrameBuffer;
53 static bool sModeChosen;
54 static bool sSettingsLoaded;
55 
56 
57 static int
58 compare_video_modes(video_mode *a, video_mode *b)
59 {
60 	int compare = a->width - b->width;
61 	if (compare != 0)
62 		return compare;
63 
64 	compare = a->height - b->height;
65 	if (compare != 0)
66 		return compare;
67 
68 	// TODO: compare video_mode::mode?
69 	return a->bits_per_pixel - b->bits_per_pixel;
70 }
71 
72 
73 /*!	Insert the video mode into the list, sorted by resolution and bit depth.
74 	Higher resolutions/depths come first.
75 */
76 static void
77 add_video_mode(video_mode *videoMode)
78 {
79 	video_mode *mode = NULL;
80 	while ((mode = (video_mode *)list_get_next_item(&sModeList, mode))
81 			!= NULL) {
82 		int compare = compare_video_modes(videoMode, mode);
83 		if (compare == 0) {
84 			// mode already exists
85 			return;
86 		}
87 
88 		if (compare > 0)
89 			break;
90 	}
91 
92 	list_insert_item_before(&sModeList, mode, videoMode);
93 	sModeCount++;
94 }
95 
96 
97 /*! \brief Finds a video mode with the given resolution.
98 	If \a allowPalette is true, 8-bit modes are considered, too.
99 	If \a height is \c -1, the height is ignored, and only the width
100 	matters.
101 */
102 static video_mode *
103 find_video_mode(int32 width, int32 height, bool allowPalette)
104 {
105 	video_mode *found = NULL;
106 	video_mode *mode = NULL;
107 	while ((mode = (video_mode *)list_get_next_item(&sModeList, mode))
108 			!= NULL) {
109 		if (mode->width == width && (height == -1 || mode->height == height)
110 			&& (mode->bits_per_pixel > 8 || allowPalette)) {
111 			if (found == NULL || found->bits_per_pixel < mode->bits_per_pixel)
112 				found = mode;
113 		}
114 	}
115 
116 	return found;
117 }
118 
119 
120 static video_mode *
121 closest_video_mode(int32 width, int32 height, int32 depth)
122 {
123 	video_mode *bestMode = NULL;
124 	uint32 bestDiff = 0;
125 
126 	video_mode *mode = NULL;
127 	while ((mode = (video_mode *)list_get_next_item(&sModeList, mode))
128 			!= NULL) {
129 		uint32 diff = 2 * abs(mode->width - width) + abs(mode->height - height)
130 			+ abs(mode->bits_per_pixel - depth);
131 
132 		if (bestMode == NULL || bestDiff > diff) {
133 			bestMode = mode;
134 			bestDiff = diff;
135 		}
136 	}
137 
138 	return bestMode;
139 }
140 
141 
142 static video_mode *
143 find_edid_mode(edid1_info &info, bool allowPalette)
144 {
145 	video_mode *mode = NULL;
146 
147 	// try detailed timing first
148 	for (int32 i = 0; i < EDID1_NUM_DETAILED_MONITOR_DESC; i++) {
149 		edid1_detailed_monitor &monitor = info.detailed_monitor[i];
150 
151 		if (monitor.monitor_desc_type == EDID1_IS_DETAILED_TIMING) {
152 			mode = find_video_mode(monitor.data.detailed_timing.h_active,
153 				monitor.data.detailed_timing.v_active, allowPalette);
154 			if (mode != NULL)
155 				return mode;
156 		}
157 	}
158 
159 	// try standard timings next
160 	for (int32 i = 0; i < EDID1_NUM_STD_TIMING; i++) {
161 		if (info.std_timing[i].h_size <= 256)
162 			continue;
163 
164 		video_mode *found = find_video_mode(info.std_timing[i].h_size,
165 			info.std_timing[i].v_size, allowPalette);
166 		if (found != NULL) {
167 			if (mode != NULL) {
168 				// prefer higher resolutions
169 				if (found->width > mode->width)
170 					mode = found;
171 			} else
172 				mode = found;
173 		}
174 	}
175 
176 	return NULL;
177 }
178 
179 
180 static bool
181 get_mode_from_settings(void)
182 {
183 	if (sSettingsLoaded)
184 		return true;
185 
186 	void *handle = load_driver_settings("vesa");
187 	if (handle == NULL)
188 		return false;
189 
190 	bool found = false;
191 
192 	const driver_settings *settings = get_driver_settings(handle);
193 	if (settings == NULL)
194 		goto out;
195 
196 	sSettingsLoaded = true;
197 
198 	for (int32 i = 0; i < settings->parameter_count; i++) {
199 		driver_parameter &parameter = settings->parameters[i];
200 
201 		if (!strcmp(parameter.name, "mode") && parameter.value_count > 2) {
202 			// parameter found, now get its values
203 			int32 width = strtol(parameter.values[0], NULL, 0);
204 			int32 height = strtol(parameter.values[1], NULL, 0);
205 			int32 depth = strtol(parameter.values[2], NULL, 0);
206 
207 			// search mode that fits
208 
209 			video_mode *mode = closest_video_mode(width, height, depth);
210 			if (mode != NULL) {
211 				found = true;
212 				sMode = mode;
213 			}
214 		}
215 	}
216 
217 out:
218 	unload_driver_settings(handle);
219 	return found;
220 }
221 
222 
223 //	#pragma mark - vga
224 
225 
226 static void
227 vga_set_palette(const uint8 *palette, int32 firstIndex, int32 numEntries)
228 {
229 	out8(firstIndex, VGA_COLOR_WRITE_MODE);
230 	// write VGA palette
231 	for (int32 i = firstIndex; i < numEntries; i++) {
232 		// VGA (usually) has only 6 bits per gun
233 		out8(palette[i * 3 + 0] >> 2, VGA_COLOR_DATA);
234 		out8(palette[i * 3 + 1] >> 2, VGA_COLOR_DATA);
235 		out8(palette[i * 3 + 2] >> 2, VGA_COLOR_DATA);
236 	}
237 }
238 
239 
240 static void
241 vga_enable_bright_background_colors(void)
242 {
243 	// reset attribute controller
244 	in8(VGA_INPUT_STATUS_1);
245 
246 	// select mode control register
247 	out8(0x30, VGA_ATTRIBUTE_WRITE);
248 
249 	// read mode control register, change it (we need to clear bit 3), and write it back
250 	uint8 mode = in8(VGA_ATTRIBUTE_READ) & 0xf7;
251 	out8(mode, VGA_ATTRIBUTE_WRITE);
252 }
253 
254 
255 //	#pragma mark - vesa
256 
257 
258 static status_t
259 vesa_get_edid(edid1_info *info)
260 {
261 	struct bios_regs regs;
262 	regs.eax = 0x4f15;
263 	regs.ebx = 0;
264 		// report DDC service
265 	regs.ecx = 0;
266 	regs.es = 0;
267 	regs.edi = 0;
268 	call_bios(0x10, &regs);
269 
270 	dprintf("EDID1: %lx\n", regs.eax);
271 	// %ah contains the error code
272 	// %al determines wether or not the function is supported
273 	if (regs.eax != 0x4f)
274 		return B_NOT_SUPPORTED;
275 
276 	dprintf("EDID2: ebx %lx\n", regs.ebx);
277 	// test if DDC is supported by the monitor
278 	if ((regs.ebx & 3) == 0)
279 		return B_NOT_SUPPORTED;
280 
281 	edid1_raw edidRaw;
282 
283 	regs.eax = 0x4f15;
284 	regs.ebx = 1;
285 		// read EDID
286 	regs.ecx = 0;
287 	regs.edx = 0;
288 	regs.es = ADDRESS_SEGMENT(&edidRaw);
289 	regs.edi = ADDRESS_OFFSET(&edidRaw);
290 	call_bios(0x10, &regs);
291 	dprintf("EDID3: %lx\n", regs.eax);
292 
293 	if (regs.eax != 0x4f)
294 		return B_NOT_SUPPORTED;
295 
296 	// retrieved EDID - now parse it
297 	dprintf("Got EDID!\n");
298 	edid_decode(info, &edidRaw);
299 	edid_dump(info);
300 	return B_OK;
301 }
302 
303 
304 static status_t
305 vesa_get_mode_info(uint16 mode, struct vbe_mode_info *modeInfo)
306 {
307 	memset(modeInfo, 0, sizeof(vbe_mode_info));
308 
309 	struct bios_regs regs;
310 	regs.eax = 0x4f01;
311 	regs.ecx = mode;
312 	regs.es = ADDRESS_SEGMENT(modeInfo);
313 	regs.edi = ADDRESS_OFFSET(modeInfo);
314 	call_bios(0x10, &regs);
315 
316 	// %ah contains the error code
317 	if ((regs.eax & 0xff00) != 0)
318 		return B_ENTRY_NOT_FOUND;
319 
320 	return B_OK;
321 }
322 
323 
324 static status_t
325 vesa_get_vbe_info_block(vbe_info_block *info)
326 {
327 	memset(info, 0, sizeof(vbe_info_block));
328 	info->signature = VBE2_SIGNATURE;
329 
330 	struct bios_regs regs;
331 	regs.eax = 0x4f00;
332 	regs.es = ADDRESS_SEGMENT(info);
333 	regs.edi = ADDRESS_OFFSET(info);
334 	call_bios(0x10, &regs);
335 
336 	// %ah contains the error code
337 	if ((regs.eax & 0xff00) != 0)
338 		return B_ERROR;
339 
340 	if (info->signature != VESA_SIGNATURE)
341 		return B_ERROR;
342 
343 	dprintf("VESA version = %d.%d\n", info->version.major, info->version.minor);
344 
345 	if (info->version.major < 2) {
346 		dprintf("VESA support too old\n");
347 		return B_ERROR;
348 	}
349 
350 	info->oem_string = SEGMENTED_TO_LINEAR(info->oem_string);
351 	info->mode_list = SEGMENTED_TO_LINEAR(info->mode_list);
352 	dprintf("oem string: %s\n", (const char *)info->oem_string);
353 
354 	return B_OK;
355 }
356 
357 
358 static status_t
359 vesa_init(vbe_info_block *info, video_mode **_standardMode)
360 {
361 	if (vesa_get_vbe_info_block(info) != B_OK)
362 		return B_ERROR;
363 
364 	// fill mode list and find standard video mode
365 
366 	video_mode *standardMode = NULL;
367 
368 	for (int32 i = 0; true; i++) {
369 		uint16 mode = ((uint16 *)info->mode_list)[i];
370 		if (mode == 0xffff)
371 			break;
372 
373 		TRACE(("  %x: ", mode));
374 
375 		struct vbe_mode_info modeInfo;
376 		if (vesa_get_mode_info(mode, &modeInfo) == B_OK) {
377 			TRACE(("%u x %u x %u (a = %d, mem = %d, phy = %lx, p = %d, b = %d)\n",
378 				modeInfo.width, modeInfo.height, modeInfo.bits_per_pixel, modeInfo.attributes,
379 				modeInfo.memory_model, modeInfo.physical_base, modeInfo.num_planes,
380 				modeInfo.num_banks));
381 
382 			const uint32 requiredAttributes = MODE_ATTR_AVAILABLE
383 				| MODE_ATTR_GRAPHICS_MODE | MODE_ATTR_COLOR_MODE
384 				| MODE_ATTR_LINEAR_BUFFER;
385 
386 			if (modeInfo.width >= 640
387 				&& modeInfo.physical_base != 0
388 				&& modeInfo.num_planes == 1
389 				&& (modeInfo.memory_model == MODE_MEMORY_PACKED_PIXEL
390 					|| modeInfo.memory_model == MODE_MEMORY_DIRECT_COLOR)
391 				&& (modeInfo.attributes & requiredAttributes)
392 					== requiredAttributes) {
393 				// this mode fits our needs
394 				video_mode *videoMode = (video_mode *)malloc(
395 					sizeof(struct video_mode));
396 				if (videoMode == NULL)
397 					continue;
398 
399 				videoMode->mode = mode;
400 				videoMode->bytes_per_row = modeInfo.bytes_per_row;
401 				videoMode->width = modeInfo.width;
402 				videoMode->height = modeInfo.height;
403 				videoMode->bits_per_pixel = modeInfo.bits_per_pixel;
404 				if (modeInfo.bits_per_pixel == 16
405 					&& modeInfo.red_mask_size + modeInfo.green_mask_size
406 						+ modeInfo.blue_mask_size == 15) {
407 					// this is really a 15-bit mode
408 					videoMode->bits_per_pixel = 15;
409 				}
410 
411 				add_video_mode(videoMode);
412 			}
413 		} else
414 			TRACE(("(failed)\n"));
415 	}
416 
417 	// Choose default resolution (when no EDID information is available)
418 	const uint32 kPreferredWidth = 1024;
419 	const uint32 kFallbackWidth = 800;
420 
421 	standardMode = find_video_mode(kPreferredWidth, -1, false);
422 	if (standardMode == NULL) {
423 		standardMode = find_video_mode(kFallbackWidth, -1, false);
424 		if (standardMode == NULL) {
425 			standardMode = find_video_mode(kPreferredWidth, -1, true);
426 			if (standardMode == NULL)
427 				standardMode = find_video_mode(kFallbackWidth, -1, true);
428 		}
429 	}
430 	if (standardMode == NULL) {
431 		// just take any mode
432 		standardMode = (video_mode *)list_get_first_item(&sModeList);
433 	}
434 
435 	if (standardMode == NULL) {
436 		// no usable VESA mode found...
437 		return B_ERROR;
438 	}
439 
440 	*_standardMode = standardMode;
441 	return B_OK;
442 }
443 
444 
445 #if 0
446 static status_t
447 vesa_get_mode(uint16 *_mode)
448 {
449 	struct bios_regs regs;
450 	regs.eax = 0x4f03;
451 	call_bios(0x10, &regs);
452 
453 	if ((regs.eax & 0xffff) != 0x4f)
454 		return B_ERROR;
455 
456 	*_mode = regs.ebx & 0xffff;
457 	return B_OK;
458 }
459 #endif
460 
461 
462 static status_t
463 vesa_set_mode(uint16 mode)
464 {
465 	struct bios_regs regs;
466 	regs.eax = 0x4f02;
467 	regs.ebx = (mode & SET_MODE_MASK) | SET_MODE_LINEAR_BUFFER;
468 	call_bios(0x10, &regs);
469 
470 	if ((regs.eax & 0xffff) != 0x4f)
471 		return B_ERROR;
472 
473 #if 0
474 	// make sure we have 8 bits per color channel
475 	regs.eax = 0x4f08;
476 	regs.ebx = 8 << 8;
477 	call_bios(0x10, &regs);
478 #endif
479 
480 	return B_OK;
481 }
482 
483 
484 static status_t
485 vesa_set_palette(const uint8 *palette, int32 firstIndex, int32 numEntries)
486 {
487 	// is this an 8 bit indexed color mode?
488 	if (gKernelArgs.frame_buffer.depth != 8)
489 		return B_BAD_TYPE;
490 
491 #if 0
492 	struct bios_regs regs;
493 	regs.eax = 0x4f09;
494 	regs.ebx = 0;
495 	regs.ecx = numEntries;
496 	regs.edx = firstIndex;
497 	regs.es = (addr_t)palette >> 4;
498 	regs.edi = (addr_t)palette & 0xf;
499 	call_bios(0x10, &regs);
500 
501 	if ((regs.eax & 0xffff) != 0x4f) {
502 #endif
503 		// the VESA call does not work, just try good old VGA mechanism
504 		vga_set_palette(palette, firstIndex, numEntries);
505 #if 0
506 		return B_ERROR;
507 	}
508 #endif
509 	return B_OK;
510 }
511 
512 
513 //	#pragma mark -
514 
515 
516 bool
517 video_mode_hook(Menu *menu, MenuItem *item)
518 {
519 	// find selected mode
520 	video_mode *mode = NULL;
521 
522 	menu = item->Submenu();
523 	item = menu->FindMarked();
524 	if (item != NULL) {
525 		switch (menu->IndexOf(item)) {
526 			case 0:
527 				// "Default" mode special
528 				sMode = sDefaultMode;
529 				sModeChosen = false;
530 				return true;
531 			case 1:
532 				// "Standard VGA" mode special
533 				// sets sMode to NULL which triggers VGA mode
534 				break;
535 			default:
536 				mode = (video_mode *)item->Data();
537 				break;
538 		}
539 	}
540 
541 	if (mode != sMode) {
542 		// update standard mode
543 		// ToDo: update fb settings!
544 		sMode = mode;
545 	}
546 
547 	sModeChosen = true;
548 	return true;
549 }
550 
551 
552 Menu *
553 video_mode_menu()
554 {
555 	Menu *menu = new(nothrow) Menu(CHOICE_MENU, "Select Video Mode");
556 	MenuItem *item;
557 
558 	menu->AddItem(item = new(nothrow) MenuItem("Default"));
559 	item->SetMarked(true);
560 	item->Select(true);
561 	item->SetHelpText("The Default video mode is the one currently configured "
562 		"in the system. If there is no mode configured yet, a viable mode will "
563 		"be chosen automatically.");
564 
565 	menu->AddItem(new(nothrow) MenuItem("Standard VGA"));
566 
567 	video_mode *mode = NULL;
568 	while ((mode = (video_mode *)list_get_next_item(&sModeList, mode)) != NULL) {
569 		char label[64];
570 		sprintf(label, "%ux%u %u bit", mode->width, mode->height,
571 			mode->bits_per_pixel);
572 
573 		menu->AddItem(item = new(nothrow) MenuItem(label));
574 		item->SetData(mode);
575 	}
576 
577 	menu->AddSeparatorItem();
578 	menu->AddItem(item = new(nothrow) MenuItem("Return to main menu"));
579 	item->SetType(MENU_ITEM_NO_CHOICE);
580 
581 	return menu;
582 }
583 
584 
585 static void
586 set_vga_mode(void)
587 {
588 	// sets 640x480 16 colors graphics mode
589 	bios_regs regs;
590 	regs.eax = 0x0012;
591 	call_bios(0x10, &regs);
592 }
593 
594 
595 static void
596 set_text_mode(void)
597 {
598 	// sets 80x25 text console
599 	bios_regs regs;
600 	regs.eax = 0x0003;
601 	call_bios(0x10, &regs);
602 
603 	// turns off text cursor
604 	regs.eax = 0x0100;
605 	regs.ecx = 0x2000;
606 	call_bios(0x10, &regs);
607 }
608 
609 
610 //	#pragma mark - blit
611 
612 
613 static void
614 blit32(const uint8 *data, uint16 width, uint16 height, uint16 imageWidth,
615 	uint16 left, uint16 top)
616 {
617 	uint32 *start = (uint32 *)(sFrameBuffer
618 		+ gKernelArgs.frame_buffer.bytes_per_row * top + 4 * left);
619 
620 	for (int32 y = 0; y < height; y++) {
621 		const uint8* src = data;
622 		uint32* dst = start;
623 		for (int32 x = 0; x < width; x++) {
624 			dst[0] = (src[2] << 16) | (src[1] << 8) | (src[0]);
625 			dst++;
626 			src += 3;
627 		}
628 
629 		data += imageWidth * 3;
630 		start = (uint32 *)((addr_t)start
631 			+ gKernelArgs.frame_buffer.bytes_per_row);
632 	}
633 }
634 
635 
636 static void
637 blit24(const uint8 *data, uint16 width, uint16 height, uint16 imageWidth,
638 	uint16 left, uint16 top)
639 {
640 	uint8 *start = (uint8 *)sFrameBuffer
641 		+ gKernelArgs.frame_buffer.bytes_per_row * top + 3 * left;
642 
643 	for (int32 y = 0; y < height; y++) {
644 		const uint8* src = data;
645 		uint8* dst = start;
646 		for (int32 x = 0; x < width; x++) {
647 			dst[0] = src[0];
648 			dst[1] = src[1];
649 			dst[2] = src[2];
650 			dst += 3;
651 			src += 3;
652 		}
653 
654 		data += imageWidth * 3;
655 		start = start + gKernelArgs.frame_buffer.bytes_per_row;
656 	}
657 }
658 
659 
660 static void
661 blit16(const uint8 *data, uint16 width, uint16 height, uint16 imageWidth,
662 	uint16 left, uint16 top)
663 {
664 	uint16 *start = (uint16 *)(sFrameBuffer
665 		+ gKernelArgs.frame_buffer.bytes_per_row * top + 2 * left);
666 
667 	for (int32 y = 0; y < height; y++) {
668 		const uint8* src = data;
669 		uint16* dst = start;
670 		for (int32 x = 0; x < width; x++) {
671 			dst[0] = ((src[2] >> 3) << 11)
672 				| ((src[1] >> 2) << 5)
673 				| ((src[0] >> 3));
674 			dst++;
675 			src += 3;
676 		}
677 
678 		data += imageWidth * 3;
679 		start = (uint16 *)((addr_t)start
680 			+ gKernelArgs.frame_buffer.bytes_per_row);
681 	}
682 }
683 
684 
685 static void
686 blit15(const uint8 *data, uint16 width, uint16 height, uint16 imageWidth,
687 	uint16 left, uint16 top)
688 {
689 	uint16 *start = (uint16 *)(sFrameBuffer
690 		+ gKernelArgs.frame_buffer.bytes_per_row * top + 2 * left);
691 
692 	for (int32 y = 0; y < height; y++) {
693 		const uint8* src = data;
694 		uint16* dst = start;
695 		for (int32 x = 0; x < width; x++) {
696 			dst[0] = ((src[2] >> 3) << 10)
697 				| ((src[1] >> 3) << 5)
698 				| ((src[0] >> 3));
699 			dst++;
700 			src += 3;
701 		}
702 
703 		data += imageWidth * 3;
704 		start = (uint16 *)((addr_t)start
705 			+ gKernelArgs.frame_buffer.bytes_per_row);
706 	}
707 }
708 
709 
710 static void
711 blit8(const uint8 *data, uint16 width, uint16 height, uint16 imageWidth,
712 	const uint8 *palette, uint16 left, uint16 top)
713 {
714 	if (!data || !palette)
715 		return;
716 
717 	if (vesa_set_palette((const uint8 *)palette, 0, 256) != B_OK)
718 		dprintf("set palette failed!\n");
719 
720 	addr_t start = sFrameBuffer + gKernelArgs.frame_buffer.bytes_per_row * top
721 		+ left;
722 
723 	for (int32 i = 0; i < height; i++) {
724 		memcpy((void *)(start + gKernelArgs.frame_buffer.bytes_per_row * i),
725 			&data[i * imageWidth], width);
726 	}
727 }
728 
729 
730 static void
731 blit4(const uint8 *data, uint16 width, uint16 height, uint16 imageWidth,
732 	const uint8 *palette, uint16 left, uint16 top)
733 {
734 	if (!data || !palette)
735 		return;
736 	//	vga_set_palette((const uint8 *)kPalette16, 0, 16);
737 	// ToDo: no boot logo yet in VGA mode
738 #if 1
739 // this draws 16 big rectangles in all the available colors
740 	uint8 *bits = (uint8 *)sFrameBuffer;
741 	uint32 bytesPerRow = 80;
742 	for (int32 i = 0; i < 32; i++) {
743 		bits[9 * bytesPerRow + i + 2] = 0x55;
744 		bits[30 * bytesPerRow + i + 2] = 0xaa;
745 	}
746 
747 	for (int32 y = 10; y < 30; y++) {
748 		for (int32 i = 0; i < 16; i++) {
749 			out16((15 << 8) | 0x02, VGA_SEQUENCER_INDEX);
750 			bits[32 * bytesPerRow + i*2 + 2] = i;
751 
752 			if (i & 1) {
753 				out16((1 << 8) | 0x02, VGA_SEQUENCER_INDEX);
754 				bits[y * bytesPerRow + i*2 + 2] = 0xff;
755 				bits[y * bytesPerRow + i*2 + 3] = 0xff;
756 			}
757 			if (i & 2) {
758 				out16((2 << 8) | 0x02, VGA_SEQUENCER_INDEX);
759 				bits[y * bytesPerRow + i*2 + 2] = 0xff;
760 				bits[y * bytesPerRow + i*2 + 3] = 0xff;
761 			}
762 			if (i & 4) {
763 				out16((4 << 8) | 0x02, VGA_SEQUENCER_INDEX);
764 				bits[y * bytesPerRow + i*2 + 2] = 0xff;
765 				bits[y * bytesPerRow + i*2 + 3] = 0xff;
766 			}
767 			if (i & 8) {
768 				out16((8 << 8) | 0x02, VGA_SEQUENCER_INDEX);
769 				bits[y * bytesPerRow + i*2 + 2] = 0xff;
770 				bits[y * bytesPerRow + i*2 + 3] = 0xff;
771 			}
772 		}
773 	}
774 
775 	// enable all planes again
776 	out16((15 << 8) | 0x02, VGA_SEQUENCER_INDEX);
777 #endif
778 }
779 
780 
781 static void
782 blit_image(const uint8 *data, const uint8* indexedData, uint16 width,
783 	uint16 height, uint16 imageWidth, const uint8 *palette, uint16 left,
784 	uint16 top)
785 {
786 	switch (gKernelArgs.frame_buffer.depth) {
787 		case 4:
788 			return blit4(indexedData, width, height, imageWidth, palette,
789 				left, top);
790 		case 8:
791 			return blit8(indexedData, width, height, imageWidth, palette,
792 				left, top);
793 		case 15:
794 			return blit15(data, width, height, imageWidth, left, top);
795 		case 16:
796 			return blit16(data, width, height, imageWidth, left, top);
797 		case 24:
798 			return blit24(data, width, height, imageWidth, left, top);
799 		case 32:
800 			return blit32(data, width, height, imageWidth, left, top);
801 	}
802 }
803 
804 
805 static void
806 uncompress_RLE(const uint8 compressed[], uint8 *uncompressed)
807 {
808 	uint32 cursorUncompressed = 0;
809 	uint32 cursorCompressed = 0;
810 	uint8 count = 0;
811 	uint8 item = 0;
812 	int i = 0;
813 	for (uint8 c = 0; c < 3; c++) {
814 		// for Red channel, then Green, then finally Blue...
815 		cursorUncompressed = c;
816 		while (compressed[cursorCompressed]) {
817 			// at the end of the channel there is a terminating 0,
818 			// so the loop will end... (ref: generate_boot_screen.cpp)
819 			count = compressed[cursorCompressed++];
820 			if (count < 128) {
821 				// regular run, repeat "item" "count" times...
822 				item = compressed[cursorCompressed++];
823 				for (i = count - 1; i >= 0; --i) {
824 					uncompressed[cursorUncompressed] = item;
825 					cursorUncompressed += 3;
826 				}
827 			} else {
828 				// enumeration, just write the next "count" items as is...
829 				count = count - 128;
830 				for (i = count - 1; i >= 0; --i) {
831 					uncompressed[cursorUncompressed]
832 						= compressed[cursorCompressed++];
833 					cursorUncompressed += 3;
834 				}
835 			}
836 		}
837 		// the current position of compressed[cursor] is the end of channel,
838 		// we skip it...
839 		cursorCompressed++;
840 	}
841 }
842 
843 //	#pragma mark -
844 
845 extern "C" void
846 platform_switch_to_logo(void)
847 {
848 	// in debug mode, we'll never show the logo
849 	if ((platform_boot_options() & BOOT_OPTION_DEBUG_OUTPUT) != 0)
850 		return;
851 
852 	addr_t lastBase = gKernelArgs.frame_buffer.physical_buffer.start;
853 	size_t lastSize = gKernelArgs.frame_buffer.physical_buffer.size;
854 
855 	if (sVesaCompatible && sMode != NULL) {
856 		if (!sModeChosen)
857 			get_mode_from_settings();
858 
859 		// On some BIOS / chipset / monitor combinations, there seems to be a timing issue between
860 		// getting the EDID data and setting the video mode. As such we wait here briefly to give
861 		// everything enough time to settle.
862 		spin(1000);
863 		if (vesa_set_mode(sMode->mode) != B_OK)
864 			goto fallback;
865 
866 		struct vbe_mode_info modeInfo;
867 		if (vesa_get_mode_info(sMode->mode, &modeInfo) != B_OK)
868 			goto fallback;
869 
870 		gKernelArgs.frame_buffer.width = modeInfo.width;
871 		gKernelArgs.frame_buffer.height = modeInfo.height;
872 		gKernelArgs.frame_buffer.bytes_per_row = modeInfo.bytes_per_row;
873 		gKernelArgs.frame_buffer.depth = modeInfo.bits_per_pixel;
874 		gKernelArgs.frame_buffer.physical_buffer.size = modeInfo.bytes_per_row
875 			* gKernelArgs.frame_buffer.height;
876 		gKernelArgs.frame_buffer.physical_buffer.start = modeInfo.physical_base;
877 	} else {
878 fallback:
879 		// use standard VGA mode 640x480x4
880 		set_vga_mode();
881 
882 		gKernelArgs.frame_buffer.width = 640;
883 		gKernelArgs.frame_buffer.height = 480;
884 		gKernelArgs.frame_buffer.bytes_per_row = 640 / 2;
885 		gKernelArgs.frame_buffer.depth = 4;
886 		gKernelArgs.frame_buffer.physical_buffer.size
887 			= gKernelArgs.frame_buffer.width
888 			* gKernelArgs.frame_buffer.height / 2;
889 		gKernelArgs.frame_buffer.physical_buffer.start = 0xa0000;
890 	}
891 
892 	dprintf("video mode: %ux%ux%u\n", gKernelArgs.frame_buffer.width,
893 		gKernelArgs.frame_buffer.height, gKernelArgs.frame_buffer.depth);
894 
895 	gKernelArgs.frame_buffer.enabled = true;
896 
897 	// If the new frame buffer is either larger than the old one or located at
898 	// a different address, we need to remap it, so we first have to throw
899 	// away its previous mapping
900 	if (lastBase != 0
901 		&& (lastBase != gKernelArgs.frame_buffer.physical_buffer.start
902 			|| lastSize < gKernelArgs.frame_buffer.physical_buffer.size)) {
903 		mmu_free((void *)sFrameBuffer, lastSize);
904 		lastBase = 0;
905 	}
906 	if (lastBase == 0) {
907 		// the graphics memory has not been mapped yet!
908 		sFrameBuffer = mmu_map_physical_memory(
909 			gKernelArgs.frame_buffer.physical_buffer.start,
910 			gKernelArgs.frame_buffer.physical_buffer.size, kDefaultPageFlags);
911 	}
912 
913 	// clear the video memory
914 	memset((void *)sFrameBuffer, 0,
915 		gKernelArgs.frame_buffer.physical_buffer.size);
916 
917 	uint8 *uncompressedLogo = (uint8 *)kernel_args_malloc(kSplashLogoWidth
918 		* kSplashLogoHeight * 3);
919 	if (uncompressedLogo == NULL)
920 		return;
921 	uncompress_RLE(kSplashLogoCompressedImage, uncompressedLogo);
922 
923 	// TODO: support indexed versions of the images!
924 
925 	// render splash logo
926 	uint16 iconsHalfHeight = kSplashIconsHeight / 2;
927 
928 	int width = min_c(kSplashLogoWidth, gKernelArgs.frame_buffer.width);
929 	int height = min_c(kSplashLogoHeight + iconsHalfHeight,
930 		gKernelArgs.frame_buffer.height);
931 	int placementX = max_c(0, min_c(100, kSplashLogoPlacementX));
932 	int placementY = max_c(0, min_c(100, kSplashLogoPlacementY));
933 
934 	int x = (gKernelArgs.frame_buffer.width - width) * placementX / 100;
935 	int y = (gKernelArgs.frame_buffer.height - height) * placementY / 100;
936 
937 	height = min_c(kSplashLogoHeight, gKernelArgs.frame_buffer.height);
938 	blit_image(uncompressedLogo, NULL, width, height, kSplashLogoWidth,
939 		NULL, x, y);
940 
941 	kernel_args_free(uncompressedLogo);
942 
943 	gKernelArgs.boot_splash = (uint8 *)kernel_args_malloc(kSplashIconsWidth
944 		* kSplashIconsHeight * 3);
945 	if (gKernelArgs.boot_splash == NULL)
946 		return;
947 	uncompress_RLE(kSplashIconsCompressedImage, gKernelArgs.boot_splash );
948 
949 	// render initial (grayed out) icons
950 	// the grayed out version is the lower half of the icons image
951 
952 	width = min_c(kSplashIconsWidth, gKernelArgs.frame_buffer.width);
953 	height = min_c(kSplashLogoHeight + iconsHalfHeight,
954 		gKernelArgs.frame_buffer.height);
955 	placementX = max_c(0, min_c(100, kSplashIconsPlacementX));
956 	placementY = max_c(0, min_c(100, kSplashIconsPlacementY));
957 
958 	x = (gKernelArgs.frame_buffer.width - width) * placementX / 100;
959 	y = kSplashLogoHeight + (gKernelArgs.frame_buffer.height - height)
960 		* placementY / 100;
961 
962 	// pointer into the lower half of the icons image data
963 	const uint8* lowerHalfIconImage = gKernelArgs.boot_splash
964 		+ (kSplashIconsWidth * iconsHalfHeight) * 3;
965 	height = min_c(iconsHalfHeight, gKernelArgs.frame_buffer.height);
966 	blit_image(lowerHalfIconImage, NULL, width, height,
967 		kSplashIconsWidth, NULL, x, y);
968 }
969 
970 
971 extern "C" void
972 platform_switch_to_text_mode(void)
973 {
974 	if (!gKernelArgs.frame_buffer.enabled) {
975 		vga_enable_bright_background_colors();
976 		return;
977 	}
978 
979 	set_text_mode();
980 	gKernelArgs.frame_buffer.enabled = 0;
981 
982 	vga_enable_bright_background_colors();
983 }
984 
985 
986 extern "C" status_t
987 platform_init_video(void)
988 {
989 	gKernelArgs.frame_buffer.enabled = 0;
990 	list_init(&sModeList);
991 
992 	set_text_mode();
993 		// You may wonder why we do this here:
994 		// Obviously, some graphics card BIOS implementations don't
995 		// report all available modes unless you've done this before
996 		// getting the VESA information.
997 		// One example of those is the SiS 630 chipset in my laptop.
998 
999 	sVesaCompatible = vesa_init(&sInfo, &sDefaultMode) == B_OK;
1000 	if (!sVesaCompatible) {
1001 		TRACE(("No VESA compatible graphics!\n"));
1002 		return B_ERROR;
1003 	}
1004 
1005 	TRACE(("VESA compatible graphics!\n"));
1006 
1007 	// store VESA modes into kernel args
1008 	vesa_mode *modes = (vesa_mode *)kernel_args_malloc(
1009 		sModeCount * sizeof(vesa_mode));
1010 	if (modes != NULL) {
1011 		video_mode *mode = NULL;
1012 		uint32 i = 0;
1013 		while ((mode = (video_mode *)list_get_next_item(&sModeList, mode))
1014 				!= NULL) {
1015 			modes[i].mode = mode->mode;
1016 			modes[i].width = mode->width;
1017 			modes[i].height = mode->height;
1018 			modes[i].bits_per_pixel = mode->bits_per_pixel;
1019 			i++;
1020 		}
1021 
1022 		gKernelArgs.vesa_modes = modes;
1023 		gKernelArgs.vesa_modes_size = sModeCount * sizeof(vesa_mode);
1024 	}
1025 
1026 	edid1_info info;
1027 	if (vesa_get_edid(&info) == B_OK) {
1028 		// we got EDID information from the monitor, try to find a new default
1029 		// mode
1030 		video_mode *defaultMode = find_edid_mode(info, false);
1031 		if (defaultMode == NULL)
1032 			find_edid_mode(info, true);
1033 
1034 		if (defaultMode != NULL) {
1035 			// We found a new default mode to use!
1036 			sDefaultMode = defaultMode;
1037 		}
1038 
1039 		gKernelArgs.edid_info = kernel_args_malloc(sizeof(edid1_info));
1040 		if (gKernelArgs.edid_info != NULL)
1041 			memcpy(gKernelArgs.edid_info, &info, sizeof(edid1_info));
1042 	}
1043 
1044 	sMode = sDefaultMode;
1045 	return B_OK;
1046 }
1047 
1048