[Back to GRAPHICS SWAG index] [Back to Main SWAG index] [Original]

    (*
    Here is a great text for a start:
    Title:  INTRODUCTION TO MODE X

    Version:        1.7

    Author:  Robert Schmidt

    Copyright: (C) 1993 of Ztiff Zox Softwear - refer to Status below.

    Last revision:  28-Aug-93

    Figures: 1. M13ORG - memory organization in mode 13h
      2. MXORG - memory organization in unchained modes

      The figures are available both as 640x480x16 bitmaps
      (in GIF format), and as 7-bit ASCII text (ASC) files.

    C sources: 1. LIB.C v1.1 - simple graphics library for planar,
                       256-color modes - optionally self-testing.

                    Excerpts from the source(s) appear in this article.
                    Whenever there are conflicts, the external source file(s),
      _not_ the excerpts provided here, are correct (or, at
      least, newest).

    Status:  This article, its associated figures and source listings
      named above, are all donated to the public domain.
      Do with it whatever you like, just don't claim it's your
      work, or make money on it without doing some work
                    yourself.  Please distribute the archive in its entirety.

      The standard disclaimer applies.

    Index:  0. ABSTRACT
      1. INTRODUCTION TO THE VGA AND ITS 256-COLOR MODE
      2. GETTING MORE PAGES AND PUTTING YOUR FIRST PIXEL
      3. THE ROAD FROM HERE
      4. BOOKS ON THE SUBJECT
                    5. BYE - FOR NOW

    0. ABSTRACT

    This text gives a fairly basic, yet technical, explanation to what, why
    and how Mode X is.  It first tries to explain the layout of the VGA
    memory and the shortcomings of the standard 320x200 256-color mode,
    then gives instructions on how one can progress from mode 13h to a
    multipage, planar 320x200 256-color mode, and from there to the
    quasi-standard 320x240 mode, known as Mode X.

    A little experience in programming the standard VGA mode 13h
    (320x200 in 256 colors) is assumed.  Likewise a good understanding of
    hexadecimal notation and the concepts of segments and I/O ports is
    assumed.  Keep a VGA reference handy, which at least should have
    definitions of the VGA registers at bit level.

    Throughout the article, a simple graphics library for unchained (planar)
    256-color modes is developed.  The library supports the 320x200 and
    320x240 modes, active and visible pages, and writing and reading
    individual pixels.

    1. INTRODUCTION TO THE VGA AND ITS 256-COLOR MODE

    Since its first appearance on the motherboards of the IBM PS/2 50, 60
    and 80 models in 1987, the Video Graphics Array has been the de facto
    standard piece of graphics hardware for IBM and compatible personal
    computers.  The abbreviation, VGA, was to most people synonymous with
    acceptable resolution (640x480 pixels), and a stunning rainbow of colors
    (256 from a palette of 262,144), at least compared to the rather gory
    CGA and EGA cards.

    Sadly, to use 256 colors, the VGA BIOS limited the users to 320x200
    pixels, i.e. the well-known mode 13h.  This mode has one good and one
    bad asset.  The good one is that each pixel is easily addressable in
    the video memory segment at 0A000h.  Simply calculate the offset using
    this formula:

    offset = (y * 320) + x;

    Set the byte at this address (0A000h:offset) to the color you want, and
    the pixel is there.  Reading a pixel is just as simple: just read a
    byte.  This was heaven, compared to the hell of planes and masking
    registers needed in 16-color modes.  Suddenly, the distance from a
    graphics algorithm on paper to an implemented graphics routine was cut
    down to a fraction.  The results were impressively fast too!

    The bad asset is that mode 13h is also limited to only one page, i.e.
    the VGA can only hold one screenful at any one time.  Most 16-color
    modes let the VGA hold more than one page, and this enables you to show
    one of the pages to the user, while drawing on another page in the
    meantime.  Page flipping is an important concept in making flicker free
    animations.  Nice looking and smooth scrolling is also almost impossible
    in this mode using plain VGA hardware.

    Now, the alert reader might say: "Hold on a minute!  If mode 13h enables
    only one page, this means that there is memory for only one page.  But I
    know for a fact that all VGAs have at least 256 Kb RAM, and one 320x200
    256-color page should consume only 320*200=64000 bytes, which is less
    than 64 Kb.  A standard VGA should room a little more than four 320x200
    pages!"  Quite correct, and to see how the BIOS puts this limitation on
    mode 13h, I'll elaborate a little on the memory organization of the VGA.

    The memory is separated into four bit planes.  The reason for this stems
    from the EGA, where graphics modes were 16-color.  Using bit planes, the
    designers chose to let each pixel on screen be addressable by a single
    bit in a single byte in the video segment.  Assuming the palette has
    not been modified from the default, each plane represent one of the EGA
    primary colors: red, green, blue and intensity.  When modifying the bit
    representing a pixel, the Write Plane Enable register is set to the
    wanted color.  Reading is more complex and slower, since you can
    only read from a single plane at a time, by setting the Read Plane
    Select register.  Now, since each address in the video segment can
    access 8 pixels, and there are 64 Kb addresses, 8 * 65,536 = 524,288
    16-color pixels can be accessed.  In a 320x200 16-color mode, this makes
    for about 8 (524,288/(320*200)) pages, in 640x480 you get nearly 2
    (524,288/(640*480)) pages.

    In a 256-color mode, the picture changes subtly.  The designers decided
    to fix the number of bit planes to 4, so extending the logic above to 8
    planes and 256 colors does not work.  Instead, one of their goals was to
    make the 256-color mode as easily accessible as possible.  Comparing the
    8 pixels/address in 16-color modes to the 1-to-1 correspondence of
    pixels and addresses of mode 13h, one can say that they have
    succeeded, but at a certain cost.  For reasons I am not aware of, the
    designers came up with the following effective, but memory-wasting
    scheme:

    The address space of mode 13h is divided evenly across the four bit
    planes.  When an 8-bit color value is written to a 16-bit address in the
    VGA segment, a bit plane is automatically selected by the 2 least
    significant bits of the address.  Then all 8 bits of the data is written
    to the byte at the 16-bit address in the selected bitplane (have a look at
    figure 1).  Reading works exactly the same way.  Since the bit planes are so
    closely tied to the address, only every fourth byte in the video memory is
    accessible, and 192 Kb of a 256 Kb VGA go to waste.  Eliminating the
    need to bother about planes sure is convenientand beneficial, but in
    most people's opinion the loss of 3/4 of VGA memory is too much.

    To accomodate this new method of accessing video memory, the VGA
    designers introduced a new configuration bit called Chain-4, which
    resides as bit number 3 in index 4 of the Sequencer.  In 16-color modes,
    the default state for this bit is off (zero), and the VGA operates as
    described earlier.  In the VGA's standard 256-color mode, mode 13h, this
    bit is turned on (set to one), and this turns the tieing of bit
    planes and memory address on.

    In this state, the bit planes are said to be chained together.

    Note that Chain-4 in itself is not enough to set a 256-color mode -
    there are other registers which deals with the other subtle changes in
    nature from 16 to 256 colors.  But, as we now will base our work with
    mode X on mode 13h, which already is 256-color, we won't bother about
    these for now.

    2. GETTING MORE PAGES AND PUTTING YOUR FIRST PIXEL

    The observant reader might at this time suggest that clearing the
    Chain-4 bit after setting mode 13h will give us access to all 256 Kb of
    video memory, as the two least significant bits of the byte address
    won't be `wasted' on selecting a bit plane.  This is correct.  You might
    also start feeling a little uneasy, because something tells you that
    you'll instantly loose the simple addressing of mode 13h.  Sadly, that
    is also correct.

    At the moment Chain-4 is cleared, each byte offset addresses *four*
    sequential pixels.  Before writing to a byte offset in the video
    segment, you should make sure that the 4-bit mask in the Write Plane
    Enable register is set correctly, according to which of the four
    addressable pixels you want to modify.  In essence, it works like a
    16-color mode with a twist.  See figure 2.

    So, is this mode X?  Not quite.  We need to elaborate to the VGA how to
    fetch data for refreshing the monitor image.  Explaining the logic
    behind this is beyond the scope of this getting-you-started text, and it
    wouldn't be very interesting anyway.  Here is the minimum snippet of
    code to initiate the 4 page variant of mode 13h, written in plain C,
    using some DOS specific features (see header for a note about the
    sources included):

    ----8<-------cut begin------

    /* width and height should specify the mode dimensions.  widthBytes
       specify the width of a line in addressable bytes. */

    int width, height, widthBytes;

    /* actStart specifies the start of the page being accessed by
       drawing operations.  visStart specifies the contents of the Screen
       Start register, i.e. the start of the visible page */

    unsigned actStart, visStart;

    /*
     * set320x200x256_X()
     * sets mode 13h, then turns it into an unchained (planar), 4-page
     * 320x200x256 mode.
     */

    set320x200x256_X()
     {

     union REGS r;

     /* Set VGA BIOS mode 13h: */

     r.x.ax = 0x0013;
     int86(0x10, &r, &r);

     /* Turn off the Chain-4 bit (bit 3 at index 4, port 0x3c4): */

     outport(SEQU_ADDR, 0x0604);

     /* Turn off word mode, by setting the Mode Control register
        of the CRT Controller (index 0x17, port 0x3d4): */

     outport(CRTC_ADDR, 0xE317);

     /* Turn off doubleword mode, by setting the Underline Location
        register (index 0x14, port 0x3d4): */

     outport(CRTC_ADDR, 0x0014);

     /* Clear entire video memory, by selecting all four planes, then
        writing 0 to the entire segment. */

     outport(SEQU_ADDR, 0x0F02);
     memset(vga+1, 0, 0xffff); /* stupid size_t exactly 1 too small */
     vga[0] = 0;

     /* Update the global variables to reflect dimensions of this
        mode.  This is needed by most future drawing operations. */

            width   = 320;
     height = 200;

            /* Each byte addresses four pixels, so the width of a scan line
               in *bytes* is one fourth of the number of pixels on a line. */

            widthBytes = width / 4;

            /* By default we want screen refreshing and drawing operations
               to be based at offset 0 in the video segment. */

     actStart = visStart = 0;

     }

    ----8<-------cut end------

    As you can see, I've already provided some of the mechanics needed to
    support multiple pages, by providing the actStart and visStart variables.
    Selecting pages can be done in one of two contexts:

     1) selecting the visible page, i.e. which page is visible on
        screen, and

     2) selecting the active page, i.e. which page is accessed by
        drawing operations

    Selecting the active page is just a matter of offsetting our graphics
    operations by the address of the start of the page, as demonstrated in
    the put pixel routine below.  Selecting the visual page must be passed
    in to the VGA, by setting the Screen Start register.  Sadly enough, the
    resolution of this register is limited to one addressable byte, which
    means four pixels in unchained 256-color modes.  Some trickery is needed
    for 1-pixel smooth, horizontal scrolling, but I'll make that a subject
    for later.  The setXXXStart() functions provided here accept byte
    offsets as parameters, so they'll work in any mode.  If widthBytes and
    height are set correctly, so will the setXXXPage() functions.

    ----8<-------cut begin------

    /*
     * setActiveStart() tells our graphics operations which address in video
     * memory should be considered the top left corner.
     */

    setActiveStart(unsigned offset)
     {
     actStart = offset;
     }

    /*
     * setVisibleStart() tells the VGA from which byte to fetch the first
     * pixel when starting refresh at the top of the screen.  This version
     * won't look very well in time critical situations (games for
     * instance) as the register outputs are not synchronized with the
     * screen refresh.  This refresh might start when the high byte is
     * set, but before the low byte is set, which produces a bad flicker.
     */

    setVisibleStart(unsigned offset)
     {
     visStart = offset;
     outport(CRTC_ADDR, 0x0C);  /* set high byte */
     outport(CRTC_ADDR+1, visStart >> 8);
     outport(CRTC_ADDR, 0x0D);  /* set low byte */
     outport(CRTC_ADDR+1, visStart & 0xff);
     }

    /*
     * setXXXPage() sets the specified page by multiplying the page number
     * with the size of one page at the current resolution, then handing the
     * resulting offset value over to the corresponding setXXXStart()
     * function.  The first page number is 0.
     */

    setActivePage(int page)
     {
     setActiveStart(page * widthBytes * height);
     }

    setVisiblePage(int page)
     {
     setVisibleStart(page * widthBytes * height);
     }

    ----8<-------cut end------

    Due to the use of bit planes, the graphics routines tend to get more
    complex than in mode 13h, and your first versions will generally tend to
    be a little slower than mode 13h algorithms.  Here's a put pixel routine
    for any unchained 256-color mode (it assumes that the 'width' variable
    from the above code is set correctly).  Optimizing is left as an exercise
    to you, the reader.  This will be the only drawing operation I'll cover
    in this article.

    ----8<-------cut begin------

    putPixel_X(int x, int y, char color)
     {

     /* Each address accesses four neighboring pixels, so set
        Write Plane Enable according to which pixel we want
        to modify.  The plane is determined by the two least
        significant bits of the x-coordinate: */

     outportb(0x3c4, 0x02);
     outportb(0x3c5, 0x01 << (x & 3));

     /* The offset of the pixel into the video segment is
        offset = (width * y + x) / 4, and write the given
        color to the plane we selected above.  Heed the active
        page start selection. */

     vga[(unsigned)(widthBytes * y) + (x / 4) + actStart] = color;

     }

    char getPixel_X(int x, int y)
     {

     /* Select the plane from which we must read the pixel color: */

     outport(GRAC_ADDR, 0x04);
     outport(GRAC_ADDR+1, x & 3);

     return vga[(unsigned)(widthBytes * y) + (x / 4) + actStart];

     }

    ----8<-------cut end------

    However, by now you should be aware of that the Write Plane Enable
    register isn't limited to selecting just one bit plane, like the
    ReadPlane Select register is.  You can enable any combination of all
    four to be written.  This ability to access 4 pixels with one
    instruction helps quadrupling the speed, especially when drawing
    horizontal lines and filling polygons of a constant color.  Also, most
    block algorithms can be optimized in various ways so that they need only
    a constant number of OUTs (typically four) to the Write Plane Enable
    register.  OUT is a relatively slow instruction.

    The gained ability to access the full 256 Kb of memory on a standard
    VGA enables you to do paging and all the goodies following from that:
    smooth scrolling over large maps, page flipping for flicker free
    animation... and I'll leave something for your own imagination.
    *)

[Back to GRAPHICS SWAG index] [Back to Main SWAG index] [Original]