Sunday, September 22, 2019 | Toby Opferman
 

Super VGA Tutorial

Toby Opferman
http://www.opferman.net
programming@opferman.net




                How To Use Super VGA (VESA 1.x Non-Linear)



        First, we set the Video Mode we want, in our example,
we will be using 640x480x256 since it is a common mode.

        MOV AX, 04F02h      ; Set VESA Video Mode
        MOV BX, 101h        ; 640x480x256
        INT 10h             ; Video Interrupt
        CMP AX, 04Fh        ; AX = 04Fh ? 
        JNE MODE_ERROR      ;    If No Then Video Error

        We Call Function 4Fh and SubFunction 02h of Interrupt 10h.
Function 4Fh = Vesa Functions, SubFunction 02h = Set Video Mode

        Now, you have successfully set the Video Mode.  But, there
is one problem.  The Video Buffer is at A000:0000!  That is a 64k Segment.
640*480 > 64k!!!  So, how can you display shit on the screen if the video
screen isn't large enough to hold the image?  Well, there is something called
bank switching.

        Bank Switching allows you to switch to another Window of the screen.

         -----------------------------------------------------------|
         |                                                          |
         |                                                          |
         |                 Bank 0                                   |
         |                                                          |
         |----------------------------------------------------------|
         |                                                          |
         |                 Bank 1                                   |
         |                                                          |
         |                                                          |
         |----------------------------------------------------------|
         |                                                          |
         |                 Bank 2                                   |
         |                                                          |
         |                                                          |
         |----------------------------------------------------------|
         |                                                          |
         |                 Bank 3                                   |
         |                                                          |
         |                                                          |
         |----------------------------------------------------------|
         |                                                          |
         |                 Bank 4                                   |
         |----------------------------------------------------------|


        There is a SubFunction to Bank Switch, 05h.


        MOV AX, 04F05h       ; Bank Switch   
        XOR BX, BX           ; Page Number 0
        MOV DX, BankNumber   ; Bank Number
        INT 10h              ; Video Interrupt

  To Plot a Pixel, just let the Offset Wrap around.
in C, specify a SHORT type (65535) so that it will wrap around when
it hits the top marker and it will be in the correct position for the
plot.

  short Offset;
  Offset =  X + (Y*640);
      or
  Offset =  X + (Y<<7) + (Y<<9);

  This isn't too bad so far, but there is more.  Every video card
is differnt and doesn't lable the Banks 0, 1, 2, 3, 4 (Some Do).
It All depends on the Window Granularity.  Each Card has a Differnt Bank
Granularity.

To Get the BankNumber it is:
    BankNumber = (64/Window Granularity) * Bank;

Where Bank is 0 - 4

  To Get The Window Granularity, takes a call to SubFunction 1 of
VESA.  But, you need to allocate an entire structure because you get more
than just the window Granularity.

Here is a Example of one in C:

/* VESA Mode Information Structure */
typedef struct vesa_type {
    short ModeAttrib;
    char WindowA_Attrib, WindowB_Attrib;
    short WinGran, WinSize, SegWinA, SegWinB;
    long WindowPointer;
    short Bits_Per_Scanline, HorzRes, VertRes;
    
    char Width, Height, MemPlanes,
          BPP, NumBanks, MemType,
          SizeBank, NumberPages, Reserved,
          RedMaskSize, RedFieldPos, GreenMaskSize,
          GreenFieldSize, BlueMaskSize, BlueFieldSize,
          ReservedMaskSize, ReservedMaskPos,
          DirectColorModeInfo, ReservedBlock[216];
          
} VesaInfo, *VesaInfoPtr;

WinGran is the Window Granularity.

  Here is a call to get the Window Granularity:
     MOV AX, Seg VESAStructure    ; Get Structure Segment
     MOV ES, AX                   ; Set ES to Segment
     MOV DI, Offset VESAStructure ; Set DI To Offset
                                  ; ES:DI = VesaStructure

     MOV AX, 04F01h     ; VESA Mode Structure
     MOV CX, 101h       ; Mode 101h (640x480x256)
     INT 10h            ; Video Interrupt

Now you have the Video Granularity.

     VESAStructure.WinGran

You may now either BankSwitch = 64/VESAStructure.WinGran;
and do BankSwitch*Bank every time to get the BankNumber.
Or, The Window Granularity is a Power of 2. So, a divison
of 64 would give you a multiler that would really be like
shifting the bank.  The Following C switch statement illustrates.


    switch(VESA->WinGran)
    {
        case 1 : Shifter = 6;
        break;
        
        case 2 : Shifter = 5;
        break;
        
        case 4 : Shifter = 4;
        break;
        
        case 8 : Shifter = 3;
        break;
        
        case 16 : Shifter = 2;
        break;
        
        case 32 : Shifter = 1;
        break;
        
        case 64 : Shifter = 0;
        break;

        default:
          return 0xFF;  /* Error */
    }

  So, now to find out the BankNumber:

     Bank<<=Shifter;
     or
     BankNumber = Bank<<Shifter;

 That should be used for faster executable code.
 Now you know how to use VESA (8-bit Colour modes, for other modes,
 it is the same except the offset calculations should be differnt
 based on how many bits per pixel is used.)

 Now, you can plot to the screen using a double buffer, and every time
 you plot 64k, bank switch.  If you want to plot one pixel at a time, you will
 have to follow this form:

 long Offset;
 
 Offset = (Y<<9) + (Y<<7) + X;

 Now, the high word of Offset (Offset is 32 bit) will be the bank number.

 You see, there are 65536 bytes in 64k (0-65535), and, once you get to
 65536, you are actually in the next bank!  And that number is:
 10000h  Which is offset 0000h (Cause real mode offsets are 16 bit).
 
 Notice what we can do tho.  

 Offset = (Y<<9) + (Y<<7) + X;
 
 Calculate the offset in a 32 bit integer.  Then, to get the bank:


 Bank = Offset>>16; /* To get the Bank, we need the High Word of Offset */

 Offset&=0xFFFF;  /* The 16 Bit offset is the Low Word of Offset */

 And that's how it's done.  Then just

 BankSwitch as told above using the Shifter on Bank then calling the Interrupt.

 Then plot at A000h:Offset (16 bit Offset) your Colour.


 
About Toby Opferman

Professional software engineer with over 15 years...

Learn more »
Codeproject Articles

Programming related articles...

Articles »
Resume

Resume »
Contact

Email: codeproject(at)opferman(dot)com