Bochs VBE Extensions

From OSDev.wiki
Revision as of 12:38, 5 March 2008 by osdev>Jal
Jump to navigation Jump to search
This page is a work in progress.
This page may thus be incomplete. Its content may be changed in the near future.

The Bochs VGA BIOS supports, to an extend, the VBE specification. Since Bochs only emulates a VGA card down to the hardware level (and a Cirrus graphics card if you enable it, but that is not tied in with the Bochs VBE extensions), it emulates very simple graphics hardware that the VBE BIOS can drive. The advantage of this is that if you are running your OS in Bochs (or Qemu, which uses the Bochs VGA BIOS), you can use this emulated hardware to directly set video modes, without using VBE (which would require real mode or v86).

Overview

The Bochs emulated graphics hardware (henceforth called BGA for Bochs Graphics Adaptor) is accessed via two 16-bit IO-ports. The first one is an index port, the second one a data port (comparable to how the VGA handles its sets of registers). Via these ports it is possible to enable or disable the VBE extensions, change the screen resolution and bit depth, and manage a larger virtual screen. There are five versions of the BGA (0xB0C0 through 0xB0C4), but if you use the latest version of Bochs you only need to concern yourself with the latest one (0xB0C4).

The Bochs sources define in vga.h, located in the subdirectory iodev/, a number of defines that are useful for programming the BGA. The names of these defines all start with VBE_DISPI. They are used in the sections below, with their numerical value between parentheses.

BGA versions

As Bochs has evolved, so has the BGA. Five versions of the BGA have existed, of which 0xB0C4 is the current version (as of 2008, Bochs version 2.3.5). The main features of each version:

  • 0xB0C0 - setting X and Y resolution and bit depth (8 BPP only), banked mode
  • 0xB0C1 - virtual width and height, X and Y offset
  • 0xB0C2 - 15, 16, 24 and 32 BPP modes, support for linear frame buffer, support for retaining memory contents on mode switching
  • 0xB0C3 - support for getting capabilities, support for using 8 bit DAC
  • 0xB0C4 - [TODO: check and describe]

Programming the BGA

Writing registers

To write an index/data pair to one of the BGA registers, first write its index value to the 16-bit IO-port VBE_DISPI_IOPORT_INDEX (0x01CE), followed by writing the data value to the 16-bit IO-port VBE_DISPI_IOPORT_DATA (0x01CF). The BGA supports 10 different index values (0 through 9):

  • VBE_DISPI_INDEX_ID (0)
  • VBE_DISPI_INDEX_XRES (1)
  • VBE_DISPI_INDEX_YRES (2)
  • VBE_DISPI_INDEX_BPP (3)
  • VBE_DISPI_INDEX_ENABLE (4)
  • VBE_DISPI_INDEX_BANK (5)
  • VBE_DISPI_INDEX_VIRT_WIDTH (6)
  • VBE_DISPI_INDEX_VIRT_HEIGHT (7)
  • VBE_DISPI_INDEX_X_OFFSET (8)
  • VBE_DISPI_INDEX_Y_OFFSET (9)

In order to change the contents of registers 1-3 (VBE_DISPI_INDEX_XRES, VBE_DISPI_INDEX_YRES, VBE_DISPI_INDEX_BPP) the VBE extensions must be disabled first. To do so, write the value VBE_DISPI_DISABLED (0x00) to VBE_DISPI_INDEX_ENABLE (4). The changes are not visible until the VBE extensions are enabled again. To do so, write the value VBE_DISPI_ENABLED (0x01) to the same register (see also note below on enabling the LFB).

Reading registers

To read a register, first write the index value to VBE_DISPI_IOPORT_INDEX (0x01CE), then read the 16-bit value from VBE_DISPI_IOPORT_DATA (0x01CF). The value returned depends on the specific register that is queried. [TODO: check and describe, especially _ID and _ENABLE, others seem trivial]

Checking availability

To check whether the BGA is available, read the value from VBE_DISPI_INDEX_ID (0). If it equals VBE_DISPI_ID4 (0xB0C4) the latest version of the BGA is present. If it returns a value of 0xB0C0 through 0xB0C3, you have an old version of Bochs and/or the Bochs VGA BIOS.

If for some reason you want Bochs to emulate an older version of the BGA, you can write the desired version to VBE_DISPI_INDEX_ID (0). If succesful, reading the register again will return the value just set. This is used by the Bochs VGA BIOS to ensure it is run with the right version of Bochs. If done from an application (or your OS), this will break compatability with the Bochs VBE BIOS, which expects the latest version.

Setting display resolution and bit depth

Most likely, setting the display resolution and bit depth is all you need. To do so, disable the VBE extensions (see above), write the X resolution, Y resolution and BPP to their respective registers (VBE_DISPI_INDEX_XRES (1), VBE_DISPI_INDEX_YRES (2) and VBE_DISPI_INDEX_BPP (3)) and enable the VBE extensions. Since the BGA is not real hardware, X and Y resolutions can be set at will up to a maximum horizontal resolution of 1600 (VBE_DISPI_MAX_XRES) and a maximum vertical resolution of 1200 (VBE_DISPI_MAX_YRES) [TODO: check whether Bochs does a sanity check on width/height pairs and describe]. The bit depth needs to be one of the following [TODO: check what Bochs does with other values]:

  • VBE_DISPI_BPP_4 (0x04)
  • VBE_DISPI_BPP_8 (0x08)
  • VBE_DISPI_BPP_15 (0x0F)
  • VBE_DISPI_BPP_16 (0x10)
  • VBE_DISPI_BPP_24 (0x18)
  • VBE_DISPI_BPP_32 (0x20)

For 24 BPP, the order of the colour components is blue first, then green, then red. [TODO: check for other BPP and describe]

Using banked mode

When using banked mode, the BGA uses a 64Kb bank size (VBE_DISPI_BANK_SIZE_KB) starting at address 0xA0000 (VBE_DISPI_BANK_ADDRESS). Banked mode is the default mode, so when enabling the VBE extensions without explicitly telling the BGA to use a linear frame buffer, the BGA enables banked mode. To set the bank to use, write the bank number to the bank register (VBE_DISPI_INDEX_BANK (5)). [TODO: check whether Bochs does a sanity check, or wrap-around, or whatever and describe]

Using a linear frame buffer (LFB)

When using a linear frame buffer, the BGA exposes all of the graphics memory in a linear fashion, starting at address 0xE0000000 (VBE_DISPI_LFB_PHYSICAL_ADDRESS). [TODO: check whether this also goes for 4 BPP modes] To enable the LFB, use the VBE_DISPI_LFB_ENABLED flag (0x40) when enabling the VBE extensions (so write a value of VBE_DISPI_INDEX_ENABLE | VBE_DISPI_LFB_ENABLED (0x41)). [TODO: check whether Bochs still exposes 0xA0000 for use, and if so whether banked mode still works]

Clearing display memory

When enabling the VBE extensions, Bochs clears the video memory (i.e. sets all bytes to 0). To prevent this from happening, use the VBE_DISPI_NOCLEARMEM flag (0x80) when enabling the VBE extensions (so write a value of VBE_DISPI_INDEX_ENABLE | VBE_DISPI_NOCLEARMEM (0x81) for banked mode and VBE_DISPI_INDEX_ENABLE | VBE_DISPI_LFB_ENABLED | VBE_DISPI_NOCLEARMEM (0xC1) for LFB).

Miscellaneous

[TODO: check and describe VBE_DISPI_INDEX_ID (with associated VBE_DISPI_IDx constants), VBE_DISPI_INDEX_VIRT_WIDTH/HEIGHT, VBE_DISPI_INDEX_X/Y_OFFSET, VBE_DISPI_GETCAPS, VBE_DISPI_8BIT_DAC]

Example code

void BgaWriteRegister(unsigned short IndexValue, unsigned short DataValue)
{
    outpw(VBE_DISPI_IOPORT_INDEX, IndexValue);
    outpw(VBE_DISPI_IOPORT_DATA, DataValue);
}

void BgaSetVideoMode(unsigned int Width, unsigned int Height, unsigned int BitDepth, int UseLinearFrameBuffer, int ClearVideoMemory)
{
    BgaWriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_DISABLED);
    BgaWriteRegister(VBE_DISPI_INDEX_XRES, Width);
    BgaWriteRegister(VBE_DISPI_INDEX_YRES, Height);
    BgaWriteRegister(VBE_DISPI_INDEX_BPP, BitDepth);
    BgaWriteRegister(VBE_DISPI_INDEX_ENABLE, VBE_DISPI_ENABLED |
        UseLinearFrameBuffer ? VBE_DISPI_LFB_ENABLED : 0 |
        ClearVideoMemory ? 0 : VBE_DISPI_NOCLEARMEM);
}

void BgaSetBank(unsigned short BankNumber)
{
    BgaWriteRegister(VBE_DISPI_INDEX_BANK, BankNumber);
}