Logo Search packages:      
Sourcecode: faumachine version File versions  Download package

cirrus_vga.c

/* $Id: cirrus_vga.c,v 1.30 2009-02-26 08:22:58 sand Exp $ 
 *
 * Cirrus BIOS extensions and Cirrus VESA 2.0 implementation.
 *
 * Copyright (C) 2006-2009 FAUmachine Team <info@faumachine.org>.
 * This program is free software. You can redistribute it and/or modify it
 * under the terms of the GNU General Public License, either version 2 of
 * the License, or (at your option) any later version. See COPYING.
 *
 * Inspiration and (most) register sets came from lgpl vgabios' cirrus
 * logic extension. Thanks for that!
 */

/******************************************************************************/
/*                                                                            */
/* Includes                                                                   */
/*                                                                            */
/******************************************************************************/

#include "compiler.h"
CODE16;

#include "assert.h"
#include "stdio.h"

#include "main.h"
#include "io.h"
#include "const.h"
#include "var.h"
#include "ptrace.h"
#include "vga.h"
#include "vesa.h"     /* doubles as cirrus_vga.h */

/******************************************************************************/
/*                                                                            */
/* Debugging                                                                  */
/*                                                                            */
/******************************************************************************/

/*
 * #define CIRRUS_WARNINGS
 */

#define CIRRUS_DEBUG_VESA_0          (1 << 0)
#define CIRRUS_DEBUG_VESA_0_DETAILS  (1 << 1)
#define CIRRUS_DEBUG_VESA_1          (1 << 2)
#define CIRRUS_DEBUG_VESA_1_DETAILS  (1 << 3)
#define CIRRUS_DEBUG_VESA_2          (1 << 4)
#define CIRRUS_DEBUG_VESA_2_DETAILS  (1 << 5)
#define CIRRUS_DEBUG_VESA_3          (1 << 6)
#define CIRRUS_DEBUG_VESA_4          (1 << 8)
#define CIRRUS_DEBUG_VESA_4_DETAILS  (1 << 9)
#define CIRRUS_DEBUG_VESA_5          (1 << 10)
#define CIRRUS_DEBUG_VESA_5_DETAILS  (1 << 11)
#define CIRRUS_DEBUG_VESA_6          (1 << 12)
#define CIRRUS_DEBUG_VESA_6_DETAILS  (1 << 13)
#define CIRRUS_DEBUG_VESA_7          (1 << 14)
#define CIRRUS_DEBUG_VESA_7_DETAILS  (1 << 15)
#define CIRRUS_DEBUG_VESA_9          (1 << 16)
#define CIRRUS_DEBUG_VESA_9_DETAILS  (1 << 17)
#define CIRRUS_DEBUG_VESA_B          (1 << 18)
#define CIRRUS_DEBUG_VESA_10         (1 << 19)
#define CIRRUS_DEBUG_VESA_10_DETAILS (1 << 20)
#define CIRRUS_DEBUG_VESA_15         (1 << 21)
#define CIRRUS_DEBUG_VESA_15_DETAILS (1 << 22)

#define CIRRUS_DEBUG ( \
 0 )

/*
 CIRRUS_DEBUG_VESA_0 | \
 CIRRUS_DEBUG_VESA_1 | \
 CIRRUS_DEBUG_VESA_2 | \
 CIRRUS_DEBUG_VESA_3 | \
 CIRRUS_DEBUG_VESA_4 | \
 CIRRUS_DEBUG_VESA_5 | \
 CIRRUS_DEBUG_VESA_6 | \
 CIRRUS_DEBUG_VESA_7 | \
 CIRRUS_DEBUG_VESA_9 | \
 CIRRUS_DEBUG_VESA_B | \
 CIRRUS_DEBUG_VESA_10 | \
 CIRRUS_DEBUG_VESA_15 | \
 CIRRUS_DEBUG_VESA_0_DETAILS | \
 CIRRUS_DEBUG_VESA_1_DETAILS | \
 CIRRUS_DEBUG_VESA_2_DETAILS | \
 CIRRUS_DEBUG_VESA_4_DETAILS | \
 CIRRUS_DEBUG_VESA_5_DETAILS | \
 CIRRUS_DEBUG_VESA_6_DETAILS | \
 CIRRUS_DEBUG_VESA_7_DETAILS | \
 CIRRUS_DEBUG_VESA_9_DETAILS | \
 CIRRUS_DEBUG_VESA_10_DETAILS | \
 CIRRUS_DEBUG_VESA_15_DETAILS | \
 */

#define CIRRUS_DEBUG_DUMP_REGS dprintf("  AX = 0x%04x\n", AX); \
            dprintf("  BX = 0x%04x\n", BX); \
            dprintf("  CX = 0x%04x\n", CX); \
            dprintf("  DX = 0x%04x\n", DX); \
            dprintf("  ES:DI = %04x:%04x\n", ES, DI)


/******************************************************************************/
/*                                                                            */
/* Defines                                                                    */
/*                                                                            */
/******************************************************************************/

/*
 * VESA defined return values
 *
 * AL == 4Fh: Function is supported
 * AL != 4Fh: Function is not supported
 * AH == 00h: Function call successful
 * AH == 01h: Function call failed
 * AH == 02h: Function is not supported in the current hardware configuration
 * AH == 03h: Function call invalid in current video mode
 */
#define VBE_SUCCESS       0x004f
#define VBE_ERROR         0x014f
#define VBE_UNIMPLEMENTED 0x0100
#define VBE_NOHARDWARE    0x0200

/* Accessing the PCI configuration space */
#define PCI_CONFIG_ADDRESS_REGISTER 0xcf8
#define PCI_CONFIG_DATA_REGISTER    0xcfc
#define PCI_BUS 0
#define CIRRUS_DEVICE_VENDOR_ID 0x00b81013

/*
 * This is a special legacy vga fallback mode. It will be requested if
 * VESA function 02 is called with a mode number < 0x100.
 * _cirrus_find_miblock() will then return a pointer to the last entry of
 * cirrus_mode_infos[], that is the index corresponding to the termination
 * of cirrus_cirrus_mode_list[].
 */
#define CIRRUS_FALLBACK_MODE 0xffff

/******************************************************************************/
/*                                                                            */
/* VESA extension data structures                                             */
/*                                                                            */
/******************************************************************************/

/*
 * List of supported VESA modes
 * Don't change order, indices corresponds to cirrus_mode_infos[]
 * and cirrus_cirrus_modes[]
 */
static CONST unsigned short cirrus_vesa_modes[] = {

        0x101,    /* 640x480x8    */
        0x110,    /* 640x480x15   */
        0x111,    /* 640x480x16   */
        0x112,    /* 640x480x24   */

        0x103,    /* 800x600x8    */
        0x113,    /* 800x600x15   */
        0x114,    /* 800x600x16   */
        0x115,    /* 800x600x24   */

        0x105,    /* 1024x768x8   */
        0x116,    /* 1024x768x15  */
        0x117,    /* 1024x768x16  */
        0x118,    /* 1024x768x24  */

        0x107,    /* 1280x1024x8  */
        0x119,    /* 1280x1024x15 */
        0x11a,    /* 1280x1024x16 */

        0xffff    /* termination  */
};

/*
 * List of supported Cirrus modes (thanks vgadoc4b)
 * Don't change order, indices corresponds to cirrus_mode_infos[]
 * and cirrus_vesa_modes[]
 */
static CONST unsigned short cirrus_cirrus_modes[] = {

        0x5f,    /* 640x480x8    */
        0x66,    /* 640x480x15   */
        0x64,    /* 640x480x16   */
        0x71,    /* 640x480x24   */

        0x5c,    /* 800x600x8    */
        0x67,    /* 800x600x15   */
        0x65,    /* 800x600x16   */
        0x78,    /* 800x600x24   */

        0x60,    /* 1024x768x8   */
        0x68,    /* 1024x768x15  */
        0x74,    /* 1024x768x16  */
        0x79,    /* 1024x768x24  */

        0x6d,    /* 1280x1024x8  */
        0x69,    /* 1280x1024x15 */
        0x75,    /* 1280x1024x16 */

        0x7c,    /* 1152x864x8   */

        0xffff   /* termination  */
};

/*
 * Informational strings returned by VESA function 00
 */
static CONST char cirrus_vesa_oem_string[] = "FAUmachine Cirrus VESA extension";
static CONST char cirrus_vesa_vendor_name[] = "FAUmachine";
static CONST char cirrus_vesa_product_name[] = "Cirrus VESA extension";
static CONST char cirrus_vesa_product_revision[] = "1.0";

/*
 * Beginning part of VESA defined mode info structure
 */
struct cirrus_vesa_mode_info {

        /* Mandatory information for all VBE revisions */
        unsigned short  ModeAttributes;         /* 0x003b */
        unsigned char   WinAAttributes;         /* 0x07   */
        unsigned char   WinBAttributes;         /* 0x00   */
        unsigned short  WinGranularity;         /* 0x0010 */
        unsigned short  WinSize;                /* 0x0040 */
        unsigned short  WinASegment;            /* 0xa000 */
        unsigned short  WinBSegment;            /* 0x0000 */
        unsigned long   WinFuncPtr;             /* 4f01() */
        unsigned short  BytesPerScanLine;       /* 4f01() */

        /* Mandatory information for VBE 1.2 and above */
        unsigned short  XResolution;            /* 4f01() */
        unsigned short  YResolution;            /* 4f01() */
        unsigned char   XCharSize;              /* 0x08   */
        unsigned char   YCharSize;              /* 0x10   */
        unsigned char   NumberOfPlanes;         /* 0x01   */
        unsigned char   BitsPerPixel;           /* 4f01() */
        unsigned char   NumberOfBanks;          /* 0x01   */
        unsigned char   MemoryModel;            /* 4f01() */
        unsigned char   BankSize;               /* 0x00   */
        unsigned char   NumberOfImagePages;     /* 4f01() */
        unsigned char   Reserved;               /* 0x01   */

        /*
         * "Direct Color fields" and "mandatory information for VBE 2.0
         * and above" set by bios_10_4f01()
         */

} PACKED;

/*
 * Template for all VESA defined mode info structures
 */
static CONST struct cirrus_vesa_mode_info cirrus_vesa_mode_info = {

        /*
         * ModeAttributes:
         *
         * [0] Mode supported in hardware
         * [1] Extended mode information available
         * [2] TTY Output functions not supported by BIOS
         * [3] Color mode
         *
         * [4] Mode type = Graphics mode
         * [5] VGA compatible mode = No
         * [6] VGA compatible windowed memory mode is available = Yes
         * [7] Linear frame buffer mode is available = No
         */
        0x003b,

        /* WinAAttributes: supported, readable and writeable */
        0x07,
        /* WinBAttributes: not supported */
        0x00,

        /* WinGranularity: 16kb */
        0x0010,
        /* WinSize: 64kb */
        0x0040,

        /* WinASegment at A000h (real mode segment value) */
        0xa000,
        /* No WinBSegment */
        0x0000,

        /* WinFuncPtr: segment and offset */
        0x00000000,     /* NULL, VBE function 05h must be used */

        /* BytesPerScanLine */
        0x0000,         /* set by bios_10_4f01() */

        /* XResolution */
        0x0000,         /* set by bios_10_4f01() */
        /* YResolution */
        0x0000,         /* set by bios_10_4f01() */

        /* XCharSize: 8 */
        0x08,
        /* YCharSize: 16 */
        0x10,

        /* NumberOfPlanes: 1 */
        0x01,
        /* BitsPerPixel */
        0x00,           /* set by bios_10_4f01() */

        /* NumberOfBanks: 1, no scanline banks */
        0x01,

        /* MemoryModel */
        0x00,           /* set by bios_10_4f01() */

        /* BankSize: 0, no scanline banks */
        0x00,
        /*
         * NumberOfImagePages:
         * (frame buffer memory / rounded complete display image size) - 1
         */
        0x00,           /* set by bios_10_4f01() */
        /* Reserved: 1 */
        0x01
};

/*
 * This direct color field structure is
 * a component of VESA defined mode info structure
 */
struct cirrus_direct_color_field {

        unsigned char RedMaskSize;            /* 31 */
        unsigned char RedFieldPosition;       /* 32 */
        unsigned char GreenMaskSize;          /* 33 */
        unsigned char GreenFieldPosition;     /* 34 */
        unsigned char BlueMaskSize;           /* 35 */
        unsigned char BlueFieldPosition;      /* 36 */
        unsigned char RsvdMaskSize;           /* 37 */
        unsigned char RsvdFieldPosition;      /* 38 */
        unsigned char DirectColorModeInfo;    /* 39 */

} PACKED;

/*
 * All four possible direct color fields
 */
static CONST struct cirrus_direct_color_field
cirrus_direct_color_fields[] = {

        /* rm, rp,gm,gp,bm,bp,rsvdm,rsvdp */
        {   0,  0, 0, 0, 0, 0,    0,    0}, /*  8bpp */
        {   5, 10, 5, 5, 5, 0,    1,   15}, /* 15bpp */
        {   5, 11, 6, 5, 5, 0,    0,    0}, /* 16bpp */
        {   8, 16, 8, 8, 8, 0,    0,    0}  /* 24bpp */
};

/*
 * Value sets for the sequencer (0x3c4), graphics (0x3ce) and
 * crt controller (0x3d4) registers terminated with 0xffff
 * Low byte = index
 * High byte = value
 */
static CONST unsigned short cirrus_sequencer_register_set_vga[] = {
        0x0007, 0xffff
};
static CONST unsigned short cirrus_graphics_register_set_vga[] = {
        0x0009, 0x000a, 0x000b, 0xffff
};
static CONST unsigned short cirrus_crtc_register_set_vga[] = {
        0x001a, 0x001b, 0x001d, 0xffff
};

static CONST unsigned short cirrus_graphics_register_set_svgacolor[] = {
        0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x4005, 0x0506, 0x0f07, 0xff08,
        0x0009, 0x000a, 0x000b,
        0xffff
};

static CONST unsigned short cirrus_sequencer_register_set_640x480x8[] = {
        0x0300, 0x2101, 0x0f02, 0x0003, 0x0e04, 0x1107,
        0x580b, 0x580c, 0x580d, 0x580e,
        0x0412, 0x0013, 0x2017,
        0x331b, 0x331c, 0x331d, 0x331e,
        0xffff
};
static CONST unsigned short cirrus_crtc_register_set_640x480x8[] = {
        0x2c11, 0x5f00, 0x4f01, 0x4f02, 0x8003, 0x5204, 0x1e05, 0x0b06, 0x3e07,
        0x4009, 0x000c, 0x000d,
        0xea10, 0xdf12, 0x5013, 0x4014, 0xdf15, 0x0b16, 0xc317, 0xff18,
        0x001a, 0x221b, 0x001d,
        0xffff
};

static CONST unsigned short cirrus_sequencer_register_set_640x480x16[] = {
        0x0300, 0x2101, 0x0f02, 0x0003, 0x0e04, 0x1707,
        0x580b, 0x580c, 0x580d, 0x580e,
        0x0412, 0x0013, 0x2017,
        0x331b, 0x331c, 0x331d, 0x331e,
        0xffff
};
static CONST unsigned short cirrus_crtc_register_set_640x480x16[] = {
        0x2c11, 0x5f00, 0x4f01, 0x4f02, 0x8003, 0x5204, 0x1e05, 0x0b06, 0x3e07,
        0x4009, 0x000c, 0x000d,
        0xea10, 0xdf12, 0xa013, 0x4014, 0xdf15, 0x0b16, 0xc317, 0xff18,
        0x001a, 0x221b, 0x001d,
        0xffff
};

static CONST unsigned short cirrus_sequencer_register_set_640x480x24[] = {
        0x0300, 0x2101, 0x0f02, 0x0003, 0x0e04, 0x1507,
        0x580b, 0x580c, 0x580d, 0x580e,
        0x0412, 0x0013, 0x2017,
        0x331b, 0x331c, 0x331d, 0x331e,
        0xffff
};
static CONST unsigned short cirrus_crtc_register_set_640x480x24[] = {
        0x2c11, 0x5f00, 0x4f01, 0x4f02, 0x8003, 0x5204, 0x1e05, 0x0b06, 0x3e07,
        0x4009, 0x000c, 0x000d,
        0xea10, 0xdf12, 0x0013, 0x4014, 0xdf15, 0x0b16, 0xc317, 0xff18,
        0x001a, 0x321b, 0x001d,
        0xffff
};

static CONST unsigned short cirrus_sequencer_register_set_800x600x8[] = {
        0x0300, 0x2101, 0x0f02, 0x0003, 0x0e04, 0x1107,
        0x230b, 0x230c, 0x230d, 0x230e,
        0x0412, 0x0013, 0x2017,
        0x141b, 0x141c, 0x141d, 0x141e,
        0xffff
};
static CONST unsigned short cirrus_crtc_register_set_800x600x8[] = {
        0x2311, 0x7d00, 0x6301, 0x6302, 0x8003, 0x6b04, 0x1a05, 0x9806, 0xf007,
        0x6009, 0x000c, 0x000d,
        0x7d10, 0x5712, 0x6413, 0x4014, 0x5715, 0x9816, 0xc317, 0xff18,
        0x001a, 0x221b, 0x001d,
        0xffff
};

static CONST unsigned short cirrus_sequencer_register_set_800x600x16[] = {
        0x0300, 0x2101, 0x0f02, 0x0003, 0x0e04, 0x1707,
        0x230b, 0x230c, 0x230d, 0x230e,
        0x0412, 0x0013, 0x2017,
        0x141b, 0x141c, 0x141d, 0x141e,
        0xffff
};
static CONST unsigned short cirrus_crtc_register_set_800x600x16[] = {
        0x2311, 0x7d00, 0x6301, 0x6302, 0x8003, 0x6b04, 0x1a05, 0x9806, 0xf007,
        0x6009, 0x000c, 0x000d,
        0x7d10, 0x5712, 0xc813, 0x4014, 0x5715, 0x9816, 0xc317, 0xff18,
        0x001a, 0x221b, 0x001d,
        0xffff
};

static CONST unsigned short cirrus_sequencer_register_set_800x600x24[] = {
        0x0300, 0x2101, 0x0f02, 0x0003, 0x0e04, 0x1507,
        0x230b, 0x230c, 0x230d, 0x230e,
        0x0412, 0x0013, 0x2017,
        0x141b, 0x141c, 0x141d, 0x141e,
        0xffff
};
static CONST unsigned short cirrus_crtc_register_set_800x600x24[] = {
        0x2311, 0x7d00, 0x6301, 0x6302, 0x8003, 0x6b04, 0x1a05, 0x9806, 0xf007,
        0x6009, 0x000c, 0x000d,
        0x7d10, 0x5712, 0x2c13, 0x4014, 0x5715, 0x9816, 0xc317, 0xff18,
        0x001a, 0x321b, 0x001d,
        0xffff
};

static CONST unsigned short cirrus_sequencer_register_set_1024x768x8[] = {
        0x0300, 0x2101, 0x0f02, 0x0003, 0x0e04, 0x1107,
        0x760b, 0x760c, 0x760d, 0x760e,
        0x0412, 0x0013, 0x2017,
        0x341b, 0x341c, 0x341d, 0x341e,
        0xffff
};
static CONST unsigned short cirrus_crtc_register_set_1024x768x8[] = {
        0x2911, 0xa300, 0x7f01, 0x7f02, 0x8603, 0x8304, 0x9405, 0x2406, 0xf507,
        0x6009, 0x000c, 0x000d,
        0x0310, 0xff12, 0x8013, 0x4014, 0xff15, 0x2416, 0xc317, 0xff18,
        0x001a, 0x221b, 0x001d,
        0xffff
};

static CONST unsigned short cirrus_sequencer_register_set_1024x768x16[] = {
        0x0300, 0x2101, 0x0f02, 0x0003, 0x0e04, 0x1707,
        0x760b, 0x760c, 0x760d, 0x760e,
        0x0412, 0x0013, 0x2017,
        0x341b, 0x341c, 0x341d, 0x341e,
        0xffff
};
static CONST unsigned short cirrus_crtc_register_set_1024x768x16[] = {
        0x2911, 0xa300, 0x7f01, 0x7f02, 0x8603, 0x8304, 0x9405, 0x2406, 0xf507,
        0x6009, 0x000c, 0x000d,
        0x0310, 0xff12, 0x0013, 0x4014, 0xff15, 0x2416, 0xc317, 0xff18,
        0x001a, 0x321b, 0x001d,
        0xffff
};

static CONST unsigned short cirrus_sequencer_register_set_1024x768x24[] = {
        0x0300, 0x2101, 0x0f02, 0x0003, 0x0e04, 0x1507,
        0x760b, 0x760c, 0x760d, 0x760e,
        0x0412, 0x0013, 0x2017,
        0x341b, 0x341c, 0x341d, 0x341e,
        0xffff
};
static CONST unsigned short cirrus_crtc_register_set_1024x768x24[] = {
        0x2911, 0xa300, 0x7f01, 0x7f02, 0x8603, 0x8304, 0x9405, 0x2406, 0xf507,
        0x6009, 0x000c, 0x000d,
        0x0310, 0xff12, 0x8013, 0x4014, 0xff15, 0x2416, 0xc317, 0xff18,
        0x001a, 0x321b, 0x001d,
        0xffff
};

static CONST unsigned short cirrus_sequencer_register_set_1280x1024x8[] = {
        0x0300, 0x2101, 0x0f02, 0x0003, 0x0e04, 0x1107,
        0x760b, 0x760c, 0x760d, 0x760e,
        0x0412, 0x0013, 0x2017,
        0x341b, 0x341c, 0x341d, 0x341e,
        0xffff
};
static CONST unsigned short cirrus_crtc_register_set_1280x1024x8[] = {
        0x2911, 0xc300, 0x9f01, 0x9f02, 0x8603, 0x8304, 0x9405, 0x2406, 0xf707,
        0x6009, 0x000c, 0x000d,
        0x0310, 0xff12, 0xa013, 0x4014, 0xff15, 0x2416, 0xc317, 0xff18,
        0x001a, 0x221b, 0x001d,
        0xffff
};

static CONST unsigned short cirrus_sequencer_register_set_1280x1024x16[] = {
        0x0300, 0x2101, 0x0f02, 0x0003, 0x0e04, 0x1707,
        0x760b, 0x760c, 0x760d, 0x760e,
        0x0412, 0x0013, 0x2017,
        0x341b, 0x341c, 0x341d, 0x341e,
        0xffff
};
static CONST unsigned short cirrus_crtc_register_set_1280x1024x16[] = {
        0x2911, 0xc300, 0x9f01, 0x9f02, 0x8603, 0x8304, 0x9405, 0x2406, 0xf707,
        0x6009, 0x000c, 0x000d,
        0x0310, 0xff12, 0x4013, 0x4014, 0xff15, 0x2416, 0xc317, 0xff18,
        0x001a, 0x321b, 0x001d,
        0xffff
};

static CONST unsigned short cirrus_sequencer_register_set_1152x864x8[] = {
        0x0300, 0x2101, 0x0f02, 0x0003, 0x0e04, 0x1107,
        0x760b, 0x760c, 0x760d, 0x760e,
        0x0412, 0x0013, 0x2017,
        0x341b, 0x341c, 0x341d, 0x341e,
        0xffff
};
static CONST unsigned short cirrus_crtc_register_set_1152x864x8[] = {
        0x2911, 0xaf00, 0x8f01, 0x8f02, 0x8603, 0x8304, 0x9405, 0x2406, 0xf707,
        0x6009, 0x000c, 0x000d,
        0x0310, 0x5f12, 0x9013, 0x4014, 0xff15, 0x2416, 0xc317, 0xff18,
        0x001a, 0x221b, 0x001d,
        0xffff
};

/*
 * Cirrus specific mode information structure
 */
struct cirrus_mode_info {

        unsigned short mode;           /* 0 VESA mode */
        unsigned short width;          /* 2 XResolution */
        unsigned short height;         /* 4 YResolution */
        unsigned short bits_per_pixel; /* 6 BitsPerPixel */

        unsigned char  memory_model;   /* 8 MemoryModel */
        unsigned char  hidden_dac;     /* 9 0x3c6 */

        CONST unsigned short *sequencer_register_set;  /* 10 0x3c4 */
        CONST unsigned short *graphics_register_set;   /* 14 0x3ce */
        CONST unsigned short *crtc_register_set;       /* 18 0x3d4 */

} PACKED;

/*
 * List of supported cirrus modes
 * don't change order, indexes correspond to cirrus_vesa_modes[]
 * and cirrus_cirrus_modes[]
 */
static CONST struct cirrus_mode_info cirrus_mode_infos[] = {

        /*
         * mode, width, height, bpp, memmodel, hidden_dac,
         * *seq, *graph, *crtc
         */

        /* 640x480x8 */
        {0x101, 640, 480, 8, 4, 0x00,
         cirrus_sequencer_register_set_640x480x8,
         cirrus_graphics_register_set_svgacolor,
         cirrus_crtc_register_set_640x480x8},
        /* 640x480x15 */
        {0x110, 640, 480, 15, 6, 0xf0,
         cirrus_sequencer_register_set_640x480x16,
         cirrus_graphics_register_set_svgacolor,
         cirrus_crtc_register_set_640x480x16},
        /* 640x480x16 */
        {0x111, 640, 480, 16, 6, 0xe1,
         cirrus_sequencer_register_set_640x480x16,
         cirrus_graphics_register_set_svgacolor,
         cirrus_crtc_register_set_640x480x16},
        /* 640x480x24 */
        {0x112, 640, 480, 24, 6, 0xe5,
         cirrus_sequencer_register_set_640x480x24,
         cirrus_graphics_register_set_svgacolor,
         cirrus_crtc_register_set_640x480x24},

        /* 800x600x8 */
        {0x103, 800, 600, 8, 4, 0x00,
         cirrus_sequencer_register_set_800x600x8,
         cirrus_graphics_register_set_svgacolor,
         cirrus_crtc_register_set_800x600x8},
        /* 800x600x15 */
        {0x113, 800, 600, 15, 6, 0xf0,
         cirrus_sequencer_register_set_800x600x16,
         cirrus_graphics_register_set_svgacolor,
         cirrus_crtc_register_set_800x600x16},
        /* 800x600x16 */
        {0x114, 800, 600, 16, 6, 0xe1,
         cirrus_sequencer_register_set_800x600x16,
         cirrus_graphics_register_set_svgacolor,
         cirrus_crtc_register_set_800x600x16},
        /* 800x600x24 */
        {0x115, 800, 600, 24, 6, 0xe5,
         cirrus_sequencer_register_set_800x600x24,
         cirrus_graphics_register_set_svgacolor,
         cirrus_crtc_register_set_800x600x24},

        /* 1024x768x8 */
        {0x105, 1024, 768, 8, 4, 0x00,
         cirrus_sequencer_register_set_1024x768x8,
         cirrus_graphics_register_set_svgacolor,
         cirrus_crtc_register_set_1024x768x8},
        /* 1024x768x15 */
        {0x116, 1024, 768, 15, 6, 0xf0,
         cirrus_sequencer_register_set_1024x768x16,
         cirrus_graphics_register_set_svgacolor,
         cirrus_crtc_register_set_1024x768x16},
        /* 1024x768x16 */
        {0x117, 1024, 768, 16, 6, 0xe1,
         cirrus_sequencer_register_set_1024x768x16,
         cirrus_graphics_register_set_svgacolor,
         cirrus_crtc_register_set_1024x768x16},
        /* 1024x768x24 */
        {0x118, 1024, 768, 24, 6, 0xe5,
         cirrus_sequencer_register_set_1024x768x24,
         cirrus_graphics_register_set_svgacolor,
         cirrus_crtc_register_set_1024x768x24},

        /* 1280x1024x8 */
        {0x107, 1280, 1024, 8, 4, 0x00,
         cirrus_sequencer_register_set_1280x1024x8,
         cirrus_graphics_register_set_svgacolor,
         cirrus_crtc_register_set_1280x1024x8},
        /* 1280x1024x15 */
        {0x119, 1280, 1024, 15, 6, 0xf0,
         cirrus_sequencer_register_set_1280x1024x16,
         cirrus_graphics_register_set_svgacolor,
         cirrus_crtc_register_set_1280x1024x16},
        /* 1280x1024x16 */
        {0x11a, 1280, 1024, 16, 6, 0xe1,
         cirrus_sequencer_register_set_1280x1024x16,
         cirrus_graphics_register_set_svgacolor,
         cirrus_crtc_register_set_1280x1024x16},

        /* 1152x864x8 */
        {0x07c, 1152, 864, 8, 4, 0x00,
         cirrus_sequencer_register_set_1152x864x8,
         cirrus_graphics_register_set_svgacolor,
         cirrus_crtc_register_set_1152x864x8},

        /* fallback-into-legacy-vga mode */
        {0xffff, 0, 0, 0, 0xff, 0x00,
         cirrus_sequencer_register_set_vga,
         cirrus_graphics_register_set_vga,
         cirrus_crtc_register_set_vga}
};

/*
 * EDID block returned by VESA Function 15:
 * Report DDC Capabilities
 */
static CONST char edidblock[128] = {
        /* EDID Header */
        0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
        /* Vendor Information */
        0x05, 0xb4, /* Manufacturer ID */
        0x30, 0x55, /* Product Code */
        0xbb, 0x02, 0x00, 0x00, /* Serial No. */
        0x29, 0x09, /* Week, Year-1990 */
        /* EDID Structure Versioning */
        0x01, 0x01, /* Version, Revision */
        /* Basic Display Parameters */
        0x1e, /* Video Input Definition: analog */
        36, 27, /* X,Y Size in cm */
        0x78, 0xe8, /* Gamma, Supported Features */
        /* Color Characteristics */
        0x07, 0xb3, 0xa0, 0x57, 0x4a, 0x9a, 0x26, 0x11, 0x48, 0x4f,
        /* Established Timings */
        0xbc, 0xe7, 0x80,
        /* Standard Timing Identification */
        /* First byte
         *   Horizontal resolution.  Multiply by 8, then add 248 for actual value.
         * Second byte
         *   bit 7-6: Aspect ratio.  00=16:10, 01=4:3, 10=5:4, 11=16:9
         *   bit 5-0: Vertical frequeny.  Add 60 to get actual value.
         */
        0xa9, 0x4f, /* 1600x1200 75 Hz */
        0xa9, 0x4a, /* 1600x1200 70 Hz */
        0x61, 0x59, /* 1024x768 85 Hz */
        0x31, 0x59, /* 640x480 85 Hz */
        0x45, 0x59, /* 800x600 85 Hz */
        0x31, 0x4a, /* 640x480 70 Hz */
        0xa9, 0x45, /* 1600x1200 65 Hz */
        0x01, 0x01, /* unused */
        /* Detailed Timing Description #1 */
        0x1a, 0x4f, 0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, 0xc0,
        0x13, 0x00, 0x8e, 0x2b, 0x11, 0x00, 0x00, 0x1e,
        /* Detailed Timing Description #2 */
        0xd4, 0x49, 0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, 0xc0,
        0x13, 0x00, 0x8e, 0x2b, 0x11, 0x00, 0x00, 0x1e,
        /* Detailed Timing Description #3 */
        0xea, 0x24, 0x00, 0x60, 0x41, 0x00, 0x28, 0x30, 0x30, 0x60,
        0x13, 0x00, 0x8e, 0x2b, 0x11, 0x00, 0x00, 0x1e,
        /* Detailed Timing Description #4 */
        0x40, 0x1f, 0x00, 0x30, 0x41, 0x00, 0x24, 0x30, 0x20, 0x60,
        0x33, 0x00, 0x8e, 0x2b, 0x11, 0x00, 0x00, 0x02,
        /* Extension Flag */
        0x00,
        /* Checksum: 1-byte sum of all 128 bytes in block must be 0 */
        0xc3
};

/*
 * map memory size codes saved in scratch pad register 3
 * to the number of 64k blocks that make up the memory
 */
static CONST uint8_t memory_size_map[6] = {
      0x00, /* 0 is undefined */
      0x08, /* 1 = 512kB =  8 blocks, usually unused with the GD5446 */
      0x10, /* 2 =   1MB = 16 blocks */
      0x20, /* 3 =   2MB = 32 blocks */
      0x40, /* 4 =   4MB = 64 blocks */
      0x30  /* 5 =   3MB = 48 blocks */
};


/******************************************************************************/
/*                                                                            */
/* VESA extension functions                                                   */
/*                                                                            */
/******************************************************************************/

/*
 * VESA Function 00: Return VBE Controller Information
 *
 * In:  AX      = 4f00h
 *      ES:DI   = Pointer to Buffer in which to place VbeInfoBlock structure
 *
 * Out: AX      = VBE Return Status
 */
static void
bios_10_4f00(struct regs *regs)
{
      uint8_t mem;

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_0
        dprintf("Cirrus VESA Function 00 called.\n");
#endif

        if (get_word(ES, (unsigned char *) (DI + 0)) == 0x4256    /* VB */
         && get_word(ES, (unsigned char *) (DI + 2)) == 0x3245) { /* E2 */
                /* VBE 2.0 extended information is desired */
                put_word(ES, (unsigned char *) (DI + 20), 0x0100);   /* Software revision 1.0 */
                put_word(ES, (unsigned char *) (DI + 22),
                        PTR_OFF(&cirrus_vesa_vendor_name));
                put_word(ES, (unsigned char *) (DI + 24),
                        PTR_SEG(&cirrus_vesa_vendor_name));
                put_word(ES, (unsigned char *) (DI + 26),
                        PTR_OFF(&cirrus_vesa_product_name));
                put_word(ES, (unsigned char *) (DI + 28),
                        PTR_SEG(&cirrus_vesa_product_name));
                put_word(ES, (unsigned char *) (DI + 30),
                        PTR_OFF(&cirrus_vesa_product_revision));
                put_word(ES, (unsigned char *) (DI + 32),
                        PTR_SEG(&cirrus_vesa_product_revision));
        }

        put_word(ES, (unsigned char *) (DI + 0), 0x4556);  /* VE */
        put_word(ES, (unsigned char *) (DI + 2), 0x4153);  /* SA */
        put_word(ES, (unsigned char *) (DI + 4), 0x0200);  /* Version 2.0 */

      /* OEMString pointer */
        put_word(ES, (unsigned char *) (DI + 6),
                  PTR_OFF(&cirrus_vesa_oem_string)); 
        put_word(ES, (unsigned char *) (DI + 8),
                  PTR_SEG(&cirrus_vesa_oem_string));

      /* Capabilities */
        put_word(ES, (unsigned char *) (DI + 10), 0x0000);
        put_word(ES, (unsigned char *) (DI + 12), 0x0000);

      /* Modelist pointer */
        put_word(ES, (unsigned char *) (DI + 14), PTR_OFF(&cirrus_vesa_modes));
        put_word(ES, (unsigned char *) (DI + 16), PTR_SEG(&cirrus_vesa_modes));

      outb(0x15, 0x3c4); /* read size of vram from scratch pad 3 */
      mem = inb(0x3c5);
      assert(mem < 6);
        put_word(ES, (unsigned char *) (DI + 18),
                  const_get(memory_size_map[mem]));

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_0_DETAILS
        CIRRUS_DEBUG_DUMP_REGS;
        unsigned short i;
        dprintf("  Wrote following block to ES:DI");
        for (i = 0; i < 34; i += 2) {
                if (!(i % 8)) dprintf("\n    %03d: ", i);
                dprintf("0x%04x ", get_word(ES, (unsigned char *) (DI + i)));
        }
        dprintf("\n");
#endif

        AX = VBE_SUCCESS;
}

/*
 * Returns CRTC offset value * 8
 * Reads CR13 and one bit of CR1B out of cirrus_crtc_register_set_x
 */
static unsigned short
_cirrus_get_display_pitch_entry(CONST struct cirrus_mode_info *miblock)
{
        unsigned short *crtc;
        unsigned short ioword;
        unsigned short result;

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_1_DETAILS
        dprintf("Cirrus BIOS: _cirrus_get_display_pitch_entry\n");
        dprintf("  Searching CR13");
#endif

        /* search CR13 crtc offset entry in miblock */
        crtc = (unsigned short *) const_get(miblock->crtc_register_set);

        while ((ioword = const_get(*crtc++)) != 0xffff) {
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_1_DETAILS
                if (!(i % 4)) dprintf("\n    %03d: ", i);
                dprintf("0x%04x ", ioword);
#endif
                if ((ioword & 0x00ff) == 0x0013) {
                        break;
                }
        }
        if (ioword == 0xffff) {
                dprintf("Cirrus BIOS: FATAL: No CR13 in mode info! ");
                assert(0);
        }

        result = (ioword & 0xff00) >> 5;

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_1_DETAILS
        dprintf("\n  Searching CR1B");
#endif

        /* search CR1b extended display controls entry in cirrus mode info */
        crtc = (unsigned short *) const_get(miblock->crtc_register_set);

        while ((ioword = const_get(*crtc++)) != 0xffff) {
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_1_DETAILS
                if (!(i % 4)) dprintf("\n    %03d: ", i);
                dprintf("0x%04x ", ioword);
#endif
                if ((ioword & 0x00ff) == 0x001b) {
                        break;
                }
        }
        if (ioword == 0xffff) {
                dprintf("Cirrus BIOS: FATAL: No CR1B in mode info! ");
                assert(0);
        }

        result |= (ioword & 0x1000) >> 1;

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_1_DETAILS
        dprintf("\n  Returning %d\n", result);
#endif

        return result;
}

/*
 * Read register "reg" (eg. 8 for PCI class code and revision id)
 * out of pci configuration space of device "device" function "func"
 */
static unsigned long
_cirrus_pci_read(unsigned char device,
                 unsigned char func,
                 unsigned char reg)
{
        unsigned long address;
        unsigned long original;
        unsigned long value;

        address = 0x80000000
                | ((PCI_BUS & 0xff) << 16)
                | ((device & 0x1f) << 11)
                | ((func & 0x07) << 8)
                | (reg & 0xfc);

        original = inl(PCI_CONFIG_ADDRESS_REGISTER);
        outl(address, PCI_CONFIG_ADDRESS_REGISTER);
        value = inl(PCI_CONFIG_DATA_REGISTER);
        outl(original, PCI_CONFIG_ADDRESS_REGISTER);

        return value;
}

/*
 * Read cirrus' config space at addr
 */
static unsigned long
_cirrus_pci_read_cirrus(unsigned char addr)
{
        unsigned char device;
        unsigned long value;

        /* scan all pci devices for the cirrus */
        for (device = 0; device < 0x20; device++) {

                /* read PCI0 device/vendor id */
                value = _cirrus_pci_read(device, 0, 0);

                /* are we at home? */
                if (value == CIRRUS_DEVICE_VENDOR_ID) {

                        value = _cirrus_pci_read(device, 0, addr);
                        return value;
                }
        }

        dprintf("Cirrus BIOS: FATAL: No cirrus card found! ");
        assert(0);
        return 0;
}

/*
 * Returns cirrus vga's pci config register 10
 * (Display memory base address)
 */
static unsigned long
_cirrus_get_lfb_address(void)
{
        unsigned long value;

        /* read PCI10 display memory base address */
        value = _cirrus_pci_read_cirrus(0x10);

        if ((value & 0xfe000000) == 0xfe000000
         || (value & 0xfe000000) == 0x00000000) {

#ifdef CIRRUS_WARNINGS
                dprintf("WARNING: Cirrus BIOS: _cirrus_get_lfb_address: ");
                dprintf("Display Memory Base Address uninitialized?\n");
#endif
                return 0;
        }

        if (value & 1) {

                dprintf("Cirrus BIOS: FATAL: LFB is I/O? ");
                assert(0);
        }

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_1_DETAILS
        dprintf("Cirrus BIOS: _cirrus_get_lfb_address() ");
        dprintf("returns 0x%08x\n", value);
#endif
        return value;
}

/*
 * Returns cirrus_mode_info corresponding to VESA mode "mode"
 */
static const struct cirrus_mode_info *
_cirrus_find_miblock(unsigned short mode)
{
        unsigned short index;

        index = 0;

        if (mode == CIRRUS_FALLBACK_MODE) {
                /* legacy fallback mode requested */

                while (const_get(cirrus_cirrus_modes[index]) != 0xffff) {

                        if (const_get(cirrus_cirrus_modes[index]) == mode) {
                                break;
                        }
                        index++;
                }
        } else if (0x100 <= mode) {
                /* vesa mode requested */

                while (const_get(cirrus_vesa_modes[index]) != 0xffff) {

                        if (const_get(cirrus_vesa_modes[index]) == mode) {
                                break;
                        }
                        index++;
                }
                if (const_get(cirrus_vesa_modes[index]) == 0xffff) {

                        /* vesa mode doesn't exist */
                        return 0;
                }
        } else {
                /* cirrus mode requested */

                while (const_get(cirrus_cirrus_modes[index]) != 0xffff) {

                        if (const_get(cirrus_cirrus_modes[index]) == mode) {
                                break;
                        }
                        index++;
                }
                if (const_get(cirrus_cirrus_modes[index]) == 0xffff) {

                        /* cirrus mode doesn't exist */
                        return 0;
                }
        }

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_1_DETAILS
        dprintf("Cirrus BIOS: _cirrus_find_miblock(): Returning ");
        dprintf("pointer to struct cirrus_mode_info[%d]", index);
        unsigned short i;
        for (i = 0; i < sizeof(struct cirrus_mode_info); i += 2) {
                if (!(i % 8)) dprintf("\n    %03d: ", i);
                dprintf("0x%04x ", get_word(get_cs(), ((CONST unsigned char *) &cirrus_mode_infos[index]) + i));
        }
        dprintf("\n");
#endif

        return (CONST struct cirrus_mode_info *) &cirrus_mode_infos[index];
}

/*
 * VESA Function 01: Return VBE Mode information
 *
 * In:  AX      = 4f01h
 *      CX      = Mode number
 *      ES:DI   = Pointer to ModeInfoBlock structure
 *
 * Out: AX      = VBE Return Status
 */
static void
bios_10_4f01(struct regs *regs)
{
        CONST unsigned char *src;
        unsigned short mode;
        unsigned short i;
        CONST struct cirrus_mode_info *miblock;
        unsigned long display_size;
        unsigned long lfb_addr;
        unsigned short display_pitch;
        unsigned char num_of_pages;
      uint8_t mem;

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_1
        dprintf("Cirrus VESA Function 01 called. CX = 0x%04x\n", CX);
#endif

        /* zero ModeInfoBlock structure out */
        for (i = 0; i < 256; i++) {
                put_byte(ES, (unsigned char *) (DI + i), 0x00);
        }

      /* Correct? FIXME VOSSI */
      if (CX == 0xffff) {
            mode = var_get(vbe_mode);
      } else {
            mode = CX;
      }

        /* find requested miblock */
        miblock = _cirrus_find_miblock(mode);
        if (! miblock) {
#ifdef CIRRUS_WARNINGS
                dprintf("WARNING: Cirrus BIOS: bios_10_4f01: Requested ");
                dprintf("mode 0x%04x doesn't exist!\n", mode);
#endif
                AX = VBE_UNIMPLEMENTED;
                return;
        }

        /* copy generic cirrus_vesa_mode_info block into beginning of ES:DI */
        src = (CONST unsigned char *) &cirrus_vesa_mode_info;
        for (i = 0; i < sizeof(struct cirrus_vesa_mode_info); i++) {
                put_byte(ES, (unsigned char *) (DI + i),
                  get_byte(PTR_SEG(src + i), PTR_OFF(src + i)));
        }

        /* copy direct color field information */
        if (const_get(miblock->bits_per_pixel) == 8) {
                src = (CONST unsigned char *) &cirrus_direct_color_fields[0];
        } else if (const_get(miblock->bits_per_pixel) == 15) {
                src = (CONST unsigned char *) &cirrus_direct_color_fields[1];
        } else if (const_get(miblock->bits_per_pixel) == 16) {
                src = (CONST unsigned char *) &cirrus_direct_color_fields[2];
        } else if (const_get(miblock->bits_per_pixel) == 24) {
                src = (CONST unsigned char *) &cirrus_direct_color_fields[3];
        } else {
                /* mustn't happen */
                dprintf("Cirrus BIOS: FATAL: mode=%d bits_per_pixel=%d\n",
                        mode, const_get(miblock->bits_per_pixel));
                assert(0);
        }
        for (i = 0; i < sizeof(struct cirrus_direct_color_field); i++) {
                put_byte(ES, (unsigned char *)
                        (DI + i + sizeof(struct cirrus_vesa_mode_info)),
                  get_byte(PTR_SEG(src + i), PTR_OFF(src + i)));
        }

        /* WinFuncPtr: segment and offset */
        /* FIXME? Somehow possible? Lack of struct reg ptr? */
#if 0
        put_word(ES, (unsigned char *) (DI + 12), (void (*)()) vesa_05h_callback);
        put_word(ES, (unsigned char *) (DI + 14), get_cs());
#endif

        /* BytesPerScanLine */
        display_pitch = _cirrus_get_display_pitch_entry(miblock);
        put_word(ES, (unsigned char *) (DI + 16), display_pitch);

        /* XResolution */
        put_word(ES, (unsigned char *) (DI + 18), const_get(miblock->width));
        /* YResolution */
        put_word(ES, (unsigned char *) (DI + 20), const_get(miblock->height));

        /* BitsPerPixel */
        put_byte(ES, (unsigned char *) (DI + 25), const_get(miblock->bits_per_pixel));

        /* MemoryModel */
        put_byte(ES, (unsigned char *) (DI + 27), const_get(miblock->memory_model));

        /*
         * NumberOfImagePages:
         * (frame buffer memory / rounded complete display image size) - 1
         */
        display_size = ((unsigned long) display_pitch)
                * ((unsigned long) const_get(miblock->height));
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_1_DETAILS
        dprintf("  display_size = %d\n", display_size);
#endif
        if (display_size & 0x0000ffff) {
                /* round up */
                display_size += 0x10000;
        }
        display_size &= 0xffff0000;
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_1_DETAILS
        dprintf("  display_size rounded = %d\n", display_size);
#endif
      outb(0x15, 0x3c4); /* read size of vram from scratch pad 3 */
      mem = inb(0x3c5);
      assert(mem < 6);
        num_of_pages = ((unsigned char)
                        ((const_get(memory_size_map[mem]) << 16) / display_size)) - 1;
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_1_DETAILS
        dprintf("  num_of_pages = %d\n", num_of_pages);
#endif
        put_byte(ES, (unsigned char *) (DI + 29), num_of_pages);

        /*
         * DirectColorModeInfo field is already zero -> Color ramp not
         * programmable and Bits in Rsvd field reserved
         */

        /* PhysBasePtr: */
        lfb_addr = _cirrus_get_lfb_address();
        if (lfb_addr) {

                put_long(ES, (unsigned char *) (DI + 40), lfb_addr);
                /* set bit 7 of ModeAttributes field, LFB available */
                put_word(ES, (unsigned char *) (DI + 0),
                         get_word(ES, (unsigned char *)
                                      (DI + 0)) | 0x0080);
        }
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_1_DETAILS
        dprintf("  PhysBasePtr = 0x%08x\n",
                get_long(ES, (unsigned char *) (DI + 40)));
#endif

        /* check if LFB in mode number selected but not available */
        if ((mode & 0x4000) && ! lfb_addr) {
#ifdef CIRRUS_WARNINGS
                dprintf("WARNING: Cirrus BIOS: bios_10_4f01: ");
                dprintf("LFB requested but not available\n");
#endif
                AX = VBE_ERROR;
                return;
        }

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_1_DETAILS
        dprintf("  Returning following ModeInfoBlock (first 64 bytes)");
        for (i = 0; i < 64; i += 2) {
                if (!(i % 8)) dprintf("\n    %03d: ", i);
                dprintf("0x%04x ", get_word(ES, (unsigned char *) (DI + i)));
        }
        dprintf("\n");
#endif

        AX = VBE_SUCCESS;
}

static void
_cirrus_switch_mode(CONST struct cirrus_mode_info *miblock)
{
        unsigned short *register_set;
        unsigned short ioword;
        unsigned char dummy;
        unsigned char palette_reg;

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_2
        dprintf("Cirrus BIOS: Switching to mode 0x%04x.\n",
                (const_get(miblock->mode)));
#endif

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_2_DETAILS
        dprintf("  outw-ing sequencer regset to 0x3c4");
#endif

        /* set sequencer registers */
        register_set = (unsigned short *) const_get(miblock->sequencer_register_set);
        while ((ioword = const_get(*register_set++)) != 0xffff) {
                outw(ioword, 0x3c4);
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_2_DETAILS
                if (!(i % 4)) dprintf("\n    %03d: ", i);
                dprintf("0x%04x ", ioword);
#endif
        }

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_2_DETAILS
        dprintf("\n  outw-ing graphics regset to 0x3ce");
#endif

        /* set graphics registers */
        register_set = (unsigned short *) const_get(miblock->graphics_register_set);
        while ((ioword = const_get(*register_set++)) != 0xffff) {
                outw(ioword, 0x3ce);
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_2_DETAILS
                if (!(i % 4)) dprintf("\n    %03d: ", i);
                dprintf("0x%04x ", ioword);
#endif
        }

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_2_DETAILS
        dprintf("\n  outw-ing crtc regset to 0x3d4");
#endif

        /* set crtc registers */
        register_set = (unsigned short *) const_get(miblock->crtc_register_set);
        while ((ioword = const_get(*register_set++)) != 0xffff) {
                outw(ioword, 0x3d4);
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_2_DETAILS
                if (!(i % 4)) dprintf("\n    %03d: ", i);
                dprintf("0x%04x ", ioword);
#endif
        }

        /* set hidden dac */
        outb(0x00, 0x3c6);      /* writing resets hidden dac counter */
        dummy = inb(0x3c6);
        dummy = inb(0x3c6);
        dummy = inb(0x3c6);
        dummy = inb(0x3c6);     /* read 4 times, next access is hidden dac */
        outb(const_get(miblock->hidden_dac), 0x3c6);
        outb(0xff, 0x3c6);      /* enable mask */

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_2_DETAILS
        dprintf("\n  wrote 0x%02x to hidden dac\n", const_get(miblock->hidden_dac));
#endif

        /*
         * Check memory organization and update attribute controller mode
         * register AR10 bits 6 and 0.
         * AR10 bit6: pixel double clock select
         * AR10 bit0: graphics mode
         */
        if (const_get(miblock->memory_model) == 0) {
                /* text mode -> bit 6 and 0 not set */
                dummy = 0x00;

        } else if (const_get(miblock->memory_model) != 3) {
                /* not planar -> bit 0 set */
                dummy = 0x01;

        } else {
                /* planar -> bit 6 and 0 both set */
                dummy = 0x41;
        }

        (void) inb(0x3da);      /* Clear reg/value flag */
        outb(0x10, 0x3c0);      /* Write palette register number */
        palette_reg = inb(0x3c1); /* Read palette register */

        palette_reg = (palette_reg & 0xbe) | dummy;

        (void) inb(0x3da);      /* Clear reg/value flag */
        outb(0x10, 0x3c0);      /* Write palette register number */
        outb(palette_reg, 0x3c0); /* Write modified value back */
}

static void
_cirrus_enable_16k_granularity(void)
{
        unsigned char dummy;

        /* select 8.21 GRB graphics controller mode extensions */
        outb(0x0b, 0x3ce);

        /* enable bit 5, offset granularity */
        dummy = inb(0x3cf);
        dummy |= 0x20;
        outb(dummy, 0x3cf);
}

static void
_cirrus_clear_display(CONST struct cirrus_mode_info *miblock)
{
#ifdef CIRRUS_WARNINGS
        dprintf("WARNING: Cirrus BIOS: ");
        dprintf("Ignoring clear display memory\n");
#endif
}

/*
 * VESA Function 02: Set VBE mode
 *
 * In:  AX      = 4f02h
 *      BX      = VBE mode
 *              bits:
 *              0-8     mode number
 *              9-10    reserved (must be 0)
 *              11      refresh rate (0=current,1=user)
 *              12-13   reserved (must be 0)
 *              14      frame buffer model (0=windowed,1=linear)
 *              15      clear display memory (0=yes,1=no)
 *      ES:DI   = ptr to VBECRTCInfoBlock struct
 *
 * Out: AX      = VBE return status
 *
 * Note: refresh rate is ignored
 */
static void
bios_10_4f02(struct regs *regs)
{
        CONST struct cirrus_mode_info *miblock;

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_2
        dprintf("Cirrus VESA Function 02 called.\n");
#endif
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_2_DETAILS
        CIRRUS_DEBUG_DUMP_REGS;
#endif

        if (BX & 0x3600) {
                /* default zeroes not zero */
                AX = VBE_UNIMPLEMENTED;
#ifdef CIRRUS_WARNINGS
                dprintf("WARNING: Cirrus BIOS: bios_10_4f02: Default ");
                dprintf("zeroes not zero! Ignoring mode set.\n");
#endif
                return;
        }

        if ((BX & 0x1ff) < 0x100) {
                /* legacy mode */

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_2
                dprintf("  Switching to legacy mode...\n");
#endif

                /* switch extension registers back to legacy vga mode */
                miblock = _cirrus_find_miblock(CIRRUS_FALLBACK_MODE);
                _cirrus_switch_mode(miblock);

                outw(BL, 0x03de);
                outw(0, 0x03dc);
                set_mode(BL);   /* in vgabios/main.c */

                AX = VBE_SUCCESS;
                return;
        }

        /* find requested miblock */
        if (!(miblock = _cirrus_find_miblock(BX & 0x1ff))) {
#ifdef CIRRUS_WARNINGS
                dprintf("WARNING: Cirrus BIOS: bios_10_4f02: Couldn't find ");
                dprintf("requested mode in list! Ignoring mode set.\n");
#endif
                AX = VBE_UNIMPLEMENTED;
                return;
        }

        _cirrus_switch_mode(miblock);

        if (!(BX & 0x4000)) {
                /* use windowed framebuffer model */
                _cirrus_enable_16k_granularity();
        }
        /* else use linear framebuffer */

        if (!(BX & 0x8000)) {
                _cirrus_clear_display(miblock);
        }

        /* remember vbe mode */
        var_put(vbe_mode, BX);

        AX = VBE_SUCCESS;
}

/*
 * VESA Function 03: Get current video mode
 *
 * In:  -
 *
 * Out: AX      = VBE return status
 *      BX      = bit 14: linear mode
 *                bit 13: accelerated mode
 *                bit 12-0: video mode
 */
static void
bios_10_4f03(struct regs *regs)
{
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_3
        dprintf("Cirrus VESA Function 03 called.\n");
        dprintf("  Returning 0x%04x\n", var_get(vbe_mode));
#endif

        BX = var_get(vbe_mode);

        AX = VBE_SUCCESS;
}

/*
 * VESA Function 04: Save/restore state
 *
 * In:  DL      = 0: Get size of state buffer
 *                1: Save video state
 *                2: Restore video state
 *      ES:BX   = Pointer to video state buffer
 *      CX      = bit3: SuperVGA register state
 *                bit2: DAC state
 *                bit1: BIOS data state
 *                bit0: Video hardware state
 *
 * Out: BX      = # of 64byte blocks needed for saving state
 *      AX      = VBE return status
 */
static void
bios_10_4f04(struct regs *regs)
{
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_4
        dprintf("Cirrus VESA Function 04 called.\n");
#endif
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_4_DETAILS
        CIRRUS_DEBUG_DUMP_REGS;
#endif

        if (DL == 0) {
                /* Get size of state buffer. */
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_4_DETAILS
                dprintf("  VESA get size of buffer called\n");
#endif
                BX = 1;
        } else if (DL == 1) {
                /* Save state to buffer. */
                /* FIXME? */
#ifdef CIRRUS_WARNINGS
                dprintf("WARNING: Cirrus BIOS: bios_10_4f04: Unimplemented ");
                dprintf("VESA save state called\n");
#endif
        } else if (DL == 2) {
                /* Restore state from buffer. */
                /* FIXME? */
#ifdef CIRRUS_WARNINGS
                dprintf("WARNING: Cirrus BIOS: bios_10_4f04: Unimplemented ");
                dprintf("VESA restore state called\n");
#endif
        }

        AX = VBE_SUCCESS;
}

/*
 * VESA Function 05: Display window control
 *
 * In:  AX      = 4f05h
 *      BH      = 00h   set memory window
 *              = 01h   get memory window
 *      BL      = window #
 *      DX      = window # in window granularity units (only if BH==0)
 *
 * Out: AX      = VBE return status
 *      DX      = window # in window granularity units (only if BH==1)
 */
static void
bios_10_4f05(struct regs *regs)
{
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_5
        dprintf("Cirrus VESA Function 05 called.\n");
#endif

        /* FIXME? fail when in a lfb mode */

        if (BL > 0x01) {
                /* There are only two memory windows */
                AX = VBE_UNIMPLEMENTED;
                return;
        }

        if (BH == 0x00) {
                /* set memory window */

                if (DH) {
                        /* window # >= 0x100 */
                        AX = VBE_UNIMPLEMENTED;
                        return;
                }
                if (!BL) {      /* window 0 */
                        /* set GR9 Offset Register 0 */
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_5_DETAILS
                        dprintf("  setting memory window 0: GR9 <- 0x%0x\n", DL);
#endif
                        outb(0x09, 0x3ce);
                } else {        /* window 1 */
                        /* set GRA Offset Register 1 */
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_5_DETAILS
                        dprintf("  setting memory window 1: GRA <- 0x%0x\n", DL);
#endif
                        outb(0x0a, 0x3ce);
                }
                outb(DL, 0x3cf);

        } else if (BH == 0x01) {
                /* get memory window */

                if (!BL) {      /* window 0 */
                        /* get GR9 Offset Register 0 */
                        outb(0x09, 0x3ce);
                } else {        /* window 1 */
                        /* get GRA Offset Register 1 */
                        outb(0x0a, 0x3ce);
                }
                DL = inb(0x3cf);
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_5_DETAILS
                if (!BL) {
                        dprintf("  getting memory window 0: GR9 -> 0x%0x\n", DL);
                } else {
                        dprintf("  getting memory window 1: GRA -> 0x%0x\n", DL);
                }
#endif

        } else {
                /* unknown command */
                AX = VBE_UNIMPLEMENTED;
                return;
        }

        AX = VBE_SUCCESS;
}

/*
 * Returns CRTC offset value * 8
 * Reads CR13 and one bit of CR1B
 */
static unsigned short
_cirrus_get_display_pitch(void)
{
        unsigned short length;
        unsigned char tmp;

        /* select CR13: CRTC offset (pitch) */
        outb(0x13, 0x3d4);
        tmp = inb(0x3d5);
        /* bits 3 to 10 of length from CR13 */
        length = ((unsigned short) tmp) << 3;

        /* select CR1B: extended display controls */
        outb(0x1b, 0x3d4);
        tmp = inb(0x3d5);
        /* bit 11 of length from CR1B */
        length |= ((unsigned short) (tmp & 0x10)) << 7;

        return length;
}

/*
 * Sets CTRC offset value to lenght / 8
 * Writes CR13 and one bit of CR1B
 */
static void
_cirrus_set_display_pitch(unsigned short length)
{
        unsigned char edc;

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_6_DETAILS
        dprintf("  setting display pitch to 0x%04x\n", length);
#endif

        /* select CR13: CRTC offset (pitch) */
        outb(0x13, 0x3d4);
        /* bits 3 to 10 of length for CR13 */
        outb((unsigned char) ((length & 0x07f8) >> 3), 0x3d5);

        /* select CR1B: extended display controls */
        outb(0x1b, 0x3d4);
        edc = inb(0x3d5);
        /* bit 11 of length for CR1B */
        edc |= (unsigned char) ((length & 0x0800) >> 7);
        outb(edc, 0x3d5);
}

/*
 * Returns bpp as specified in SR7
 * (Sequencer and CRT Clocking Control)
 */
static unsigned short
_cirrus_get_bpp_bytes(void)
{
        unsigned char mode;

        /* get SR7 extendend sequencer mode */
        outb(0x07, 0x3c4);
        mode = inb(0x3c5);

        /* sequencer and crt clocking control */
        if ((mode & 0x0e) == 0x00) {
                /* 8bpp */
                return 1;
        } else if ((mode & 0x0e) == 0x06) {
                /* 16bpp */
                return 2;
        } else if ((mode & 0x0e) == 0x04) {
                /* 24bpp */
                return 3;
        } else if ((mode & 0x0e) == 0x08) {
                /* 32bpp */
                return 4;
        } else {
                dprintf("Cirrus BIOS: FATAL: Unknown mode! ");
                assert(0);
        }
        return 0;
}

/*
 * VESA Function 06: Set/get logical scanline length
 *
 * In:  AX      = 4f06h
 *      BL      = 00h   set scanline length in pixels
 *              = 01h   get scanline length
 *              = 02h   set scanline length in bytes
 *              = 03h   get maximum scanline length
 *      CX      if BL = 00h: desired width in pixels
 *              if BL = 02h: desired width in bytes
 *
 * Out: AX      = VBE return status
 *      BX      = bytes per scanline
 *      CX      = actual pixels per scanline (truncated
 *                to nearest complete pixel)
 *      DX      = maximum number of scanlines
 */
static void
bios_10_4f06(struct regs *regs)
{
        unsigned short length;
      uint8_t mem;

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_6
        dprintf("Cirrus VESA Function 06 called.\n");
#endif
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_6_DETAILS
        CIRRUS_DEBUG_DUMP_REGS;
#endif

        if (BL == 0x00 || BL == 0x02) {
                /* setters */
                length = CX;
                if (BL == 0x00) {
                        length *= _cirrus_get_bpp_bytes();
                }
                _cirrus_set_display_pitch(length);

        } else if (BL != 0x01) {
                /* FIXME get maximum scanline length */
                AX = VBE_UNIMPLEMENTED;
#ifdef CIRRUS_WARNINGS
                dprintf("WARNING: Cirrus BIOS: bios_10_4f06: get maximum ");
                dprintf("scanline length unimplemented.\n");
#endif
                return;
        }

        BX = _cirrus_get_display_pitch();
        CX = BX / _cirrus_get_bpp_bytes();
      outb(0x15, 0x3c4); /* read size of vram from scratch pad 3 */
      mem = inb(0x3c5);
      assert(mem < 6);
        DX = (const_get(memory_size_map[mem]) << 16) / BX;
        AX = VBE_SUCCESS;

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_6_DETAILS
        dprintf("  Returning following registers:\n");
        CIRRUS_DEBUG_DUMP_REGS;
#endif
}

/*
 * Gets the Screen Start A field.
 */
static unsigned long
_cirrus_get_start_address(void)
{
        unsigned char dummy;
        unsigned long address;

        /* read CRD: CRTC screen start address low */
        outb(0x0d, 0x3d4);
        dummy = inb(0x3d5);
        address = dummy;

        /* read CRC: CRTC screen start address high */
        outb(0x0c, 0x3d4);
        dummy = inb(0x3d5);
        address |= (dummy << 8);

        /* read CR1B: Extended display controls */
        /* reg bit 0 -> addr bit 16; reg bits 2,3 -> addr bits 17,18 */
        outb(0x1b, 0x3d4);
        dummy = inb(0x3d5);
        address |= ((dummy & 0x01) << 16);
        address |= ((dummy & 0x0c) << 15);

        /* read CR1D: Overlay extended control */
        /* reg bit 7 -> addr bit 19 */
        outb(0x1d, 0x3d4);
        dummy = inb(0x3d5);
        address |= ((dummy & 0x80) << 12);

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_7_DETAILS
        dprintf("  _cirrus_get_start_address() returning 0x%08x\n", address);
#endif

        return address;
}

/*
 * Sets the Screen Start A field, the location in display memory where
 * the screen begins, to "address"
 */
static void
_cirrus_set_start_address(unsigned long address)
{
        unsigned char dummy;

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_7_DETAILS
        dprintf("  _cirrus_set_start_address(0x%08x)\n", address);
#endif

        /* write CRD: CRTC screen start address low */
        outb(0x0d, 0x3d4);
        outb(address & 0x000000ff, 0x3d5);

        /* write CRC: CRTC screen start address high */
        outb(0x0c, 0x3d4);
        outb((address & 0x0000ff00) >> 8, 0x3d5);

        /* write CR1B: Extended display controls */
        /* addr bit 16 -> reg bit 0; addr bits 17,18 -> reg bits 2,3 */
        outb(0x1b, 0x3d4);
        dummy = inb(0x3d5);
        dummy = (dummy & 0xf2)
                | ((address & 0x00010000) >> 16)
                | ((address & 0x00060000) >> 15);
        outb(dummy, 0x3d5);

        /* write CR1D: Overlay extended control */
        /* addr bit 19 -> reg bit 7 */
        outb(0x1d, 0x3d4);
        dummy = inb(0x3d5);
        dummy = (dummy & 0x7f) | ((address & 0x00080000) >> 12);
        outb(dummy, 0x3d5);

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_7_DETAILS
        dprintf("  now trying to read address again...\n", address);
        _cirrus_get_start_address();
#endif
}

/*
 * VESA Function 07: Set/Get Display Start
 *
 * In:     AX   = 4f07h
 *         BH   = 00h   reserved
 *         BL   = 00h   Set Display Start
 *              = 01h   Get Display Start
 *              = 80h   Set Display Start during Vertical Retrace
 *         CX   if BL = 00h/80h: First Displayed Pixel In Scan Line
 *         DX   if BL = 00h/80h: First Displayed Scan Line
 *
 * Out: AX      = VBE return status
 *      BH      if BL = 01h: Reserved and will be 0
 *      CX      if BL = 01h: First Displayed Pixel In Scan Line
 *      DX      if BL = 01h: First Displayed Scan Line
 */
static void
bios_10_4f07(struct regs *regs)
{
        unsigned long start;

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_7
        dprintf("Cirrus VESA Function 07 called.\n");
#endif
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_7_DETAILS
        CIRRUS_DEBUG_DUMP_REGS;
#endif

        if (BL == 0x00 || BL == 0x80) {
                /* set display start */

                /* calculate display start address */
                start = ((unsigned long) CX) * _cirrus_get_bpp_bytes()
                        + ((unsigned long) DX) * _cirrus_get_display_pitch();

                _cirrus_set_start_address(start);

                AX = VBE_SUCCESS;

        } else if (BL == 0x01) {
                /* get display start */

                start = _cirrus_get_start_address();

                DX = start / _cirrus_get_display_pitch();
                CX = (start % _cirrus_get_display_pitch()) / _cirrus_get_bpp_bytes();
                BH = 0;
                AX = VBE_SUCCESS;

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_7_DETAILS
                dprintf("  registers returned:\n");
                CIRRUS_DEBUG_DUMP_REGS;
#endif

        } else {
                AX = VBE_UNIMPLEMENTED;
        }
}

/*
 * VESA Function 09: Set/Get Palette Data
 *
 * In:   AX    = 4F09h VBE Load/Unload Palette Data
 *       BL    = 00h   Set Palette Data
 *             = 01h   Get Palette Data
 *             = 02h   Set Secondary Palette Data
 *             = 03h   Get Secondary Palette Data
 *             = 80h   Set Palette Data during Vertical Retrace
 *       CX    =       Number of palette registers to update (to a
 *                     maximum of 256)
 *       DX    =       First of the palette registers to update (start)
 *       ES:DI =       Table of palette values (see below for format)
 * Out:  AX    =       VBE Return Status
 */
static void
bios_10_4f09(struct regs *regs)
{
        int i;
        unsigned char blue, green, red;

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_9
        dprintf("Cirrus VESA Function 09 called.\n");
#endif
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_9_DETAILS
        CIRRUS_DEBUG_DUMP_REGS;
#endif

        if (BL == 0x00 || BL == 0x80) {

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_9_DETAILS
                dprintf("  Setting palette data...\n");
#endif
                outb(DX, 0x3c8); /* init palette address for write mode */

                for (i = DX; i < DX + CX; i++) {

                        blue  = get_byte(ES, (unsigned char *) (DI + i * 4 + 0));
                        green = get_byte(ES, (unsigned char *) (DI + i * 4 + 1));
                        red   = get_byte(ES, (unsigned char *) (DI + i * 4 + 2));

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_9_DETAILS
                        dprintf("    i=%d b=%02x g=%02x r=%02x\n",
                                i, blue, green, red);
#endif
                        outb(red, 0x3c9); /* write palette entry */
                        outb(green, 0x3c9);
                        outb(blue, 0x3c9);
                }
                AX = VBE_SUCCESS;

        } else if (BL == 0x01) {

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_9_DETAILS
                dprintf("  Getting palette data...\n");
#endif
                outb(DX, 0x3c7); /* init palette address for read mode */

                for (i = DX; i < DX + CX; i++) {

                        red   = inb(0x3c9); /* read palette entry */
                        green = inb(0x3c9);
                        blue  = inb(0x3c9);

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_9_DETAILS
                        dprintf("    i=%d b=%02x g=%02x r=%02x\n",
                                i, blue, green, red);
#endif
                        put_byte(ES, (unsigned char *) (DI + i * 4 + 0), blue);
                        put_byte(ES, (unsigned char *) (DI + i * 4 + 1), green);
                        put_byte(ES, (unsigned char *) (DI + i * 4 + 2), red);
                }
                AX = VBE_SUCCESS;

        } else if (BL == 0x02 || BL == 0x03) {

                /* Set/Get Secondary Palette Data */
                AX = VBE_NOHARDWARE;

        } else {
                AX = VBE_UNIMPLEMENTED;
        }
}

/*
 * VESA Function 0A: Return VBE Protected Mode Interface
 *
 * Input:  AX = 4F0Ah VBE 2.0 Protected Mode Interface
 *         BL = 00h   Return protected mode table
 * Output: AX =       Status
 *         ES =       Real Mode Segment of Table
 *         DI =       Offset of Table
 *         CX =       Length of Table including protected mode code in bytes
 */
static void
bios_10_4f0a(struct regs *regs)
{
#ifdef CIRRUS_WARNINGS
        dprintf("WARNING: Cirrus BIOS: bios_10_4f0a: ");
        dprintf("Protected Mode Interface not implemented\n");
#endif
        AX = VBE_UNIMPLEMENTED;
}

/*
 * VESA Function 0B: Get/Set pixel clock
 *
 * Input:  AX  = 4F0Bh Get/Set pixel clock
 *         BL  = 00h   Get closest pixel clock
 *         ECX =       Requested pixel clock in units of Hz
 *         DX  =       Mode number pixel clock will be used with
 * Output: AX  =       Status
 *         ECX =       Closest pixel clock (BL = 00h)
*/
static void
bios_10_4f0b(struct regs *regs)
{
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_B
        dprintf("Cirrus VESA Function 0B called.\n");
#endif

        /* actually, since our simulated card doesn't care about pixel clocks
         * this should be a good enough implementation :-) */
        AX = VBE_SUCCESS;
}

/*
 * VESA Function 10: Display Power Management Extensions
 *
 * Report VBE/PM Capabilities
 *       Input:         AH =   4fh      Super VGA support
 *                      AL =   10f      VBE/PM services
 *                      BL =   00h      Report VBE/PM capabilities
 *                      ES:DI= Null pointer, must be 0000:0000 in version 1.0
 *       Output:        AX =   Status
 *                      BH =   Power saving state signals support by the controller:
 *                             1 = supported, 0 = not supported
 *                             bits 7:4 reserved
 *                             bit 3    REDUCED ON (not supported by DPMS 1.0)
 *                             bit 2    OFF
 *                             bit 1    SUSPEND
 *                             bit 0    STAND BY
 *                      BL =   VBE/PM version number
 *                             bits 7:4 Major version number
 *                             bits 3:0 Minor version number
 *
 *  Set Display Power State
 *       Input:         AH =   4fh      Super VGA support
 *                      AL =   10h      VBE/PM services
 *                      BL =   01h      Set display power state
 *                      BH =   Requested power state
 *                             00h      ON
 *                             01h      STAND BY
 *                             02h      SUSPEND
 *                             04h      OFF
 *                             08h      REDUCED ON (not supported by DPMS 1.0)
 *      Output:         AX =   Status
 *                      BH =   Unchanged
 *
 *  Get Display Power State
 *      Input:        AH = 4fh      Super VGA support
 *                    AL = 10h      VBE/PM services
 *                    BL = 02h      Get display power state
 *      Output:       AX = Status
 *                    BH = Display power state
 *                         00h      ON
 *                         01h      STAND BY
 *                         02h      SUSPEND
 *                         04h      OFF
 *                         08h      REDUCED ON (not supported by DPMS 1.0)
 *                         bits 7:4 are reserved and
 *                         should be ignored to ensure upward compatibility.
 */
static void
bios_10_4f10(struct regs *regs)
{
        unsigned char gre;

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_10
        dprintf("Cirrus VESA Function 10 called.\n");
#endif

        if (BL == 0x00) {       /* Report VBE/PM Capabilities */

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_10_DETAILS
                dprintf("  Reporting VBE/PM Capabilities\n");
#endif
                BH = 0x01;      /* supporting STAND BY */
                BL = 0x10;      /* Version 1.0 */
                AX = VBE_SUCCESS;

        } else if (BL == 0x01) { /* Set Display Power State */

                if (BH == 0x00) {

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_10_DETAILS
                        dprintf("  Setting Display Power State ON\n");
#endif
                        /* unset GRE[3:4] */
                        outb(0x0e, 0x3ce);
                        gre = inb(0x3cf);
                        gre &= 0xe7;
                        outb(gre, 0x3cf);

                } else if (BH == 0x01) {

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_10_DETAILS
                        dprintf("  Setting Display Power State STAND BY\n");
#endif
                        /* set GRE[3:4] */
                        outb(0x0e, 0x3ce);
                        gre = inb(0x3cf);
                        gre |= 0x18;
                        outb(gre, 0x3cf);

                        /* FIXME? Support more states? */

                } else {

#ifdef CIRRUS_WARNINGS
                        dprintf("WARNING: Cirrus BIOS: ");
                        dprintf("bios_10_4f10: Set DPS: Unknown State %02x\n",
                                BH);
#endif
                        AX = VBE_UNIMPLEMENTED;
                        return;
                }
                AX = VBE_SUCCESS;

        } else if (BL == 0x02) { /* Get Display Power State */

                outb(0x0e, 0x3ce);
                gre = inb(0x3cf);
                if ((gre & 0x10) && (gre & 0x08)) {

                        /* GRE[3:4] set */
                        BH = 0x01;    /* STAND BY */
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_10_DETAILS
                        dprintf("  Get Display Power State: STAND BY\n");
#endif
                } else {

                        BH = 0x00;    /* ON */
#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_10_DETAILS
                        dprintf("  Get Display Power State: ON\n");
#endif
                }
                AX = VBE_SUCCESS;

        } else {

#ifdef CIRRUS_WARNINGS
                dprintf("WARNING: Cirrus BIOS: ");
                dprintf("Power Management request unimplemented.\n");
#endif
                AX = VBE_UNIMPLEMENTED;
        }
}

/*
 * VESA Function 15: Report DDC Capabilities / Read EDID
 *
 * In:  AX      = 4f15h
 *      BL      = 00h   report DDC capabilities
 *              = 01h   read EDID sub-function
 *      CX      = controller unit (=0)
 *      ES:DI   = NULL (if BL==0)
 *              = ptr to EDID block (if BL==1)
 *
 * Out: AX      = VBE return status
 *      BH      = time to transfer 1 EDID block (if BL==0)
 *      BL      = DDC level supported (if BL==0)
 */
static void
bios_10_4f15(struct regs *regs)
{
        unsigned char *src;
        unsigned short i;

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_15
        dprintf("Cirrus VESA Function 15 called.\n");
#endif

        if (BL == 0x00) {

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_15_DETAILS
                dprintf("  reporting DDC capabilities...\n");
#endif
                /* DDC 2 supported, no screen blank during data transfer */
                BH = 0;
                BL = 0x02;
                AX = VBE_SUCCESS;

        } else if (BL == 0x01) {

#if CIRRUS_DEBUG & CIRRUS_DEBUG_VESA_15_DETAILS
                dprintf("  copying EDID infos...\n");
#endif
                src = (unsigned char *)&edidblock;
                for (i = 0; i < sizeof(edidblock); i++) {
                        put_byte(ES, (unsigned char *) (DI + i),
                                 get_byte(PTR_SEG(src + i), PTR_OFF(src + i)));
                }
                AX = VBE_SUCCESS;

        } else {

#ifdef CIRRUS_WARNINGS
                dprintf("WARNING: Cirrus BIOS: DDC request unimplemented.\n");
#endif
                AX = VBE_UNIMPLEMENTED;
        }
}

/*
 * VESA BIOS extension:
 * different sub-function dispatcher
 */
void
bios_10_4fxx(struct regs *regs)
{
        if (AL == 0x00) {
                bios_10_4f00(regs);

        } else if (AL == 0x01) {
                bios_10_4f01(regs);

        } else if (AL == 0x02) {
                bios_10_4f02(regs);

        } else if (AL == 0x03) {
                bios_10_4f03(regs);

        } else if (AL == 0x04) {
                bios_10_4f04(regs);

        } else if (AL == 0x05) {
                bios_10_4f05(regs);

        } else if (AL == 0x06) {
                bios_10_4f06(regs);

        } else if (AL == 0x07) {
                bios_10_4f07(regs);

        } else if (AL == 0x09) {
                bios_10_4f09(regs);

        } else if (AL == 0x0a) {
                bios_10_4f0a(regs);

        } else if (AL == 0x0b) {
                bios_10_4f0b(regs);

        } else if (AL == 0x10) {
                bios_10_4f10(regs);

        } else if (AL == 0x15) {
                bios_10_4f15(regs);

        } else {
                /* Unimplemented Cirrus VESA Extension */
#ifdef CIRRUS_WARNINGS
                dprintf("WARNING: Cirrus BIOS: Unimplemented Cirrus ");
                dprintf("VESA BIOS Function 0x%02x.\n", AL);
#endif
                AX = VBE_UNIMPLEMENTED;
        }
}

/******************************************************************************/
/*                                                                            */
/* Cirrus extensions                                                          */
/*                                                                            */
/******************************************************************************/

void
cirrus_a0_callback(void)
{
        /* FIXME? */
}

/*
 * Cirrus BIOS Extension: different sub-functions
 *
 * In:  AH = 12h
 *      BL = sub-function number
 */
void
cirrus_10_12xx_extensions(struct regs *regs)
{
        if (BL == 0x80) {
                /*
                 * Inquire VGA Type
                 *
                 * Out: AX = Controller Type
                 *      BL = Silicon revision number
                 */
                AX = 0x39;      /* CL-GD5446 */
                BL = 0xb8;      /* Silicon revision number */

        } else if (BL == 0x81) {
                /*
                 * Inquire BIOS Version Number
                 *
                 * Out: AH = Major BIOS version number
                 *      AL = Minor BIOS version number
                 */
                AH = 1;         /* Version 1.0 */
                AL = 0;

        } else if (BL == 0x82) {
                /*
                 * Inquire Cirrus Logic Design Revision Code
                 *
                 * Out: AL = Chip revision
                 */
                outb(0x27, 0x3d4);
                AL = inb(0x3d5);   /* read CR27 */
                AL &= 0x03;        /* two lower bits indicate revision */
                AH = 0xaf;

        } else if (BL == 0x85) {
                /*
                 * Return Installed Memory
                 *
                 * Out: AL = Amount of frame buffer memory present
                 *           in 64K units
                 */
            uint8_t mem;
            outb(0x15, 0x3c4);
            mem = inb(0x3c5);
            assert(mem < 6);
            AL = const_get(memory_size_map[mem]);

        } else if (BL == 0x9a) {
                /*
                 * Inquire User Options
                 *
                 * Out: AX = Contains the following options
                 *           Bits 13:0 = Reserved
                 *           Bit 14 = Vertical montype 640  480 frequency
                 *                    (VGA refresh)
                 *           Bit 15 = Reserved
                 *      BX = Reserved
                 *      CX = Contains the following options
                 *           Bit 0 = Reserved
                 *           Bits 3:1 = 1280  1024 vertical frequency
                 *           Bits 7:4 = Maximum Vertical Resolution
                 *           Bits 11:8 = 800  600 vertical frequency
                 *           Bits 15:12 = 1024  768 vertical frequency
                 *      DX = Bits 15:14 = 1152 vertical frequency
                 *           Bits 13:12 = 1600 vertical frequency
                 */
                AX = 0x4060;    /* bit 14 1 */
                CX = 0x1132;    /* frequencies all 1, resolution 3 */
                DX = 0x5000;    /* frequencies all 1 */

        } else if (BL == 0xa0) {
                /*
                 * Query Display Mode Availability
                 *
                 * In:  AL = Display mode number (0-7fh)
                 *
                 * Out: AH = Bit 0 = 0 Display mode not supported
                 *                   1 Display mode supported
                 *      DS:SI: Pointer to standard display parameters or
                 *             FFFF:FFFF if standard parameters undefined
                 *             for this mode
                 *      ES:DI: Pointer to supplemental display parameters
                 *             or FFFF:FFFF if supplemental parameters
                 *             undefined for this mode
                 *      BX =   Offset to BIOS subroutine that "fix up" the
                 *             parameters pointed to by DS:SI. This routine
                 *             requires ES:DI pointers to the proper
                 *             supplemental display parameters.
                 */
                /* FIXME? */
                AH = 0x01;      /* mode supported */
                DS = 0xffff;    /* no documentations for those parameters */
                SI = 0xffff;
                ES = 0xffff;
                DI = 0xffff;
                BX = (unsigned short) (unsigned long) cirrus_a0_callback;

        } else if (BL == 0xa1) {
                /*
                 * Read Monitor ID/Type
                 *
                 * Out: BH = Monitor ID
                 *           0Dh = IBM 8503 or equivalent
                 *           0Eh = IBM 8512/8513 or equivalent
                 *           0Fh = No monitor
                 *           00 thru 0C = reserved
                 *      BL = Monitor gender
                 *           00 = Color display
                 *           01 = Grayscale display
                 *           02 = No display
                 */
                BH = 0x0e;
                BL = 0x00;

        } else if (BL == 0xa2) {
                /*
                 * Set Monitor Type - Horizontal
                 */
                AL = 0x07;

        } else if (BL == 0xa3) {
                /*
                 * Set Refresh Type
                 */
                goto cirrus_extension_unimplemented;

        } else if (BL == 0xa4) {
                /*
                 * Set Monitor Type
                 */
                goto cirrus_extension_unimplemented;

        } else if (BL == 0xa5) {
                /*
                 * Process Generic Fixup Table
                 *
                 * In:  AL = 1
                 *
                 * Out: Nothing
                 */
                goto cirrus_extension_unimplemented;

        } else if (BL == 0xa7) {
                /*
                 * Return Chip Capabilities
                 *
                 * In:  CX = 0000h
                 */
                goto cirrus_extension_unimplemented;

        } else if (BL == 0xae) {
                /*
                 * Get High Refresh
                 *
                 * Out: AL = Bit 0 indicates 640  480 high-refresh rate
                 *           0 = 72 Hz
                 *           1 = 75 Hz
                 */
                AL = 0x01;

        } else if (BL == 0xaf) {
                /*
                 * Set High Refresh
                 *
                 * In:  AL = Bit 0 indicates 640  480 high-refresh rate
                 *           0 = 72 Hz
                 *           1 = 75 Hz
                 */
                goto cirrus_extension_unimplemented;

        } else {
                /*
                 * Unimplemented BIOS Extension
                 */
        cirrus_extension_unimplemented:
                while(0);       /* no more gcc warning */
        }
}

/*
 * Initialize cirrus specific part of graphics adapter
 */
void
cirrus_extension_init(void)
{
        uint8_t dummy;
      uint8_t grb;
      uint8_t gr5;
      uint8_t gr9;
      uint16_t value0;
      uint16_t value2;
      uint16_t value3;

      /* initialize scratch pad registers */
      /* scratch pad 0 (SR9) */
      outb(0x09, 0x3c4);
      outb(0x44, 0x3c5);
      /* scratch pad 1 (SRA) */
      outb(0x0a, 0x3c4);
      outb(0x40, 0x3c5);
      /* scratch pad 2 (SR14) */
      outb(0x14, 0x3c4);
      outb(0x08, 0x3c5);
      /* scratch pad 3 (SR15) */
      outb(0x15, 0x3c4);
      outb(0x01, 0x3c5);

      /* put controller to Cirrus mode (SR7) */
      outb(0x07, 0x3c4);
      outb(0x01, 0x3c5);

      /* offset register 0, 16kB granularity, no extended mode (GRB) */
      outb(0x0b, 0x3ce);
      grb = inb(0x3cf);
      outb((grb & ~0x07) | 0x20, 0x3cf);

      /* write mode 0 (GR5) */
      outb(0x05, 0x3ce);
      gr5 = inb(0x3cf);
      outb(gr5 & ~0x07, 0x3cf);

      /* configure memory access mode to:
       * 64 bit, noswap, 2 banks, 2MB bank
       * (SR17, SRF) */
      outb(0x17, 0x3c4);
      dummy = inb(0x3c5);
      outb(dummy & ~0x82, 0x3c5); /* noswap, 2MB banks */

      outb(0x0f, 0x3c4);
      dummy = inb(0x3c5);
      outb(dummy | 0x98, 0x3c5); /* 64 bit, 2 banks */

      /* check whether we really need a 64 bit mode */

      outb(0x09, 0x3ce);
      gr9 = inb(0x3cf);
      outb(0x00, 0x3cf); /* map first 16kB page to 0xa0000 */
      put_word(0xa000, 0x0000, 0xa5b6);
      if (get_word(0xa000, 0x0000) != 0xa5b6) {
            /* 32 bit, noswap, half bank, 2MB bank (VRAM == 1MB)
             * (this is the only legal mode for 32 bit bus) */
            outb(0x0f, 0x3c4);
            dummy = inb(0x3c5) & ~0x98;
            outb(dummy | 0x10, 0x3c5); /* 32 bit, 1 bank */
            dummy = 2;
            dprintf("Cirrus BIOS: VRAM is 2 x 256Kx16 (= 1 MB)\n");

            goto probe_done;
      }

      /* write beyond 1MB, 2MB, and 3MB borders */

      outb(0x09, 0x3ce);
      outb(0x40, 0x3cf); /* map 16kB page at 1MB to 0xa0000 */
      put_word(0xa000, 0x0000, 0x1111);

      outb(0x09, 0x3ce);
      outb(0x80, 0x3cf); /* map 16kB page at 2MB to 0xa0000 */
      put_word(0xa000, 0x0000, 0x2222);

      outb(0x09, 0x3ce);
      outb(0xc0, 0x3cf); /* map 16kB page at 3MB to 0xa0000 */
      put_word(0xa000, 0x0000, 0x3333);

      /* read data from 0, 2MB, and 3MB borders */

      outb(0x09, 0x3ce);
      outb(0x00, 0x3cf); /* map first 16kB page to 0xa0000 */
      value0 = get_word(0xa000, 0x0000);

      outb(0x09, 0x3ce);
      outb(0x80, 0x3cf); /* map 16kB page at 2MB to 0xa0000 */
      value2 = get_word(0xa000, 0x0000);

      outb(0x09, 0x3ce);
      outb(0xc0, 0x3cf); /* map 16kB page at 3MB to 0xa0000 */
      value3 = get_word(0xa000, 0x0000); 

      /* find out memory configuration 
       * and set up SR17 and SRF accordingly */

      if (value0 == 0x1111) {
            if (value2 == 0x3333 && value3 == 0x3333) {
                  /* 64 bit, noswap, 2 banks, 1 MB bank (VRAM == 2MB) */
                  outb(0x17, 0x3c4);
                  dummy = inb(0x3c5);
                  outb(dummy | 0x80, 0x3c5); /* switch to 1MB banks */
                  dummy = 3;

            } else if (value2 == 0x2222 && value3 == 0x3333) {
                  /* 64 bit, swap, 2 banks, mixed banks (VRAM == 3MB) */
                  outb(0x17, 0x3c4);
                  dummy = inb(0x3c5);
                  outb(dummy | 0x02, 0x3c5); /* switch to bank swap */
                  dummy = 5;

            } else {
                  /* 64 bit, noswap, 1 bank, 1 MB bank (VRAM == 1MB) */
                  outb(0x17, 0x3c4);
                  dummy = inb(0x3c5);
                  outb(dummy | 0x80, 0x3c5); /* switch to 1MB banks */

                  outb(0x0f, 0x3c4);
                  dummy = inb(0x3c5);
                  outb(dummy & ~0x80, 0x3c5); /* disable 2. bank */
                  dummy = 2;
            }

      } else {
            assert(value0 == 0xa5b6);

            if (value2 == 0x2222 && value3 == 0x3333) {
                  /* 64 bit, noswap, 2 banks, 2 MB bank (VRAM == 4MB) */
                  /* (already configured, just keep it) */
                  dummy = 4;

            } else {
                  /* 64 bit, noswap, 1 bank, 2 MB bank (VRAM == 2MB) */
                  outb(0x0f, 0x3c4);
                  dummy = inb(0x3c5);
                  outb(dummy & ~0x80, 0x3c5); /* disable 2. bank */
                  dummy = 3;
            }
      }

probe_done:

      /* write memory size code set above to scratch pad 3
       * (=SR15); see definition of memory_size_map[] */
      outb(0x15, 0x3c4);
      outb(dummy, 0x3c5);

      /* reset page offset 0 (GR9) */
      outb(0x09, 0x3ce);
      outb(gr9, 0x3cf);

      /* reset graphics controller mode extensions (GRB) */
      outb(0x0b, 0x3ce);
      outb(grb, 0x3cf);

      /* reset graphics controller mode (GR5) */
      outb(0x05, 0x3ce);
      outb(gr5, 0x3cf);

      /* reset extended sequencer mode (SR7) */
      outb(0x07, 0x3c4);
      outb(0x00, 0x3c5);

        /* Reset bitblitter with GR31: BLT start/status */
      outb(0x31, 0x3ce);
      outb(0x04, 0x3cf);
      outb(0x31, 0x3ce);
      outb(0x00, 0x3cf);
}

/*
 * Set cirrus specific graphic modes (all non-vesa and above 0x13)
 */
void
cirrus_set_mode(unsigned int m)
{
        CONST struct cirrus_mode_info *miblock;
        unsigned short mode;

        /* bit 7 is clear display flag */
        mode = (unsigned short) (m & 0x7f);

        /* find requested miblock */
        if (!(miblock = _cirrus_find_miblock(mode))) {
#ifdef CIRRUS_WARNINGS
                dprintf("WARNING: Cirrus BIOS: ");
                dprintf("Unknown requested cirrus mode 0x%04x!\n", mode);
                dprintf("Please wait until your OS switches your mode back.\n");
#endif
                return;
        }

        _cirrus_switch_mode(miblock);

        var_put(vid_mode, mode);
        var_put(vid_columns, const_get(miblock->width) / 8);
        var_put(vid_rows, const_get(miblock->height) / 16 - 1);
        var_put(curr_page, 0);

        if (m & 0x80) {
                _cirrus_clear_display(miblock);
        }
}

Generated by  Doxygen 1.6.0   Back to index