]> git.saurik.com Git - apple/boot.git/blob - i386/boot2/graphics.c
d5359b9a4a4d43dbe80aef7888ca2fb85ed8f7f1
[apple/boot.git] / i386 / boot2 / graphics.c
1 /*
2 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights
7 * Reserved. This file contains Original Code and/or Modifications of
8 * Original Code as defined in and that are subject to the Apple Public
9 * Source License Version 1.1 (the "License"). You may not use this file
10 * except in compliance with the License. Please obtain a copy of the
11 * License at http://www.apple.com/publicsource and read it before using
12 * this file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT. Please see the
19 * License for the specific language governing rights and limitations
20 * under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24 /*
25 * Copyright 1993 NeXT, Inc.
26 * All rights reserved.
27 */
28
29 #include "boot.h"
30 #include "vbe.h"
31 #include "appleClut8.h"
32 #include "appleboot.h"
33 #include "bootstruct.h"
34
35 /*
36 * for spinning disk
37 */
38 static int currentIndicator = 0;
39
40 static unsigned long lookUpCLUTIndex( unsigned char index,
41 unsigned char depth );
42
43 static void drawColorRectangle( unsigned short x,
44 unsigned short y,
45 unsigned short width,
46 unsigned short height,
47 unsigned char colorIndex );
48
49 static void drawDataRectangle( unsigned short x,
50 unsigned short y,
51 unsigned short width,
52 unsigned short height,
53 unsigned char * data );
54
55 #define VIDEO(x) (bootArgs->video.v_ ## x)
56
57
58 //==========================================================================
59 // printVBEInfo
60
61 void printVBEInfo()
62 {
63 VBEInfoBlock vbeInfo;
64 int err;
65
66 // Fetch VBE Controller Info.
67
68 bzero( &vbeInfo, sizeof(vbeInfo) );
69 err = getVBEInfo( &vbeInfo );
70 if ( err != errSuccess )
71 return;
72
73 // Check presence of VESA signature.
74
75 if ( strncmp( vbeInfo.VESASignature, "VESA", 4 ) )
76 return;
77
78 // Announce controller properties.
79
80 printf("VESA v%d.%d %dMB (%s)\n",
81 vbeInfo.VESAVersion >> 8,
82 vbeInfo.VESAVersion & 0xf,
83 vbeInfo.TotalMemory / 16,
84 VBEDecodeFP(const char *, vbeInfo.OEMStringPtr) );
85 }
86
87 //==========================================================================
88 // getVESAModeWithProperties
89 //
90 // Return the VESA mode that matches the properties specified.
91 // If a mode is not found, then return the "best" available mode.
92
93 static unsigned short
94 getVESAModeWithProperties( unsigned short width,
95 unsigned short height,
96 unsigned char bitsPerPixel,
97 unsigned short attributesSet,
98 unsigned short attributesClear,
99 VBEModeInfoBlock * outModeInfo,
100 unsigned short * vesaVersion )
101 {
102 VBEInfoBlock vbeInfo;
103 unsigned short * modePtr;
104 VBEModeInfoBlock modeInfo;
105 unsigned char modeBitsPerPixel;
106 unsigned short matchedMode = modeEndOfList;
107 int err;
108
109 // Clear output mode info.
110
111 bzero( outModeInfo, sizeof(*outModeInfo) );
112
113 // Get VBE controller info containing the list of supported modes.
114
115 bzero( &vbeInfo, sizeof(vbeInfo) );
116 err = getVBEInfo( &vbeInfo );
117 if ( err != errSuccess )
118 {
119 return modeEndOfList;
120 }
121
122 // Report the VESA major/minor version number.
123
124 if (vesaVersion) *vesaVersion = vbeInfo.VESAVersion;
125
126 // Loop through the mode list, and find the matching mode.
127
128 for ( modePtr = VBEDecodeFP( unsigned short *, vbeInfo.VideoModePtr );
129 *modePtr != modeEndOfList; modePtr++ )
130 {
131 // Get mode information.
132
133 bzero( &modeInfo, sizeof(modeInfo) );
134 err = getVBEModeInfo( *modePtr, &modeInfo );
135 if ( err != errSuccess )
136 {
137 continue;
138 }
139
140 #if 0 // debug
141 printf("Mode %x: %dx%dx%d mm:%d attr:%x\n",
142 *modePtr, modeInfo.XResolution, modeInfo.YResolution,
143 modeInfo.BitsPerPixel, modeInfo.MemoryModel,
144 modeInfo.ModeAttributes);
145 #endif
146
147 // Filter out unwanted modes based on mode attributes.
148
149 if ( ( ( modeInfo.ModeAttributes & attributesSet ) != attributesSet )
150 || ( ( modeInfo.ModeAttributes & attributesClear ) != 0 ) )
151 {
152 continue;
153 }
154
155 // Pixel depth in bits.
156
157 modeBitsPerPixel = modeInfo.BitsPerPixel;
158
159 if ( ( modeBitsPerPixel == 4 ) && ( modeInfo.MemoryModel == 0 ) )
160 {
161 // Text mode, 16 colors.
162 }
163 else if ( ( modeBitsPerPixel == 8 ) && ( modeInfo.MemoryModel == 4 ) )
164 {
165 // Packed pixel, 256 colors.
166 }
167 else if ( ( ( modeBitsPerPixel == 16 ) || ( modeBitsPerPixel == 15 ) )
168 && ( modeInfo.MemoryModel == 6 )
169 && ( modeInfo.RedMaskSize == 5 )
170 && ( modeInfo.GreenMaskSize == 5 )
171 && ( modeInfo.BlueMaskSize == 5 ) )
172 {
173 // Direct color, 16 bpp (1:5:5:5).
174 modeInfo.BitsPerPixel = modeBitsPerPixel = 16;
175 }
176 else if ( ( modeBitsPerPixel == 32 )
177 && ( modeInfo.MemoryModel == 6 )
178 && ( modeInfo.RedMaskSize == 8 )
179 && ( modeInfo.GreenMaskSize == 8 )
180 && ( modeInfo.BlueMaskSize == 8 ) )
181 {
182 // Direct color, 32 bpp (8:8:8:8).
183 }
184 else
185 {
186 continue; // Not a supported mode.
187 }
188
189 // Modes larger than the specified dimensions are skipped.
190
191 if ( ( modeInfo.XResolution > width ) ||
192 ( modeInfo.YResolution > height ) )
193 {
194 continue;
195 }
196
197 // Perfect match, we're done looking.
198
199 if ( ( modeInfo.XResolution == width ) &&
200 ( modeInfo.YResolution == height ) &&
201 ( modeBitsPerPixel == bitsPerPixel ) )
202 {
203 matchedMode = *modePtr;
204 bcopy( &modeInfo, outModeInfo, sizeof(modeInfo) );
205 break;
206 }
207
208 // Save the next "best" mode in case a perfect match is not found.
209
210 if ( modeInfo.XResolution == outModeInfo->XResolution &&
211 modeInfo.YResolution == outModeInfo->YResolution &&
212 modeBitsPerPixel <= outModeInfo->BitsPerPixel )
213 {
214 continue; // Saved mode has more depth.
215 }
216 if ( modeInfo.XResolution < outModeInfo->XResolution ||
217 modeInfo.YResolution < outModeInfo->YResolution ||
218 modeBitsPerPixel < 16 )
219 {
220 continue; // Saved mode has more resolution.
221 }
222
223 matchedMode = *modePtr;
224 bcopy( &modeInfo, outModeInfo, sizeof(modeInfo) );
225 }
226
227 return matchedMode;
228 }
229
230 //==========================================================================
231 // setupPalette
232
233 static void setupPalette( VBEPalette * p, const unsigned char * g )
234 {
235 int i;
236 unsigned char * source = (unsigned char *) g;
237
238 for (i = 0; i < 256; i++)
239 {
240 (*p)[i] = 0;
241 (*p)[i] |= ((unsigned long)((*source++) >> 2)) << 16; // Red
242 (*p)[i] |= ((unsigned long)((*source++) >> 2)) << 8; // Green
243 (*p)[i] |= ((unsigned long)((*source++) >> 2)); // Blue
244 }
245 }
246
247 //==========================================================================
248 // Simple decompressor for boot images encoded in RLE format.
249
250 static char * decodeRLE( const void * rleData, int rleBlocks, int outBytes )
251 {
252 char *out, *cp;
253
254 struct RLEBlock {
255 unsigned char count;
256 unsigned char value;
257 } * bp = (struct RLEBlock *) rleData;
258
259 out = cp = (char *) malloc( outBytes );
260 if ( out == NULL ) return NULL;
261
262 while ( rleBlocks-- )
263 {
264 memset( cp, bp->value, bp->count );
265 cp += bp->count;
266 bp++;
267 }
268
269 return out;
270 }
271
272 //==========================================================================
273 // setVESAGraphicsMode
274
275 static int
276 setVESAGraphicsMode( unsigned short width,
277 unsigned short height,
278 unsigned char bitsPerPixel,
279 unsigned short refreshRate )
280 {
281 VBEModeInfoBlock minfo;
282 unsigned short mode;
283 unsigned short vesaVersion;
284 int err = errFuncNotSupported;
285
286 do {
287 mode = getVESAModeWithProperties( width, height, bitsPerPixel,
288 maColorModeBit |
289 maModeIsSupportedBit |
290 maGraphicsModeBit |
291 maLinearFrameBufferAvailBit,
292 0,
293 &minfo, &vesaVersion );
294 if ( mode == modeEndOfList )
295 {
296 break;
297 }
298
299 if ( (vesaVersion >> 8) >= 3 && refreshRate >= 60 &&
300 (gBootMode & kBootModeSafe) == 0 )
301 {
302 VBECRTCInfoBlock timing;
303
304 // Generate CRTC timing for given refresh rate.
305
306 generateCRTCTiming( minfo.XResolution, minfo.YResolution,
307 refreshRate, kCRTCParamRefreshRate,
308 &timing );
309
310 // Find the actual pixel clock supported by the hardware.
311
312 getVBEPixelClock( mode, &timing.PixelClock );
313
314 // Re-compute CRTC timing based on actual pixel clock.
315
316 generateCRTCTiming( minfo.XResolution, minfo.YResolution,
317 timing.PixelClock, kCRTCParamPixelClock,
318 &timing );
319
320 // Set the video mode and use specified CRTC timing.
321
322 err = setVBEMode( mode | kLinearFrameBufferBit |
323 kCustomRefreshRateBit, &timing );
324 }
325 else
326 {
327 // Set the mode with default refresh rate.
328
329 err = setVBEMode( mode | kLinearFrameBufferBit, NULL );
330 }
331 if ( err != errSuccess )
332 {
333 break;
334 }
335
336 // Set 8-bit color palette.
337
338 if ( minfo.BitsPerPixel == 8 )
339 {
340 VBEPalette palette;
341 setupPalette( &palette, appleClut8 );
342 if ((err = setVBEPalette(palette)) != errSuccess)
343 {
344 break;
345 }
346 }
347
348 // Is this required for buggy Video BIOS implementations?
349 // On which adapter?
350
351 if ( minfo.BytesPerScanline == 0 )
352 minfo.BytesPerScanline = ( minfo.XResolution *
353 minfo.BitsPerPixel ) >> 3;
354
355 // Update KernBootStruct using info provided by the selected
356 // VESA mode.
357
358 bootArgs->graphicsMode = GRAPHICS_MODE;
359 bootArgs->video.v_width = minfo.XResolution;
360 bootArgs->video.v_height = minfo.YResolution;
361 bootArgs->video.v_depth = minfo.BitsPerPixel;
362 bootArgs->video.v_rowBytes = minfo.BytesPerScanline;
363 bootArgs->video.v_baseAddr = VBEMakeUInt32(minfo.PhysBasePtr);
364 }
365 while ( 0 );
366
367 return err;
368 }
369
370 //==========================================================================
371 // drawBootGraphics
372
373 static int
374 drawBootGraphics( unsigned short width,
375 unsigned short height,
376 unsigned char bitsPerPixel,
377 unsigned short refreshRate )
378 {
379 VBEModeInfoBlock minfo;
380 unsigned short mode;
381 unsigned short vesaVersion;
382 int err = errFuncNotSupported;
383
384 char * appleBoot = 0;
385 short * appleBoot16;
386 long * appleBoot32;
387 long cnt, x, y;
388 char * appleBootPict;
389
390 do {
391 mode = getVESAModeWithProperties( width, height, bitsPerPixel,
392 maColorModeBit |
393 maModeIsSupportedBit |
394 maGraphicsModeBit |
395 maLinearFrameBufferAvailBit,
396 0,
397 &minfo, &vesaVersion );
398 if ( mode == modeEndOfList )
399 {
400 break;
401 }
402
403 // Fill the background to 75% grey (same as BootX).
404
405 drawColorRectangle( 0, 0, minfo.XResolution, minfo.YResolution,
406 0x01 /* color index */ );
407
408 appleBootPict = decodeRLE( gAppleBootPictRLE, kAppleBootRLEBlocks,
409 kAppleBootWidth * kAppleBootHeight );
410
411 // Prepare the data for the happy mac.
412
413 if ( appleBootPict )
414 {
415 switch ( VIDEO(depth) )
416 {
417 case 16 :
418 appleBoot16 = malloc(kAppleBootWidth * kAppleBootHeight * 2);
419 if ( !appleBoot16 ) break;
420 for (cnt = 0; cnt < (kAppleBootWidth * kAppleBootHeight); cnt++)
421 appleBoot16[cnt] = lookUpCLUTIndex(appleBootPict[cnt], 16);
422 appleBoot = (char *) appleBoot16;
423 break;
424
425 case 32 :
426 appleBoot32 = malloc(kAppleBootWidth * kAppleBootHeight * 4);
427 if ( !appleBoot32 ) break;
428 for (cnt = 0; cnt < (kAppleBootWidth * kAppleBootHeight); cnt++)
429 appleBoot32[cnt] = lookUpCLUTIndex(appleBootPict[cnt], 32);
430 appleBoot = (char *) appleBoot32;
431 break;
432
433 default :
434 appleBoot = (char *) appleBootPict;
435 break;
436 }
437
438 x = ( VIDEO(width) - kAppleBootWidth ) / 2;
439 y = ( VIDEO(height) - kAppleBootHeight ) / 2 + kAppleBootOffset;
440
441 // Draw the happy mac in the center of the display.
442
443 if ( appleBoot )
444 {
445 drawDataRectangle( x, y, kAppleBootWidth, kAppleBootHeight,
446 appleBoot );
447 }
448
449 free( appleBootPict );
450 }
451 } while (0);
452
453 return err;
454 }
455
456 //==========================================================================
457 // LookUpCLUTIndex
458
459 static unsigned long lookUpCLUTIndex( unsigned char index,
460 unsigned char depth )
461 {
462 long result, red, green, blue;
463
464 red = appleClut8[index * 3 + 0];
465 green = appleClut8[index * 3 + 1];
466 blue = appleClut8[index * 3 + 2];
467
468 switch (depth) {
469 case 16 :
470 result = ((red & 0xF8) << 7) |
471 ((green & 0xF8) << 2) |
472 ((blue & 0xF8) >> 3);
473 result |= (result << 16);
474 break;
475
476 case 32 :
477 result = (red << 16) | (green << 8) | blue;
478 break;
479
480 default :
481 result = index | (index << 8);
482 result |= (result << 16);
483 break;
484 }
485
486 return result;
487 }
488
489 //==========================================================================
490 // drawColorRectangle
491
492 static void * stosl(void * dst, long val, long len)
493 {
494 asm( "rep; stosl"
495 : "=c" (len), "=D" (dst)
496 : "0" (len), "1" (dst), "a" (val)
497 : "memory" );
498
499 return dst;
500 }
501
502 static void drawColorRectangle( unsigned short x,
503 unsigned short y,
504 unsigned short width,
505 unsigned short height,
506 unsigned char colorIndex )
507 {
508 long pixelBytes;
509 long color = lookUpCLUTIndex( colorIndex, VIDEO(depth) );
510 char * vram;
511
512 pixelBytes = VIDEO(depth) / 8;
513 vram = (char *) VIDEO(baseAddr) +
514 VIDEO(rowBytes) * y + pixelBytes * x;
515
516 while ( height-- )
517 {
518 int rem = ( pixelBytes * width ) % 4;
519 if ( rem ) bcopy( &color, vram, rem );
520 stosl( vram + rem, color, pixelBytes * width / 4 );
521 vram += VIDEO(rowBytes);
522 }
523 }
524
525 //==========================================================================
526 // drawDataRectangle
527
528 static void drawDataRectangle( unsigned short x,
529 unsigned short y,
530 unsigned short width,
531 unsigned short height,
532 unsigned char * data )
533 {
534 long pixelBytes = VIDEO(depth) / 8;
535 char * vram = (char *) VIDEO(baseAddr) +
536 VIDEO(rowBytes) * y + pixelBytes * x;
537
538 while ( height-- )
539 {
540 bcopy( data, vram, width * pixelBytes );
541 vram += VIDEO(rowBytes);
542 data += width * pixelBytes;
543 }
544 }
545
546 //==========================================================================
547 // setVESATextMode
548
549 static int
550 setVESATextMode( unsigned short cols,
551 unsigned short rows,
552 unsigned char bitsPerPixel )
553 {
554 VBEModeInfoBlock minfo;
555 unsigned short mode = modeEndOfList;
556
557 if ( (cols != 80) || (rows != 25) ) // not 80x25 mode
558 {
559 mode = getVESAModeWithProperties( cols, rows, bitsPerPixel,
560 maColorModeBit |
561 maModeIsSupportedBit,
562 maGraphicsModeBit,
563 &minfo, NULL );
564 }
565
566 if ( ( mode == modeEndOfList ) || ( setVBEMode(mode, NULL) != errSuccess ) )
567 {
568 video_mode( 2 ); // VGA BIOS, 80x25 text mode.
569 minfo.XResolution = 80;
570 minfo.YResolution = 25;
571 }
572
573 // Update KernBootStruct using info provided by the selected
574 // VESA mode.
575
576 bootArgs->graphicsMode = TEXT_MODE;
577 bootArgs->video.v_baseAddr = 0xb8000;
578 bootArgs->video.v_width = minfo.XResolution;
579 bootArgs->video.v_height = minfo.YResolution;
580 bootArgs->video.v_depth = 8;
581 bootArgs->video.v_rowBytes = 0x8000;
582
583 return errSuccess; // always return success
584 }
585
586 //==========================================================================
587 // getNumberArrayFromProperty
588
589 static int
590 getNumberArrayFromProperty( const char * propKey,
591 unsigned long numbers[],
592 unsigned long maxArrayCount )
593 {
594 char * propStr;
595 unsigned long count = 0;
596
597 #define _isdigit(c) ((c) >= '0' && (c) <= '9')
598
599 propStr = newStringForKey( (char *) propKey );
600 if ( propStr )
601 {
602 char * delimiter = propStr;
603 char * p = propStr;
604
605 while ( count < maxArrayCount && *p != '\0' )
606 {
607 unsigned long val = strtoul( p, &delimiter, 10 );
608 if ( p != delimiter )
609 {
610 numbers[count++] = val;
611 p = delimiter;
612 }
613 while ( ( *p != '\0' ) && !_isdigit(*p) )
614 p++;
615 }
616
617 free( propStr );
618 }
619
620 return count;
621 }
622
623 //==========================================================================
624 // setVideoMode
625 //
626 // Set the video mode to TEXT_MODE or GRAPHICS_MODE.
627
628 void
629 setVideoMode( int mode )
630 {
631 unsigned long params[4];
632 int count;
633 int err = errSuccess;
634
635 if ( mode == GRAPHICS_MODE )
636 {
637 params[3] = 0;
638 count = getNumberArrayFromProperty( kGraphicsModeKey, params, 4 );
639 if ( count < 3 )
640 {
641 params[0] = 1024; // Default graphics mode is 1024x768x16.
642 params[1] = 768;
643 params[2] = 16;
644 }
645
646 // Map from pixel format to bits per pixel.
647
648 if ( params[2] == 256 ) params[2] = 8;
649 if ( params[2] == 555 ) params[2] = 16;
650 if ( params[2] == 888 ) params[2] = 32;
651
652 err = setVESAGraphicsMode( params[0], params[1], params[2], params[3] );
653 if ( err == errSuccess )
654 {
655 // If this boolean is set to true, then the console driver
656 // in the kernel will show the animated color wheel on the
657 // upper left corner.
658
659 bootArgs->video.v_display = !gVerboseMode;
660
661 if (!gVerboseMode) {
662 drawBootGraphics( params[0], params[1], params[2], params[3] );
663 }
664 }
665 }
666
667 if ( (mode == TEXT_MODE) || (err != errSuccess) )
668 {
669 count = getNumberArrayFromProperty( kTextModeKey, params, 2 );
670 if ( count < 2 )
671 {
672 params[0] = 80; // Default text mode is 80x25.
673 params[1] = 25;
674 }
675
676 setVESATextMode( params[0], params[1], 4 );
677 bootArgs->video.v_display = 0;
678 }
679
680 currentIndicator = 0;
681 }
682
683 //==========================================================================
684 // Return the current video mode, TEXT_MODE or GRAPHICS_MODE.
685
686 int getVideoMode(void)
687 {
688 return bootArgs->graphicsMode;
689 }
690
691 //==========================================================================
692 // Display and clear the activity indicator.
693
694 static char indicator[] = {'-', '\\', '|', '/', '-', '\\', '|', '/', '\0'};
695
696 // To prevent a ridiculously fast-spinning indicator,
697 // ensure a minimum of 1/9 sec between animation frames.
698 #define MIN_TICKS 2
699
700 void
701 spinActivityIndicator( void )
702 {
703 static unsigned long lastTickTime = 0;
704 unsigned long currentTickTime = time18();
705 static char string[3] = {'\0', '\b', '\0'};
706
707 if (currentTickTime < lastTickTime + MIN_TICKS)
708 return;
709 else
710 lastTickTime = currentTickTime;
711
712 if ( getVideoMode() == TEXT_MODE )
713 {
714 string[0] = indicator[currentIndicator];
715 printf(string);
716 if (indicator[++currentIndicator] == 0)
717 currentIndicator = 0;
718 }
719 }
720
721 void
722 clearActivityIndicator( void )
723 {
724 if ( getVideoMode() == TEXT_MODE )
725 {
726 printf(" \b");
727 }
728 }