xref: /haiku/src/system/boot/platform/bios_ia32/video.cpp (revision d3d8b26997fac34a84981e6d2b649521de2cc45a)
1 /*
2  * Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 
7 #include "video.h"
8 #include "bios.h"
9 #include "vesa.h"
10 #include "vga.h"
11 #include "mmu.h"
12 #include "images.h"
13 
14 #include <arch/cpu.h>
15 #include <boot/stage2.h>
16 #include <boot/platform.h>
17 #include <boot/menu.h>
18 #include <boot/kernel_args.h>
19 #include <util/list.h>
20 #include <drivers/driver_settings.h>
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 
26 
27 //#define TRACE_VIDEO
28 #ifdef TRACE_VIDEO
29 #	define TRACE(x) dprintf x
30 #else
31 #	define TRACE(x) ;
32 #endif
33 
34 
35 struct video_mode {
36 	list_link	link;
37 	uint16		mode;
38 	int32		width, height, bits_per_pixel;
39 };
40 
41 static vbe_info_block sInfo;
42 static video_mode *sMode, *sDefaultMode;
43 static bool sVesaCompatible;
44 struct list sModeList;
45 static addr_t sFrameBuffer;
46 static bool sModeChosen;
47 static bool sSettingsLoaded;
48 
49 
50 static int
51 compare_video_modes(video_mode *a, video_mode *b)
52 {
53 	int compare = a->width - b->width;
54 	if (compare != 0)
55 		return compare;
56 
57 	compare = a->height - b->height;
58 	if (compare != 0)
59 		return compare;
60 
61 	// TODO: compare video_mode::mode?
62 	return a->bits_per_pixel - b->bits_per_pixel;
63 }
64 
65 
66 /**	Insert the video mode into the list, sorted by resolution and bit depth.
67  *	Higher resolutions/depths come first.
68  */
69 
70 static void
71 add_video_mode(video_mode *videoMode)
72 {
73 	video_mode *mode = NULL;
74 	while ((mode = (video_mode *)list_get_next_item(&sModeList, mode)) != NULL) {
75 		int compare = compare_video_modes(videoMode, mode);
76 		if (compare == 0) {
77 			// mode already exists
78 			return;
79 		}
80 
81 		if (compare > 0)
82 			break;
83 	}
84 
85 	list_insert_item_before(&sModeList, mode, videoMode);
86 }
87 
88 
89 static video_mode *
90 find_video_mode(int32 width, int32 height, int32 depth)
91 {
92 	video_mode *mode = NULL;
93 	while ((mode = (video_mode *)list_get_next_item(&sModeList, mode)) != NULL) {
94 		if (mode->width == width
95 			&& mode->height == height
96 			&& mode->bits_per_pixel == depth) {
97 			return mode;
98 		}
99 	}
100 
101 	return NULL;
102 }
103 
104 
105 static void
106 get_mode_from_settings(void)
107 {
108 	if (sSettingsLoaded)
109 		return;
110 
111 	void *handle = load_driver_settings("vesa");
112 	if (handle == NULL)
113 		return;
114 
115 	const driver_settings *settings = get_driver_settings(handle);
116 	if (settings == NULL)
117 		goto out;
118 
119 	sSettingsLoaded = true;
120 
121 	for (int32 i = 0; i < settings->parameter_count; i++) {
122 		driver_parameter &parameter = settings->parameters[i];
123 
124 		if (!strcmp(parameter.name, "mode") && parameter.value_count > 2) {
125 			// parameter found, now get its values
126 			int32 width = strtol(parameter.values[0], NULL, 0);
127 			int32 height = strtol(parameter.values[1], NULL, 0);
128 			int32 depth = strtol(parameter.values[2], NULL, 0);
129 
130 			// search mode that fits
131 
132 			video_mode *mode = find_video_mode(width, height, depth);
133 			if (mode != NULL)
134 				sMode = mode;
135 		}
136 	}
137 
138 out:
139 	unload_driver_settings(handle);
140 }
141 
142 
143 //	#pragma mark - vga
144 
145 
146 static void
147 vga_set_palette(const uint8 *palette, int32 firstIndex, int32 numEntries)
148 {
149 	out8(firstIndex, VGA_COLOR_WRITE_MODE);
150 	// write VGA palette
151 	for (int32 i = firstIndex; i < numEntries; i++) {
152 		// VGA (usually) has only 6 bits per gun
153 		out8(palette[i * 3 + 0] >> 2, VGA_COLOR_DATA);
154 		out8(palette[i * 3 + 1] >> 2, VGA_COLOR_DATA);
155 		out8(palette[i * 3 + 2] >> 2, VGA_COLOR_DATA);
156 	}
157 }
158 
159 
160 static void
161 vga_enable_bright_background_colors(void)
162 {
163 	// reset attribute controller
164 	in8(VGA_INPUT_STATUS_1);
165 
166 	// select mode control register
167 	out8(0x30, VGA_ATTRIBUTE_WRITE);
168 
169 	// read mode control register, change it (we need to clear bit 3), and write it back
170 	uint8 mode = in8(VGA_ATTRIBUTE_READ) & 0xf7;
171 	out8(mode, VGA_ATTRIBUTE_WRITE);
172 }
173 
174 
175 //	#pragma mark - vesa
176 //	VESA functions
177 
178 
179 static status_t
180 vesa_get_mode_info(uint16 mode, struct vbe_mode_info *modeInfo)
181 {
182 	memset(modeInfo, 0, sizeof(vbe_mode_info));
183 
184 	struct bios_regs regs;
185 	regs.eax = 0x4f01;
186 	regs.ecx = mode;
187 	regs.es = ADDRESS_SEGMENT(modeInfo);
188 	regs.edi = ADDRESS_OFFSET(modeInfo);
189 	call_bios(0x10, &regs);
190 
191 	// %ah contains the error code
192 	if ((regs.eax & 0xff00) != 0)
193 		return B_ENTRY_NOT_FOUND;
194 
195 	return B_OK;
196 }
197 
198 
199 static status_t
200 vesa_get_vbe_info_block(vbe_info_block *info)
201 {
202 	memset(info, 0, sizeof(vbe_info_block));
203 	info->signature = VBE2_SIGNATURE;
204 
205 	struct bios_regs regs;
206 	regs.eax = 0x4f00;
207 	regs.es = ADDRESS_SEGMENT(info);
208 	regs.edi = ADDRESS_OFFSET(info);
209 	call_bios(0x10, &regs);
210 
211 	// %ah contains the error code
212 	if ((regs.eax & 0xff00) != 0)
213 		return B_ERROR;
214 
215 	if (info->signature != VESA_SIGNATURE)
216 		return B_ERROR;
217 
218 	dprintf("VESA version = %lx\n", info->version);
219 
220 	if (info->version.major < 2) {
221 		dprintf("VESA support too old\n", info->version);
222 		return B_ERROR;
223 	}
224 
225 	info->oem_string = SEGMENTED_TO_LINEAR(info->oem_string);
226 	info->mode_list = SEGMENTED_TO_LINEAR(info->mode_list);
227 	dprintf("oem string: %s\n", (const char *)info->oem_string);
228 
229 	return B_OK;
230 }
231 
232 
233 static status_t
234 vesa_init(vbe_info_block *info, video_mode **_standardMode)
235 {
236 	if (vesa_get_vbe_info_block(info) != B_OK)
237 		return B_ERROR;
238 
239 	// fill mode list and find standard video mode
240 
241 	video_mode *standardMode = NULL;
242 
243 	for (int32 i = 0; true; i++) {
244 		uint16 mode = ((uint16 *)info->mode_list)[i];
245 		if (mode == 0xffff)
246 			break;
247 
248 		TRACE(("  %lx: ", mode));
249 
250 		struct vbe_mode_info modeInfo;
251 		if (vesa_get_mode_info(mode, &modeInfo) == B_OK) {
252 			TRACE(("%ld x %ld x %ld (a = %ld, mem = %ld, phy = %lx, p = %ld, b = %ld)\n",
253 				modeInfo.width, modeInfo.height, modeInfo.bits_per_pixel, modeInfo.attributes,
254 				modeInfo.memory_model, modeInfo.physical_base, modeInfo.num_planes,
255 				modeInfo.num_banks));
256 
257 			const uint32 requiredAttributes = MODE_ATTR_AVAILABLE | MODE_ATTR_GRAPHICS_MODE
258 								| MODE_ATTR_COLOR_MODE | MODE_ATTR_LINEAR_BUFFER;
259 
260 			if (modeInfo.width >= 640
261 				&& modeInfo.physical_base != 0
262 				&& modeInfo.num_planes == 1
263 				&& (modeInfo.memory_model == MODE_MEMORY_PACKED_PIXEL
264 					|| modeInfo.memory_model == MODE_MEMORY_DIRECT_COLOR)
265 				&& (modeInfo.attributes & requiredAttributes) == requiredAttributes) {
266 				// this mode fits our needs
267 				video_mode *videoMode = (video_mode *)malloc(sizeof(struct video_mode));
268 				if (videoMode == NULL)
269 					continue;
270 
271 				videoMode->mode = mode;
272 				videoMode->width = modeInfo.width;
273 				videoMode->height = modeInfo.height;
274 				videoMode->bits_per_pixel = modeInfo.bits_per_pixel;
275 
276 				if (standardMode == NULL)
277 					standardMode = videoMode;
278 				else if (standardMode != NULL && modeInfo.bits_per_pixel <= 16) {
279 					// for the standard mode, we prefer a bit depth of 16
280 					// switch to the one with the higher resolution
281 					// ToDo: is that always a good idea? for now we'll use 800x600
282 					if (modeInfo.width >= standardMode->width && modeInfo.width <= 800) {
283 						if (modeInfo.width != standardMode->width
284 							|| modeInfo.bits_per_pixel >= standardMode->bits_per_pixel)
285 							standardMode = videoMode;
286 					}
287 				}
288 
289 				add_video_mode(videoMode);
290 			}
291 		} else
292 			TRACE(("(failed)\n"));
293 	}
294 
295 	if (standardMode == NULL) {
296 		// no usable VESA mode found...
297 		return B_ERROR;
298 	}
299 
300 	*_standardMode = standardMode;
301 	return B_OK;
302 }
303 
304 
305 #if 0
306 static status_t
307 vesa_get_mode(uint16 *_mode)
308 {
309 	struct bios_regs regs;
310 	regs.eax = 0x4f03;
311 	call_bios(0x10, &regs);
312 
313 	if ((regs.eax & 0xffff) != 0x4f)
314 		return B_ERROR;
315 
316 	*_mode = regs.ebx & 0xffff;
317 	return B_OK;
318 }
319 #endif
320 
321 
322 static status_t
323 vesa_set_mode(uint16 mode)
324 {
325 	struct bios_regs regs;
326 	regs.eax = 0x4f02;
327 	regs.ebx = (mode & SET_MODE_MASK) | SET_MODE_LINEAR_BUFFER;
328 	call_bios(0x10, &regs);
329 
330 	if ((regs.eax & 0xffff) != 0x4f)
331 		return B_ERROR;
332 
333 #if 0
334 	// make sure we have 8 bits per color channel
335 	regs.eax = 0x4f08;
336 	regs.ebx = 8 << 8;
337 	call_bios(0x10, &regs);
338 #endif
339 
340 	return B_OK;
341 }
342 
343 
344 static status_t
345 vesa_set_palette(const uint8 *palette, int32 firstIndex, int32 numEntries)
346 {
347 	// is this an 8 bit indexed color mode?
348 	if (gKernelArgs.frame_buffer.depth != 8)
349 		return B_BAD_TYPE;
350 
351 #if 0
352 	struct bios_regs regs;
353 	regs.eax = 0x4f09;
354 	regs.ebx = 0;
355 	regs.ecx = numEntries;
356 	regs.edx = firstIndex;
357 	regs.es = (addr_t)palette >> 4;
358 	regs.edi = (addr_t)palette & 0xf;
359 	call_bios(0x10, &regs);
360 
361 	if ((regs.eax & 0xffff) != 0x4f) {
362 #endif
363 		// the VESA call does not work, just try good old VGA mechanism
364 		vga_set_palette(palette, firstIndex, numEntries);
365 #if 0
366 		return B_ERROR;
367 	}
368 #endif
369 	return B_OK;
370 }
371 
372 
373 //	#pragma mark -
374 
375 
376 bool
377 video_mode_hook(Menu *menu, MenuItem *item)
378 {
379 	// find selected mode
380 	video_mode *mode = NULL;
381 
382 	menu = item->Submenu();
383 	item = menu->FindMarked();
384 	if (item != NULL) {
385 		switch (menu->IndexOf(item)) {
386 			case 0:
387 				// "Default" mode special
388 				sMode = sDefaultMode;
389 				sModeChosen = false;
390 				return true;
391 			case 1:
392 				// "Standard VGA" mode special
393 				// sets sMode to NULL which triggers VGA mode
394 				break;
395 			default:
396 				mode = (video_mode *)item->Data();
397 				break;
398 		}
399 	}
400 
401 	if (mode != sMode) {
402 		// update standard mode
403 		// ToDo: update fb settings!
404 		sMode = mode;
405 	}
406 
407 	sModeChosen = true;
408 	return true;
409 }
410 
411 
412 Menu *
413 video_mode_menu()
414 {
415 	Menu *menu = new Menu(CHOICE_MENU, "Select Video Mode");
416 	MenuItem *item;
417 
418 	menu->AddItem(item = new MenuItem("Default"));
419 	item->SetMarked(true);
420 	item->Select(true);
421 	item->SetHelpText("The Default video mode is the one currently configured in "
422 		"the system. If there is no mode configured yet, a viable mode will be chosen "
423 		"automatically.");
424 
425 	menu->AddItem(new MenuItem("Standard VGA"));
426 
427 	video_mode *mode = NULL;
428 	while ((mode = (video_mode *)list_get_next_item(&sModeList, mode)) != NULL) {
429 		char label[64];
430 		sprintf(label, "%ldx%ld %ld bit", mode->width, mode->height, mode->bits_per_pixel);
431 
432 		menu->AddItem(item = new MenuItem(label));
433 		item->SetData(mode);
434 	}
435 
436 	menu->AddSeparatorItem();
437 	menu->AddItem(item = new MenuItem("Return to main menu"));
438 	item->SetType(MENU_ITEM_NO_CHOICE);
439 
440 	return menu;
441 }
442 
443 
444 static void
445 set_vga_mode(void)
446 {
447 	// sets 640x480 16 colors graphics mode
448 	bios_regs regs;
449 	regs.eax = 0x12;
450 	call_bios(0x10, &regs);
451 }
452 
453 
454 static void
455 set_text_mode(void)
456 {
457 	// sets 80x25 text console
458 	bios_regs regs;
459 	regs.eax = 3;
460 	call_bios(0x10, &regs);
461 }
462 
463 
464 //	#pragma mark - blit
465 
466 
467 static void
468 blit32(const uint8 *data, uint16 width, uint16 height,
469 	const uint8 *palette, uint16 left, uint16 top)
470 {
471 	uint32 *start = (uint32 *)sFrameBuffer + gKernelArgs.frame_buffer.width * top + left;
472 
473 	for (int32 y = 0; y < height; y++) {
474 		for (int32 x = 0; x < width; x++) {
475 			uint16 color = data[y * width + x] * 3;
476 
477 			start[x] = (palette[color + 0] << 16) | (palette[color + 1] << 8) | (palette[color + 2]);
478 		}
479 
480 		start += gKernelArgs.frame_buffer.width;
481 	}
482 }
483 
484 
485 static void
486 blit24(const uint8 *data, uint16 width, uint16 height,
487 	const uint8 *palette, uint16 left, uint16 top)
488 {
489 	uint8 *start = (uint8 *)sFrameBuffer + gKernelArgs.frame_buffer.width * 3 * top + 3 * left;
490 
491 	for (int32 y = 0; y < height; y++) {
492 		for (int32 x = 0; x < width; x++) {
493 			uint16 color = data[y * width + x] * 3;
494 			uint32 index = x * 3;
495 
496 			start[index + 0] = palette[color + 2];
497 			start[index + 1] = palette[color + 1];
498 			start[index + 2] = palette[color + 0];
499 		}
500 
501 		start += gKernelArgs.frame_buffer.width * 3;
502 	}
503 }
504 
505 
506 static void
507 blit16(const uint8 *data, uint16 width, uint16 height,
508 	const uint8 *palette, uint16 left, uint16 top)
509 {
510 	uint16 *start = (uint16 *)sFrameBuffer + gKernelArgs.frame_buffer.width * top + left;
511 
512 	for (int32 y = 0; y < height; y++) {
513 		for (int32 x = 0; x < width; x++) {
514 			uint16 color = data[y * width + x] * 3;
515 
516 			start[x] = ((palette[color + 0] >> 3) << 11) | ((palette[color + 1] >> 2) << 5)
517 				| ((palette[color + 2] >> 3));
518 		}
519 
520 		start += gKernelArgs.frame_buffer.width;
521 	}
522 }
523 
524 
525 static void
526 blit15(const uint8 *data, uint16 width, uint16 height,
527 	const uint8 *palette, uint16 left, uint16 top)
528 {
529 	uint16 *start = (uint16 *)sFrameBuffer + gKernelArgs.frame_buffer.width * top + left;
530 
531 	for (int32 y = 0; y < height; y++) {
532 		for (int32 x = 0; x < width; x++) {
533 			uint16 color = data[y * width + x] * 3;
534 
535 			start[x] = ((palette[color + 0] >> 3) << 10) | ((palette[color + 1] >> 3) << 5)
536 				| ((palette[color + 2] >> 3));
537 		}
538 
539 		start += gKernelArgs.frame_buffer.width;
540 	}
541 }
542 
543 
544 static void
545 blit8(const uint8 *data, uint16 width, uint16 height,
546 	const uint8 *palette, uint16 left, uint16 top)
547 {
548 	if (vesa_set_palette((const uint8 *)kPalette, 0, 256) != B_OK)
549 		dprintf("set palette failed!\n");
550 
551 	addr_t start = sFrameBuffer + gKernelArgs.frame_buffer.width * top + left;
552 
553 	for (int32 i = 0; i < height; i++) {
554 		memcpy((void *)(start + gKernelArgs.frame_buffer.width * i),
555 			&data[i * width], width);
556 	}
557 }
558 
559 
560 static void
561 blit4(const uint8 *data, uint16 width, uint16 height,
562 	const uint8 *palette, uint16 left, uint16 top)
563 {
564 	//	vga_set_palette((const uint8 *)kPalette16, 0, 16);
565 	// ToDo: no boot logo yet in VGA mode
566 #if 1
567 // this draws 16 big rectangles in all the available colors
568 	uint8 *bits = (uint8 *)sFrameBuffer;
569 	uint32 bytesPerRow = 80;
570 	for (int32 i = 0; i < 32; i++) {
571 		bits[9 * bytesPerRow + i + 2] = 0x55;
572 		bits[30 * bytesPerRow + i + 2] = 0xaa;
573 	}
574 
575 	for (int32 y = 10; y < 30; y++) {
576 		for (int32 i = 0; i < 16; i++) {
577 			out16((15 << 8) | 0x02, VGA_SEQUENCER_INDEX);
578 			bits[32 * bytesPerRow + i*2 + 2] = i;
579 
580 			if (i & 1) {
581 				out16((1 << 8) | 0x02, VGA_SEQUENCER_INDEX);
582 				bits[y * bytesPerRow + i*2 + 2] = 0xff;
583 				bits[y * bytesPerRow + i*2 + 3] = 0xff;
584 			}
585 			if (i & 2) {
586 				out16((2 << 8) | 0x02, VGA_SEQUENCER_INDEX);
587 				bits[y * bytesPerRow + i*2 + 2] = 0xff;
588 				bits[y * bytesPerRow + i*2 + 3] = 0xff;
589 			}
590 			if (i & 4) {
591 				out16((4 << 8) | 0x02, VGA_SEQUENCER_INDEX);
592 				bits[y * bytesPerRow + i*2 + 2] = 0xff;
593 				bits[y * bytesPerRow + i*2 + 3] = 0xff;
594 			}
595 			if (i & 8) {
596 				out16((8 << 8) | 0x02, VGA_SEQUENCER_INDEX);
597 				bits[y * bytesPerRow + i*2 + 2] = 0xff;
598 				bits[y * bytesPerRow + i*2 + 3] = 0xff;
599 			}
600 		}
601 	}
602 
603 	// enable all planes again
604 	out16((15 << 8) | 0x02, VGA_SEQUENCER_INDEX);
605 #endif
606 }
607 
608 
609 static void
610 blit_8bit_image(const uint8 *data, uint16 width, uint16 height,
611 	const uint8 *palette, uint16 left, uint16 top)
612 {
613 	switch (gKernelArgs.frame_buffer.depth) {
614 		case 4:
615 			return blit4(data, width, height, palette, left, top);
616 		case 8:
617 			return blit8(data, width, height, palette, left, top);
618 		case 15:
619 			return blit15(data, width, height, palette, left, top);
620 		case 16:
621 			return blit16(data, width, height, palette, left, top);
622 		case 24:
623 			return blit24(data, width, height, palette, left, top);
624 		case 32:
625 			return blit32(data, width, height, palette, left, top);
626 	}
627 }
628 
629 
630 //	#pragma mark -
631 
632 
633 extern "C" void
634 platform_switch_to_logo(void)
635 {
636 	// in debug mode, we'll never show the logo
637 	if ((platform_boot_options() & BOOT_OPTION_DEBUG_OUTPUT) != 0)
638 		return;
639 
640 	addr_t lastBase = gKernelArgs.frame_buffer.physical_buffer.start;
641 	size_t lastSize = gKernelArgs.frame_buffer.physical_buffer.size;
642 	int32 bytesPerPixel = 1;
643 
644 	if (sVesaCompatible && sMode != NULL) {
645 		if (!sModeChosen)
646 			get_mode_from_settings();
647 
648 		if (vesa_set_mode(sMode->mode) != B_OK)
649 			goto fallback;
650 
651 		struct vbe_mode_info modeInfo;
652 		if (vesa_get_mode_info(sMode->mode, &modeInfo) != B_OK)
653 			goto fallback;
654 
655 		bytesPerPixel = (modeInfo.bits_per_pixel + 7) / 8;
656 
657 		gKernelArgs.frame_buffer.width = modeInfo.width;
658 		gKernelArgs.frame_buffer.height = modeInfo.height;
659 		gKernelArgs.frame_buffer.depth = modeInfo.bits_per_pixel;
660 		gKernelArgs.frame_buffer.physical_buffer.size = gKernelArgs.frame_buffer.width
661 			* gKernelArgs.frame_buffer.height * bytesPerPixel;
662 		gKernelArgs.frame_buffer.physical_buffer.start = modeInfo.physical_base;
663 	} else {
664 fallback:
665 		// use standard VGA mode 640x480x4
666 		set_vga_mode();
667 
668 		gKernelArgs.frame_buffer.width = 640;
669 		gKernelArgs.frame_buffer.height = 480;
670 		gKernelArgs.frame_buffer.depth = 4;
671 		gKernelArgs.frame_buffer.physical_buffer.size = gKernelArgs.frame_buffer.width
672 			* gKernelArgs.frame_buffer.height / 2;
673 		gKernelArgs.frame_buffer.physical_buffer.start = 0xa0000;
674 	}
675 
676 	gKernelArgs.frame_buffer.enabled = 1;
677 
678 	// If the new frame buffer is either larger than the old one or located at
679 	// a different address, we need to remap it, so we first have to throw
680 	// away its previous mapping
681 	if (lastBase != 0
682 		&& (lastBase != gKernelArgs.frame_buffer.physical_buffer.start
683 			|| lastSize < gKernelArgs.frame_buffer.physical_buffer.size)) {
684 		mmu_free((void *)sFrameBuffer, lastSize);
685 		lastBase = 0;
686 	}
687 	if (lastBase == 0) {
688 		// the graphics memory has not been mapped yet!
689 		sFrameBuffer = mmu_map_physical_memory(gKernelArgs.frame_buffer.physical_buffer.start,
690 							gKernelArgs.frame_buffer.physical_buffer.size, kDefaultPageFlags);
691 	}
692 
693 	// clear the video memory
694 	// ToDo: this shouldn't be necessary on real hardware (and Bochs), but
695 	//	at least booting with Qemu looks ugly when this is missing
696 	memset((void *)sFrameBuffer, 0, gKernelArgs.frame_buffer.physical_buffer.size);
697 
698 	// ToDo: the boot image is only a temporary solution - it should be
699 	//	provided by the loader itself, as well as the blitting routines.
700 	//	The image should be compressed, too.
701 
702 	blit_8bit_image(kImageData, kWidth, kHeight, kPalette,
703 		gKernelArgs.frame_buffer.width - kWidth - 40,
704 		gKernelArgs.frame_buffer.height - kHeight - 60);
705 }
706 
707 
708 extern "C" void
709 platform_switch_to_text_mode(void)
710 {
711 	if (!gKernelArgs.frame_buffer.enabled) {
712 		vga_enable_bright_background_colors();
713 		return;
714 	}
715 
716 	set_text_mode();
717 	gKernelArgs.frame_buffer.enabled = 0;
718 
719 	vga_enable_bright_background_colors();
720 }
721 
722 
723 extern "C" status_t
724 platform_init_video(void)
725 {
726 	gKernelArgs.frame_buffer.enabled = 0;
727 	list_init(&sModeList);
728 
729 	set_text_mode();
730 		// You may wonder why we do this here:
731 		// Obviously, some graphics card BIOS implementations don't
732 		// report all available modes unless you've done this before
733 		// getting the VESA information.
734 		// One example of those is the SiS 630 chipset in my laptop.
735 
736 	sVesaCompatible = vesa_init(&sInfo, &sDefaultMode) == B_OK;
737 	if (!sVesaCompatible) {
738 		TRACE(("No VESA compatible graphics!\n"));
739 		return B_ERROR;
740 	}
741 
742 	sMode = sDefaultMode;
743 
744 	TRACE(("VESA compatible graphics!\n"));
745 
746 	return B_OK;
747 }
748 
749