]> git.saurik.com Git - apple/boot.git/blobdiff - i386/boot2/graphics.c
boot-111.1.tar.gz
[apple/boot.git] / i386 / boot2 / graphics.c
index db91fe503e44178941b30c2a7f663459be560b60..d5359b9a4a4d43dbe80aef7888ca2fb85ed8f7f1 100644 (file)
  * 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;
 }
 
 //==========================================================================
@@ -201,8 +708,8 @@ spinActivityIndicator( void )
         return;
     else
         lastTickTime = currentTickTime;
-       
-    if ( currentMode() == TEXT_MODE )
+
+    if ( getVideoMode() == TEXT_MODE )
     {
         string[0] = indicator[currentIndicator];
         printf(string);
@@ -214,7 +721,7 @@ spinActivityIndicator( void )
 void
 clearActivityIndicator( void )
 {
-    if ( currentMode() == TEXT_MODE )
+    if ( getVideoMode() == TEXT_MODE )
     {
         printf(" \b");
     }