Click here if you are stuck in someone else's frames.
Changes to VMODE files

Whew...  that was a rather lengthy explanation, I sure hope I did not scare you off.  Maybe I should have warned you, oh well....

Before we move on may I suggest that we do a slight change to the SetPixel macro, in addition to the shifting?  This is optional, you don't have to do it and I doubt that your code will suffer any noticeable performance loss if you didn't.  Here's what I suggest...

If you're using Turbo C, try the following:

    Remove the declarations of the videoMem variable from both VMODE.C and
    VMODE.H files.  Delete ...

    extern unsigned char far *videoMem;

    ... from VMODE.H and ...

    unsigned char far *videoMem = (unsigned char far *)0xA0000000L;

    ... from VMODE.C.

    Now, replace the line you deleted in VMODE.H with these ...

    #define VIDEO_SEG 0xA000
    #define videoMem ((unsigned char far *)(MK_FP(VIDEO_SEG, 0)))

    ... and change the SetPixel macro as follows ...

    #define SetPixel(X,Y,C) pokeb(VIDEO_SEG, ((((Y<<2)+Y)<<6)+X), C)

If you're using compilers other than Turbo C, do the same changes outlined above except change the SetPixel macro as so...

    #define SetPixel(X,Y,C)(*((char far *)MK_FP(VIDEO_SEG, ((((Y<<2)+Y)<<6)+X)))=(char)C)

If you go with this change, change also the FillScreen macro like so:

    #define FillScreen(C) _fmemset(MK_FP(VIDEO_SEG, 0), C, 64000)

Okay, just so we are all on the same page, here are the updated VMODE.H and VMODE.C files...

    /* Video mode header file -- VMODE.H
     * by Gary Neal, Jr.
     */

    #ifndef _VMODE_H    /* Prevent multiple inclusions */
    #define _VMODE_H

    #include <dos.h>    /* Needed for int86 functions */

    /* Video display modes */
    #define VGA256    0x13
    #define TEXT_MODE 0x03

    /* Define constants for screen dimensions */
    #define ScreenWidth  (unsigned)320
    #define ScreenHeight (unsigned)200

    /* Define constant for screen colors */
    #define ScreenColors (unsigned)256

    /* Define address constant for video memory */
    #define VIDEO_SEG 0xA000
    #define videoMem ((unsigned char far *)(MK_FP(VIDEO_SEG, 0)))

    #ifdef __cplusplus  /* This module must be C style */
    extern "C" int SetVideoMode(int mode);
    #else
    int SetVideoMode(int mode);
    #endif

    /* Create macros */
    #define SetPixel(X,Y,C) pokeb(VIDEO_SEG, ((((Y<<2)+Y)<<6)+X), C)
    #define FillScreen(C)   _fmemset(MK_FP(VIDEO_SEG, 0), C, 64000)

    /* Alternative SetPixel macro */
    /* #define SetPixel(X,Y,C)(*(char far *)MK_FP(VIDEO_SEG, ((((Y<<2)+Y)<<6)+X)))=(char)C) */

    #endif  /* _VMODE_H */

    /* Library source file -- VMODE.C */

    #include "vmode.h"               /* Header file for this code */

    int SetVideoMode(int mode)
    {
        union REGS regs;             /* Define register variable */

        regs.x.ax = mode;            /* Set video mode */

        /* Call interrupt to change the mode */
        int86(0x10, &regs, &regs);

        regs.x.ax = 0xF00;           /* Get current video mode */

        /* Call interrupt to get the mode */
        int86(0x10, &regs, &regs);

        return (mode == regs.h.al);  /* Return changed condition */
    }

Why the changes?  Well, let's look at them one by one and analyze the reasons behind them.

The first change is where we got rid of the variable videoMem.  Not that using a global variable was a bad idea, it is just that variables can be changed, whether intentional or not.  If someone were to write code using your functions and accidently did this:

    videoMem = tempPtr;

When they meant to do this:

    tempPtr = videoMem;

Well, that could wreak havoc on any code, including the code stored in our library, that relied on the variable remaining a fixed value.  Now the chances of that actually happening are very slim, I know.  I realize that most people would know to leave videoMem alone.  Here's another reason, making videoMem a constant allows us to free the global variable space that was previously occupied by this variable.  Also, whenever a piece of code requires the value for videoMem, the value is place directly inline with the code, rather than having the microprocessor fetch the value from another location in RAM (saving a little, though finite, time).

For all practical purposes, either way is just as good.  Whether you choose to use the global variable or defined constant, the code will do the same thing and the differences in the code is so neglegible, you won't even notice.  So it's your call as to whether you want to do it or not.

The next change involves the function SetVideoMode.  It's a little longer and it returns a value now.  I highly recommend this change.  The function can now tell you if the change of the video mode was successful or not.  It does this by calling the BIOS function that gets the current video mode.  The assembly language listing looks like this:

    mov ah,0Fh     ; Get video mode.
    int 10h        ; Call interrupt for video mode.
                   ; Register AL now has the video mode.
    xor ah,ah      ; Zero most significant byte.
                   ; Register AX now has the video mode.

Basically, finding out what mode you're currently in takes two instructions:  put the number 15 (or 0F in hexadecimal) into register AH, then call interrupt service number 16 (10 in hexadecimal).  In our SetVideoMode function, we loaded AX with 0xF00.  This is because we wanted to make certain that AL did not contain the value we originally assigned to it using the mode variable.  This may seem silly, but it ensures that whatever value was found in AL actually came from the second interrupt call.  Also, loading AX with 0xF00 also loads AH with 0xF.  After the second interrupt call returns, we compare the value returned in AL with the value we passed to the function, and return the results of that comparison.

This allows our programs to detect whether the computer that it is running on is capable of displaying the video mode that we choose.  For example:

    #include "vmode.c"             /* Include source for testing */
    #include <conio.h>             /* For input / output functions */

    int main(void)
    {
        directvideo = 0;           /* Use BIOS for text output */

        cputs("Now in text mode.\r\nPress a key...");

        while(kbhit()) getch();    /* Clear the keyboard */
        while(!kbhit());           /* Wait for a key */

        if (!SetVideoMode(VGA256)) {
            cputs("You must have VGA graphics for this demo...");
            return 1;
        }

        cputs("Now in mode 13h.\r\nPress a key...");

        while(kbhit()) getch();    /* Clear the keyboard */
        while(!kbhit());           /* Wait for a key */

        SetVideoMode(TEXT_MODE);   /* Can test here too if you want. */

        cputs("Back in text mode...\r\nGoodbye...");
        while(kbhit()) getch();    /* Clear the keyboard */
        return 0;
    }

This will ensure that our programs won't try to modify video memory on a system whose graphics card isn't capable of VGA graphics.  By making a call to the BIOS to get the current video mode after an attempt to change it and comparing the results with one another, our programs can exit gracefully if the two values do not match.

You could, if you wanted, make a C code function that returns the resulting current video mode.  Here's one that does just that.

    int GetVideoMode(void)
    {
        union REGS regs;    /* Define register variable */

        regs.h.ah = 0xF;    /* Get current video mode */

        /* Call the interrupt to get the video mode */
        int86(0x10, &regs, &regs);

        return regs.h.al;   /* Return the results */
    }
Previous Page | Main Page | Next page

Send your questions, comments, or ideas to: garyneal@geocities.com