* All rights reserved.
*/
-#include "libsaio.h"
#include "boot.h"
#include "vbe.h"
-#include "kernBootStruct.h"
-
-#define CHAR_W 8
-#define CHAR_W_SHIFT 3
-#define CHAR_H 16
-#define CHAR_H_SHIFT 4
-#define BYTE_SHIFT 3
-#define NCOLS (screen_width / CHAR_W)
-#define NROWS (screen_height / CHAR_H)
-#define SCREEN_W (screen_width)
-#define SCREEN_H (screen_height)
+#include "appleClut8.h"
+#include "appleboot.h"
+#include "bootstruct.h"
/*
- * Forward declarations.
+ * for spinning disk
*/
-static int convert_vbe_mode(char * mode_name, int * mode);
-BOOL gSilentBoot;
+static int currentIndicator = 0;
+
+static unsigned long lookUpCLUTIndex( unsigned char index,
+ unsigned char depth );
+
+static void drawColorRectangle( unsigned short x,
+ unsigned short y,
+ unsigned short width,
+ unsigned short height,
+ unsigned char colorIndex );
+
+static void drawDataRectangle( unsigned short x,
+ unsigned short y,
+ unsigned short width,
+ unsigned short height,
+ unsigned char * data );
+
+#define VIDEO(x) (bootArgs->video.v_ ## x)
+
//==========================================================================
-// Display a (optionally centered) text message.
+// printVBEInfo
-void
-message(char * str, int centered)
+void printVBEInfo()
+{
+ VBEInfoBlock vbeInfo;
+ int err;
+
+ // Fetch VBE Controller Info.
+
+ bzero( &vbeInfo, sizeof(vbeInfo) );
+ err = getVBEInfo( &vbeInfo );
+ if ( err != errSuccess )
+ return;
+
+ // Check presence of VESA signature.
+
+ if ( strncmp( vbeInfo.VESASignature, "VESA", 4 ) )
+ return;
+
+ // Announce controller properties.
+
+ printf("VESA v%d.%d %dMB (%s)\n",
+ vbeInfo.VESAVersion >> 8,
+ vbeInfo.VESAVersion & 0xf,
+ vbeInfo.TotalMemory / 16,
+ VBEDecodeFP(const char *, vbeInfo.OEMStringPtr) );
+}
+
+//==========================================================================
+// getVESAModeWithProperties
+//
+// Return the VESA mode that matches the properties specified.
+// If a mode is not found, then return the "best" available mode.
+
+static unsigned short
+getVESAModeWithProperties( unsigned short width,
+ unsigned short height,
+ unsigned char bitsPerPixel,
+ unsigned short attributesSet,
+ unsigned short attributesClear,
+ VBEModeInfoBlock * outModeInfo,
+ unsigned short * vesaVersion )
{
- register int x;
+ VBEInfoBlock vbeInfo;
+ unsigned short * modePtr;
+ VBEModeInfoBlock modeInfo;
+ unsigned char modeBitsPerPixel;
+ unsigned short matchedMode = modeEndOfList;
+ int err;
+
+ // Clear output mode info.
+
+ bzero( outModeInfo, sizeof(*outModeInfo) );
+
+ // Get VBE controller info containing the list of supported modes.
+
+ bzero( &vbeInfo, sizeof(vbeInfo) );
+ err = getVBEInfo( &vbeInfo );
+ if ( err != errSuccess )
+ {
+ return modeEndOfList;
+ }
+
+ // Report the VESA major/minor version number.
- x = (NCOLS - strlen(str)) >> 1;
+ if (vesaVersion) *vesaVersion = vbeInfo.VESAVersion;
- if ( currentMode() == TEXT_MODE )
+ // Loop through the mode list, and find the matching mode.
+
+ for ( modePtr = VBEDecodeFP( unsigned short *, vbeInfo.VideoModePtr );
+ *modePtr != modeEndOfList; modePtr++ )
{
- if (centered)
+ // Get mode information.
+
+ bzero( &modeInfo, sizeof(modeInfo) );
+ err = getVBEModeInfo( *modePtr, &modeInfo );
+ if ( err != errSuccess )
+ {
+ continue;
+ }
+
+#if 0 // debug
+ printf("Mode %x: %dx%dx%d mm:%d attr:%x\n",
+ *modePtr, modeInfo.XResolution, modeInfo.YResolution,
+ modeInfo.BitsPerPixel, modeInfo.MemoryModel,
+ modeInfo.ModeAttributes);
+#endif
+
+ // Filter out unwanted modes based on mode attributes.
+
+ if ( ( ( modeInfo.ModeAttributes & attributesSet ) != attributesSet )
+ || ( ( modeInfo.ModeAttributes & attributesClear ) != 0 ) )
+ {
+ continue;
+ }
+
+ // Pixel depth in bits.
+
+ modeBitsPerPixel = modeInfo.BitsPerPixel;
+
+ if ( ( modeBitsPerPixel == 4 ) && ( modeInfo.MemoryModel == 0 ) )
+ {
+ // Text mode, 16 colors.
+ }
+ else if ( ( modeBitsPerPixel == 8 ) && ( modeInfo.MemoryModel == 4 ) )
+ {
+ // Packed pixel, 256 colors.
+ }
+ else if ( ( ( modeBitsPerPixel == 16 ) || ( modeBitsPerPixel == 15 ) )
+ && ( modeInfo.MemoryModel == 6 )
+ && ( modeInfo.RedMaskSize == 5 )
+ && ( modeInfo.GreenMaskSize == 5 )
+ && ( modeInfo.BlueMaskSize == 5 ) )
+ {
+ // Direct color, 16 bpp (1:5:5:5).
+ modeInfo.BitsPerPixel = modeBitsPerPixel = 16;
+ }
+ else if ( ( modeBitsPerPixel == 32 )
+ && ( modeInfo.MemoryModel == 6 )
+ && ( modeInfo.RedMaskSize == 8 )
+ && ( modeInfo.GreenMaskSize == 8 )
+ && ( modeInfo.BlueMaskSize == 8 ) )
+ {
+ // Direct color, 32 bpp (8:8:8:8).
+ }
+ else
+ {
+ continue; // Not a supported mode.
+ }
+
+ // Modes larger than the specified dimensions are skipped.
+
+ if ( ( modeInfo.XResolution > width ) ||
+ ( modeInfo.YResolution > height ) )
{
- while(x--) printf(" ");
+ continue;
}
- printf("%s\n", str);
- }
+
+ // Perfect match, we're done looking.
+
+ if ( ( modeInfo.XResolution == width ) &&
+ ( modeInfo.YResolution == height ) &&
+ ( modeBitsPerPixel == bitsPerPixel ) )
+ {
+ matchedMode = *modePtr;
+ bcopy( &modeInfo, outModeInfo, sizeof(modeInfo) );
+ break;
+ }
+
+ // Save the next "best" mode in case a perfect match is not found.
+
+ if ( modeInfo.XResolution == outModeInfo->XResolution &&
+ modeInfo.YResolution == outModeInfo->YResolution &&
+ modeBitsPerPixel <= outModeInfo->BitsPerPixel )
+ {
+ continue; // Saved mode has more depth.
+ }
+ if ( modeInfo.XResolution < outModeInfo->XResolution ||
+ modeInfo.YResolution < outModeInfo->YResolution ||
+ modeBitsPerPixel < 16 )
+ {
+ continue; // Saved mode has more resolution.
+ }
+
+ matchedMode = *modePtr;
+ bcopy( &modeInfo, outModeInfo, sizeof(modeInfo) );
+ }
+
+ return matchedMode;
}
-/*
- * for spinning disk
- */
-static int currentIndicator = 0;
+//==========================================================================
+// setupPalette
+
+static void setupPalette( VBEPalette * p, const unsigned char * g )
+{
+ int i;
+ unsigned char * source = (unsigned char *) g;
+
+ for (i = 0; i < 256; i++)
+ {
+ (*p)[i] = 0;
+ (*p)[i] |= ((unsigned long)((*source++) >> 2)) << 16; // Red
+ (*p)[i] |= ((unsigned long)((*source++) >> 2)) << 8; // Green
+ (*p)[i] |= ((unsigned long)((*source++) >> 2)); // Blue
+ }
+}
//==========================================================================
-// Set the screen mode: TEXT_MODE or GRAPHICS_MODE
+// Simple decompressor for boot images encoded in RLE format.
-void
-setMode(int mode)
+static char * decodeRLE( const void * rleData, int rleBlocks, int outBytes )
{
- unsigned int vmode;
- char * vmode_name;
- int err = errSuccess;
+ char *out, *cp;
+
+ struct RLEBlock {
+ unsigned char count;
+ unsigned char value;
+ } * bp = (struct RLEBlock *) rleData;
- if ( currentMode() == mode ) return;
+ out = cp = (char *) malloc( outBytes );
+ if ( out == NULL ) return NULL;
- if ( mode == GRAPHICS_MODE &&
- (vmode_name = newStringForKey(kGraphicsModeKey)) != 0)
+ while ( rleBlocks-- )
{
- // Set to the graphics mode specified in the config table file,
- // enable linear frame buffer mode, and update kernBootStruct.
+ memset( cp, bp->value, bp->count );
+ cp += bp->count;
+ bp++;
+ }
- if ( convert_vbe_mode(vmode_name, &vmode) == 0 )
- vmode = mode1024x768x256; /* default mode */
+ return out;
+}
- err = set_linear_video_mode(vmode);
+//==========================================================================
+// setVESAGraphicsMode
- if ( err == errSuccess )
+static int
+setVESAGraphicsMode( unsigned short width,
+ unsigned short height,
+ unsigned char bitsPerPixel,
+ unsigned short refreshRate )
+{
+ VBEModeInfoBlock minfo;
+ unsigned short mode;
+ unsigned short vesaVersion;
+ int err = errFuncNotSupported;
+
+ do {
+ mode = getVESAModeWithProperties( width, height, bitsPerPixel,
+ maColorModeBit |
+ maModeIsSupportedBit |
+ maGraphicsModeBit |
+ maLinearFrameBufferAvailBit,
+ 0,
+ &minfo, &vesaVersion );
+ if ( mode == modeEndOfList )
+ {
+ break;
+ }
+
+ if ( (vesaVersion >> 8) >= 3 && refreshRate >= 60 &&
+ (gBootMode & kBootModeSafe) == 0 )
+ {
+ VBECRTCInfoBlock timing;
+
+ // Generate CRTC timing for given refresh rate.
+
+ generateCRTCTiming( minfo.XResolution, minfo.YResolution,
+ refreshRate, kCRTCParamRefreshRate,
+ &timing );
+
+ // Find the actual pixel clock supported by the hardware.
+
+ getVBEPixelClock( mode, &timing.PixelClock );
+
+ // Re-compute CRTC timing based on actual pixel clock.
+
+ generateCRTCTiming( minfo.XResolution, minfo.YResolution,
+ timing.PixelClock, kCRTCParamPixelClock,
+ &timing );
+
+ // Set the video mode and use specified CRTC timing.
+
+ err = setVBEMode( mode | kLinearFrameBufferBit |
+ kCustomRefreshRateBit, &timing );
+ }
+ else
+ {
+ // Set the mode with default refresh rate.
+
+ err = setVBEMode( mode | kLinearFrameBufferBit, NULL );
+ }
+ if ( err != errSuccess )
+ {
+ break;
+ }
+
+ // Set 8-bit color palette.
+
+ if ( minfo.BitsPerPixel == 8 )
{
- kernBootStruct->graphicsMode = GRAPHICS_MODE;
- kernBootStruct->video.v_display = gSilentBoot;
- kernBootStruct->video.v_baseAddr = (unsigned long) frame_buffer;
- kernBootStruct->video.v_width = SCREEN_W;
- kernBootStruct->video.v_height = SCREEN_H;
- kernBootStruct->video.v_depth = bits_per_pixel;
- kernBootStruct->video.v_rowBytes = (screen_rowbytes == 0) ?
- (SCREEN_W * bits_per_pixel) >> BYTE_SHIFT : screen_rowbytes;
+ VBEPalette palette;
+ setupPalette( &palette, appleClut8 );
+ if ((err = setVBEPalette(palette)) != errSuccess)
+ {
+ break;
+ }
}
- free(vmode_name);
+ // Is this required for buggy Video BIOS implementations?
+ // On which adapter?
+
+ if ( minfo.BytesPerScanline == 0 )
+ minfo.BytesPerScanline = ( minfo.XResolution *
+ minfo.BitsPerPixel ) >> 3;
+
+ // Update KernBootStruct using info provided by the selected
+ // VESA mode.
+
+ bootArgs->graphicsMode = GRAPHICS_MODE;
+ bootArgs->video.v_width = minfo.XResolution;
+ bootArgs->video.v_height = minfo.YResolution;
+ bootArgs->video.v_depth = minfo.BitsPerPixel;
+ bootArgs->video.v_rowBytes = minfo.BytesPerScanline;
+ bootArgs->video.v_baseAddr = VBEMakeUInt32(minfo.PhysBasePtr);
}
+ while ( 0 );
+
+ return err;
+}
+
+//==========================================================================
+// drawBootGraphics
+
+static int
+drawBootGraphics( unsigned short width,
+ unsigned short height,
+ unsigned char bitsPerPixel,
+ unsigned short refreshRate )
+{
+ VBEModeInfoBlock minfo;
+ unsigned short mode;
+ unsigned short vesaVersion;
+ int err = errFuncNotSupported;
+
+ char * appleBoot = 0;
+ short * appleBoot16;
+ long * appleBoot32;
+ long cnt, x, y;
+ char * appleBootPict;
+
+ do {
+ mode = getVESAModeWithProperties( width, height, bitsPerPixel,
+ maColorModeBit |
+ maModeIsSupportedBit |
+ maGraphicsModeBit |
+ maLinearFrameBufferAvailBit,
+ 0,
+ &minfo, &vesaVersion );
+ if ( mode == modeEndOfList )
+ {
+ break;
+ }
+
+ // Fill the background to 75% grey (same as BootX).
+
+ drawColorRectangle( 0, 0, minfo.XResolution, minfo.YResolution,
+ 0x01 /* color index */ );
+
+ appleBootPict = decodeRLE( gAppleBootPictRLE, kAppleBootRLEBlocks,
+ kAppleBootWidth * kAppleBootHeight );
+
+ // Prepare the data for the happy mac.
+
+ if ( appleBootPict )
+ {
+ switch ( VIDEO(depth) )
+ {
+ case 16 :
+ appleBoot16 = malloc(kAppleBootWidth * kAppleBootHeight * 2);
+ if ( !appleBoot16 ) break;
+ for (cnt = 0; cnt < (kAppleBootWidth * kAppleBootHeight); cnt++)
+ appleBoot16[cnt] = lookUpCLUTIndex(appleBootPict[cnt], 16);
+ appleBoot = (char *) appleBoot16;
+ break;
- if ( (mode == TEXT_MODE) || (err != errSuccess) )
+ case 32 :
+ appleBoot32 = malloc(kAppleBootWidth * kAppleBootHeight * 4);
+ if ( !appleBoot32 ) break;
+ for (cnt = 0; cnt < (kAppleBootWidth * kAppleBootHeight); cnt++)
+ appleBoot32[cnt] = lookUpCLUTIndex(appleBootPict[cnt], 32);
+ appleBoot = (char *) appleBoot32;
+ break;
+
+ default :
+ appleBoot = (char *) appleBootPict;
+ break;
+ }
+
+ x = ( VIDEO(width) - kAppleBootWidth ) / 2;
+ y = ( VIDEO(height) - kAppleBootHeight ) / 2 + kAppleBootOffset;
+
+ // Draw the happy mac in the center of the display.
+
+ if ( appleBoot )
+ {
+ drawDataRectangle( x, y, kAppleBootWidth, kAppleBootHeight,
+ appleBoot );
+ }
+
+ free( appleBootPict );
+ }
+ } while (0);
+
+ return err;
+}
+
+//==========================================================================
+// LookUpCLUTIndex
+
+static unsigned long lookUpCLUTIndex( unsigned char index,
+ unsigned char depth )
+{
+ long result, red, green, blue;
+
+ red = appleClut8[index * 3 + 0];
+ green = appleClut8[index * 3 + 1];
+ blue = appleClut8[index * 3 + 2];
+
+ switch (depth) {
+ case 16 :
+ result = ((red & 0xF8) << 7) |
+ ((green & 0xF8) << 2) |
+ ((blue & 0xF8) >> 3);
+ result |= (result << 16);
+ break;
+
+ case 32 :
+ result = (red << 16) | (green << 8) | blue;
+ break;
+
+ default :
+ result = index | (index << 8);
+ result |= (result << 16);
+ break;
+ }
+
+ return result;
+}
+
+//==========================================================================
+// drawColorRectangle
+
+static void * stosl(void * dst, long val, long len)
+{
+ asm( "rep; stosl"
+ : "=c" (len), "=D" (dst)
+ : "0" (len), "1" (dst), "a" (val)
+ : "memory" );
+
+ return dst;
+}
+
+static void drawColorRectangle( unsigned short x,
+ unsigned short y,
+ unsigned short width,
+ unsigned short height,
+ unsigned char colorIndex )
+{
+ long pixelBytes;
+ long color = lookUpCLUTIndex( colorIndex, VIDEO(depth) );
+ char * vram;
+
+ pixelBytes = VIDEO(depth) / 8;
+ vram = (char *) VIDEO(baseAddr) +
+ VIDEO(rowBytes) * y + pixelBytes * x;
+
+ while ( height-- )
{
- video_mode(2); // 80x25 text mode
-
- kernBootStruct->graphicsMode = TEXT_MODE;
- kernBootStruct->video.v_display = 0;
- kernBootStruct->video.v_baseAddr = (unsigned long) 0xb8000;
- kernBootStruct->video.v_width = 80;
- kernBootStruct->video.v_height = 25;
- kernBootStruct->video.v_depth = 8;
- kernBootStruct->video.v_rowBytes = 0x8000;
+ int rem = ( pixelBytes * width ) % 4;
+ if ( rem ) bcopy( &color, vram, rem );
+ stosl( vram + rem, color, pixelBytes * width / 4 );
+ vram += VIDEO(rowBytes);
}
+}
- currentIndicator = 0;
+//==========================================================================
+// drawDataRectangle
+
+static void drawDataRectangle( unsigned short x,
+ unsigned short y,
+ unsigned short width,
+ unsigned short height,
+ unsigned char * data )
+{
+ long pixelBytes = VIDEO(depth) / 8;
+ char * vram = (char *) VIDEO(baseAddr) +
+ VIDEO(rowBytes) * y + pixelBytes * x;
+
+ while ( height-- )
+ {
+ bcopy( data, vram, width * pixelBytes );
+ vram += VIDEO(rowBytes);
+ data += width * pixelBytes;
+ }
}
//==========================================================================
-// Return the current screen mode, TEXT_MODE or GRAPHICS_MODE.
+// setVESATextMode
-int currentMode(void)
+static int
+setVESATextMode( unsigned short cols,
+ unsigned short rows,
+ unsigned char bitsPerPixel )
{
- return kernBootStruct->graphicsMode;
+ VBEModeInfoBlock minfo;
+ unsigned short mode = modeEndOfList;
+
+ if ( (cols != 80) || (rows != 25) ) // not 80x25 mode
+ {
+ mode = getVESAModeWithProperties( cols, rows, bitsPerPixel,
+ maColorModeBit |
+ maModeIsSupportedBit,
+ maGraphicsModeBit,
+ &minfo, NULL );
+ }
+
+ if ( ( mode == modeEndOfList ) || ( setVBEMode(mode, NULL) != errSuccess ) )
+ {
+ video_mode( 2 ); // VGA BIOS, 80x25 text mode.
+ minfo.XResolution = 80;
+ minfo.YResolution = 25;
+ }
+
+ // Update KernBootStruct using info provided by the selected
+ // VESA mode.
+
+ bootArgs->graphicsMode = TEXT_MODE;
+ bootArgs->video.v_baseAddr = 0xb8000;
+ bootArgs->video.v_width = minfo.XResolution;
+ bootArgs->video.v_height = minfo.YResolution;
+ bootArgs->video.v_depth = 8;
+ bootArgs->video.v_rowBytes = 0x8000;
+
+ return errSuccess; // always return success
}
//==========================================================================
-// Convert from a string describing a graphics mode, to a VGA mode number.
-
-typedef struct {
- char mode_name[15];
- int mode_val;
-} mode_table_t;
-
-mode_table_t mode_table[] = {
- { "640x400x256", mode640x400x256 },
- { "640x480x256", mode640x480x256 },
- { "800x600x16", mode800x600x16 },
- { "800x600x256", mode800x600x256 },
- { "1024x768x16", mode1024x768x16 },
- { "1024x768x256", mode1024x768x256 },
- { "1280x1024x16", mode1280x1024x16 },
- { "1280x1024x256", mode1280x1024x256 },
- { "640x480x555", mode640x480x555 },
- { "640x480x888", mode640x480x888 },
- { "800x600x555", mode800x600x555 },
- { "800x600x888", mode800x600x888 },
- { "1024x768x555", mode1024x768x555 },
- { "1024x768x888", mode1024x768x888 },
- { "1280x1024x555", mode1280x1024x555 },
- { "1280x1024x888", mode1280x1024x888 },
- { "", 0 }
-};
-
-int convert_vbe_mode(char * mode_name, int * mode)
-{
- mode_table_t * mtp = mode_table;
-
- if (mode_name == 0 || *mode_name == 0)
- return 0;
-
- while ( *mtp->mode_name )
+// getNumberArrayFromProperty
+
+static int
+getNumberArrayFromProperty( const char * propKey,
+ unsigned long numbers[],
+ unsigned long maxArrayCount )
+{
+ char * propStr;
+ unsigned long count = 0;
+
+#define _isdigit(c) ((c) >= '0' && (c) <= '9')
+
+ propStr = newStringForKey( (char *) propKey );
+ if ( propStr )
{
- if (strcmp(mtp->mode_name, mode_name) == 0)
+ char * delimiter = propStr;
+ char * p = propStr;
+
+ while ( count < maxArrayCount && *p != '\0' )
{
- *mode = mtp->mode_val;
- return 1;
+ unsigned long val = strtoul( p, &delimiter, 10 );
+ if ( p != delimiter )
+ {
+ numbers[count++] = val;
+ p = delimiter;
+ }
+ while ( ( *p != '\0' ) && !_isdigit(*p) )
+ p++;
}
- mtp++;
+
+ free( propStr );
}
- return 0;
+
+ return count;
+}
+
+//==========================================================================
+// setVideoMode
+//
+// Set the video mode to TEXT_MODE or GRAPHICS_MODE.
+
+void
+setVideoMode( int mode )
+{
+ unsigned long params[4];
+ int count;
+ int err = errSuccess;
+
+ if ( mode == GRAPHICS_MODE )
+ {
+ params[3] = 0;
+ count = getNumberArrayFromProperty( kGraphicsModeKey, params, 4 );
+ if ( count < 3 )
+ {
+ params[0] = 1024; // Default graphics mode is 1024x768x16.
+ params[1] = 768;
+ params[2] = 16;
+ }
+
+ // Map from pixel format to bits per pixel.
+
+ if ( params[2] == 256 ) params[2] = 8;
+ if ( params[2] == 555 ) params[2] = 16;
+ if ( params[2] == 888 ) params[2] = 32;
+
+ err = setVESAGraphicsMode( params[0], params[1], params[2], params[3] );
+ if ( err == errSuccess )
+ {
+ // If this boolean is set to true, then the console driver
+ // in the kernel will show the animated color wheel on the
+ // upper left corner.
+
+ bootArgs->video.v_display = !gVerboseMode;
+
+ if (!gVerboseMode) {
+ drawBootGraphics( params[0], params[1], params[2], params[3] );
+ }
+ }
+ }
+
+ if ( (mode == TEXT_MODE) || (err != errSuccess) )
+ {
+ count = getNumberArrayFromProperty( kTextModeKey, params, 2 );
+ if ( count < 2 )
+ {
+ params[0] = 80; // Default text mode is 80x25.
+ params[1] = 25;
+ }
+
+ setVESATextMode( params[0], params[1], 4 );
+ bootArgs->video.v_display = 0;
+ }
+
+ currentIndicator = 0;
+}
+
+//==========================================================================
+// Return the current video mode, TEXT_MODE or GRAPHICS_MODE.
+
+int getVideoMode(void)
+{
+ return bootArgs->graphicsMode;
}
//==========================================================================
return;
else
lastTickTime = currentTickTime;
-
- if ( currentMode() == TEXT_MODE )
+
+ if ( getVideoMode() == TEXT_MODE )
{
string[0] = indicator[currentIndicator];
printf(string);
void
clearActivityIndicator( void )
{
- if ( currentMode() == TEXT_MODE )
+ if ( getVideoMode() == TEXT_MODE )
{
printf(" \b");
}