                  |====================================|
                  |                                    |
                  |   TELEMACHOS proudly presents :    |
                  |                                    |
                  |    Part 2 of the PXDTECH serie -   |
                  |                                    |
                  |   Hi-/TrueColor GFX using VBE2.0   |
                  |                                    |
                  |====================================|

               ___---__-->   The PXDTECH serie   <--__---___

<><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><><>


INTRODUCTION
-------------

Hiya!
Welcome to the second tutorial in my new trainer series - the PXDTECH series!
It seems that people kinda like the idea of some units that they can rip off
and use in their own programs - so I descided to give you all a nice general
VBE 2.0 unit with support for 8,15,16,24 and 32 bit graphics. Yeah I know -
I'm such a nice person :)
Two lines above this one I mentioned a word that REALLY catches peoples
attention - VBE 2.0!!

Well - let me get one thing straight right away : IN TP7.0 WE CAN **NOT** SEE
THE DIFFERENCE BETWEEN THE VBE 1.2 INTERFACE AND THAT OF VBE 2.0!!!
(By this I obviously mean that we cannot really USE the new VBE 2.0 info in
TP - of course can access all the VBE 2.0 information)
I really can't say that enough times - the ONLY reason I use the word VBE 2.0
in the description of this tutorial is to make people downloading the doc 8-)

No - seriously, I guess the above lines need an explaination... The fact is
that people has been mailing me constantly (which really is'nt such a bad
thing - I really LIKE getting lots of mail... keep it up!) for information
on the VBE2.0 interface - and with this text I hope to straighten out the
threads which have caused so much confusion to the general coder..


As always get this series from the Peroxide homepage at :

http://peroxide.home.ml.org

(The homepage is now partly optimized for Shockwave FLASH - get it, it rulz!)

Also I think I'll start putting this series on the usual FTP-sites known from
the PXDTUT series.


Mail me at :

tm@image.dk





WHY ON EARTH DID HE SAY THAT VBE2.0 IS USELESS IN TP - VBE2.0 RULEZ!!??
------------------------------------------------------------------------

The reason for my heavy-weight statement in the introduction is that most
of the changes made from VBE1.2 to VBE2.0 only deals with protected mode
environments! The most important change is the support for what is called
LFB (Linear Frame Buffer). LFB means that we map the entire video memory to
a huge block of memory - laid out lineary (just like the good ol' mode 13h).
This means that we no longer need banks for accessing the different parts of
the video memory. We just calculate the linear 32bit address of the pixel
we wish to plot and SWAZOOOM! the pixel is right there on the screen!!
But stop - wait a second! How can we use 32bit linear addresses in Pascal?
If we wanted to plot pixel (300,400) in a 640 X 480 screen (8bit) the offset
would be 400 X 640 + 300 = 256300... How can we use this offset - it's larger
than the 64Kb we have per segment in real mode.

Well - we cannot.... which is why I say that VBE 2.0 is useless in TP!
If we really want to find use for LFB we need to switch to protected mode.
(well - it's not entirely true that we cannot use LFB in TP... but it
requires some external assembler code and we need to perform a few tricks to
get into what is called FLAT mode - but there are LOTS of things which really
is'nt cool in FLAT mode. So it's not really an alternative. If some of you guys
out there REALLY, REALLY, REALLY!! wants to do LFB in TP, then mail me and
I'll send you some source for FLAT mode and LFB in TP7.0)

Oh well - I almost forgot. We DO get something new from VBE2.0 in TP.
A new VESAInfo structure has been defined - so now we can access a few more
product ID strings - veeeeery nice, ehh ??   8)




FEATURES / REQUIREMENTS OF THIS DOC
------------------------------------


FEATURES :

This is what I'll give you in this doc :

   -  A nice generalized VBE unit dealing with all resolutions in all
      bit depths (hopefully on all gfx cards too - see note in the afterword)
      The routines won't be the fastest ones available because of numerous
      things :

         * The routines are VERY general. One single PutPixel routine deals
           with all resolutions and all bit depths. It would be faster to
           do optimized routines for specific resolutions/bit depths.

         * Only a little assembler code is used to ease the understanding
           of the code. If we really wanted to optimize we would have to do
           some 386 assembly code - which requires db $66's in TP.. and makes
           reading the code a living hell!

   - Explaination of the highcolor and truecolor memory layout!

   - Instructions on how to use the supplied routines!

   - At the end of this text you'll be one cool dude who really doesn't care
     about screen resolutions and bit depths. You'll (hopefully) fully
     understand the concept of high and truecolor and be able to program for
     them as easy as were it mode 13h!!!


REQUIREMENTS :

   - None... (as you can just rip the code and use it - but then you won't
     learn anything. Which really IS the purpose of a trainer, right? )

   - Recommended : Basic understanding of banked SVGA modes. I won't
     describe SVGA coding in this doc - I already DID a doc on SVGA. It's
     called PXDTUT5.ZIP (for pascal) and PXDTUT5c.ZIP (guess..)
     If you can answer these questions you know enough - else get my doc!

        * What is SVGA - and what has VESA to do with it?
        * What's all this talk about "banked mode" - what do I use banks for?
        * What is GRANULARITY - and why is it VERY important in SVGA code?
        * What has "Window size" to do with "Granularity"?
        * How do I plot a pixel in a standard 8bit VESA mode (fx. 640X480)?




OK - ON WITH IT!  WHAT'S ALL THIS TALK ABOUT HI-/TRUECOLOR ANYWAY??
--------------------------------------------------------------------

When entering the world of high/truecolor you put LOTS of problems behind you
(and finds lots of new ones to face ;) ) - most importantly is : NO MORE
PALLETTES!! You'll never again face the problem where you have'nt got room
for more different colors. Who has'nt tried standing there with 2 bitmaps in
mode 13h - and unfortunately they had different pallettes! You would have to
either find a common pallette for them - or reduce the number of colors in
them - both operations which greatly looses picture quality!

In hi-/truecolor the pallette is GONE, DEAD, FINISHED! There IS no such thing
as pallette. The good old 8bit modes are INDEXED modes - which means that each
color value (0 to 255) is used as an INDEX to a table containing the actual
(18bit) colors! We only have 256 colors because we only use one byte (8bit)
to index this table!

The hi-/truecolor modes are RGB modes - which means that we write the color
components directly to video-memory - instead of an index to a table.

In highcolor modes we use 15 or 16 bits to represent one pixel, which gives
us a total of 2^15 = 32768 (32K)  or  2^16 = 65536 (64K)  different colors!

In truecolor we use 24 or 32 bits per pixel. There is a catch though! The 32
bit mode is really a 24bit mode in disguise! In 24bit modes we split the bits
up so we use 8bits (one byte) for each RGB color component - often written
like this - 8:8:8
In 32bit modes we ALSO use 8bits per RGB component - and then we have an
unsused byte (we call it an alpha byte). This is often written as - 8:8:8:8
So in truecolor we have a total of 2^24 = 16777216 (16M) different colors!


So, why was 32bit modes invented anyway you might ask. Well - it can be hard
to understand the purpose of 32bit modes. But one obvious advantage from 24
bit modes is that we can write a pixel as a single Dword instead of a Word
followed by a Byte. (Another great thing about 32bit modes is that pixels
will never cross banks - more on this later!)



PACKED RGB VALUES
------------------

When we write to memory we do it byte by byte!
This is OK as long as we stay in the good old 8bit modes. And also in
truecolor modes as each RGB component is 1byte each.

But in highcolor modes where we use 15 or 16bits to represent a pixel we
have to pack the RGB data into a word!
(Even though we only use 15bits in 15bit modes a pixel occupies 16bits in
memory - just like the 16bit modes!)

The bits must be packed like this :

15bit :    1:5:5:5

bit pos :    16    15             10              5               0
               ---------------------------------------------------
              | /// |              |              |               |
              | /// |      R       |      G       |       B       |
              |_///_|______________|______________|_______________|


16bit :    5:6:5

              16             11                    5
bit pos :      ----------------------------------------------------
              |               |                    |               |
              |      R        |         G          |       B       |
              |_______________|____________________|_______________|



NOTE, NOTE, NOTE, NOTE!!!

THIS IS NOT **ALWAYS** TRUE! THE FIELDS MAY BE PLACED IN ANOTHER ORDER - FX
BGR - THE VESA SPECS WARNS ABOUT THIS! THAT IS WHY WE SHOULD CHECK THE
FOLLOWING FIELDS IN THE MODEINFOBLOCK :  - RedFieldPosition
                                         - GreenFieldPosition
                                         - BluePosition
BUT IT'S TRUE FOR **MOST** OF THE CARDS OUT THERE - PROBABLY ALSO YOURS. I
HAVE'NT YET SEEN A SINGLE CARD WHERE THE ORDER WAS'NT RGB!
IF YOU HAVE A CARD WHERE THE ORDER IS **NOT** RGB PLEASE MAIL ME!!!
I USE THE DRAWINGS ABOVE TO ILLUSTRATE HOW THE BITS ARE PACKED - NOT AS
GUIDELINES ON HOW TO DRAW THE RGB COMPONENTS!
IN THE SAMPLE PROGRAM PIXELS WILL ONLY BE PLOTTED IF THE COMPONENTS ARE IN
THIS ORDER!


OK - so how do we (assuming that the fields are RGB) pack the data?
If we use the good old R,G,B values from the 8bit modes we know that each
component ranges from 0 to 63 - ie. each component is 6bits wide!
We always use the MOST significant bits in a mode - so if we want to scale
a 6bit R value to the 5bits needed for 15 and 16bit modes we do a SHR 1 on
it!

I'll show you a little routine to pack 3 RGB bytes into a word ready for a
16bit mode :


FUNCTION Conv16b(R,G,B : byte) : word;
Assembler;  { 16bit colors are 5:6:5 }
asm
 mov bl, [R]
 shr bx, 1    {here we scale the R value to 5bits}
 shl bx,11    {and shift it 11 places to the left (remember - that was the }
              {correct starting position for the R component. Check the    }
              {drawing above...)                                           }

 mov ax,bx    {OK - ax is our target register - now it contains the R value}
 mov bl, [G]
 xor bh,bh
 shl bx, 5    {we do not scale G as it should be 6bits wide, place it at 5 }
 add ax, bx   {add the G component to the final word                       }
 mov bl, [B]
 xor bh,bh
 shr bx, 1    {The B value must be scaled as the R value                   }
 add ax,bx    {add B to ax and we have packed the RGB correctly into a word}
END;


A similar routine to pack R,G,B values into 15bits can be found in the unit.
It's called Conv15b - I'll leave it as an exercise for you to understand it :)





OK - WE'RE READY TO ACTUALLY PLOT THE PIXELS NOW!!
--------------------------------------------------

OK - on with the show, we need to get some graphics on the screen now! First
thing to say, so it won't confuse you all later : until now I have talked about
the order of the color components as RGB (assuming standard component order),
but as you'll see we write the components in this order : BGR - this is because
that even though we look at the pixels as RGB when we draw ASCII's like this

 bit pos    24             16               8                0
             |--------------|---------------|----------------|
             |      R       |       G       |       B        |
             |______________|_______________|________________|

                    (the format of a standard 24bit pixel)


it's pretty clear that we have to draw the B-byte first, as it's the
component with the smallest starting bit-number.

Now that I have this neat ASCII drawing above I think I'll use it to explain
how to plot a pixel in hi-/truecolor.
As mentioned before a 24bit pixel takes up 24bits = 3 bytes in video-memory.
The memory is laid out like this :

Xpos:(pixel)     0   1   2   3   4   5       (continues)      640
               |RGB|RGB|RGB|RGB|RGB|RGB|.....................|RGB|
Offset :        0   3   6   9   12  15                        1920

So what we do is calculate the offset for the first (R) component and then
plot 3bytes from there!


**************************************************************************
*********** FIRST TRAP IS RIGHT HERE - READ THIS CAREFULLY!!! ************

So, how do we calculate the offset? Well, the same way as we did in 8bit
modes (almost). What was the method in fx. 640X480X8bit... to plot pixel
(x,y) we did :

     offset :=  y * 640 + x;

or more generally - for any mode in 8bit :

     offset := y * XResolution + x;

So, one might think : now I use 3 bytes for each pixel so I'll address the
old offset * 3 - that is :

    offset := y * Xresolution * 3 + (x * 3);

Do that, and it MIGHT work - and it might not!!! Why? Well, many people makes
this error these days - most often seen in the otherwise impressive demos.
Have you never tried to run a demo, and somehow the screen messed up??
This is because the programmers were to stupid to code their own VBE routines
and then they use some crappy VBE library which does NOT take the field from
the VESAModeInfo block called "BytesPerScanline" into considerations ;)

I think you have guessed by now what I'm trying to get to - NEVER assume that
the amount of bytes needed for 1 scanline of graphic has ANYTHING to do with
either the Xresolution or the bitdepth!
Different graphic cards have different BytesPerScanLine values for their
modes. It's EXTRA dangerous when you use the more "uncommon" modes as fx :

320 X 240 X 15bit,   300 X 400 X 32 bit and so on.....

So, as a general rule calculate offsets like this :

offset := y * VESAModeInfo.BytesPerScanLine + X * BYTES_per_pixel;
(you would often NOT calculate X*BYTES_per_pixel, but instead do :
 BytesPerScanLine + X + X + X; for 24bit modes and so on.)


Also, if you use the VBE function called "SetScalineLength" be sure to check
what value the scanline was actually set to! Some cards does not allow you to
set the BytesPerScanLine to just any number! Some cards might even refuse to
change the BytesPerScanLine at all!!


*************************   END OF TRAP 1 *************************
*******************************************************************


Phew... Got past trap one. Now YOU won't end up coding yet another crappy
demo which won't run on MY system :)
But this is a dangerous part of this tutorial so barely out of trap 1 we
blindly wander into............
















************************************************************************
*******   TRAP TWO - THE CURSE OF THE OVERFLOWING 24BIT MODE!   ********

ARGH! Now what is THIS ??

Well - I never said hi-/truecolor was easy! Let's take some time to analyze
the different bit-depths.
At this time some of you might have wondered : Well - how can we be sure that
we won't have to change bank in the middle of plotting a pixel? And what
happens if we forget to do so?


15/16 bits per pixel (BPP) :
---------------------------
A segment of memory equals  2^16 bytes = 65536 bytes.
In 15 and 16 BPP we use TWO bytes per pixel - so quick calculations reveals
that there is 65536 / 2 = 32768 pixels per segment. Phew - no bankswitching
in pixels in those modes.


32 BPP :
--------
In 32bit we use 4bytes per pixel - so we get 65536 / 4 = 16384 pixels per
segment. ALL RIGHT!! No bankswitch here neither!

24 BPP :
--------
In 24bit we use 3bytes per pixel - so here we get 65536 / 3 = 21845.3333
pixels per segment!  ARRRGH!  Once every 21845'nd pixel we have to change
bank in the middle of a pixel! This will BTW only happen on cards with a
64Kb granularity - on cards with smaller granularities the "distance" from
a pixel to the beginning of the bank in which the pixel lies will never be
greater than the granularity - and thus we will never have a segment
overflow! (think about it..)
But our code has to deal with this 24BPP problem - so we have to check if
a pixel crosses banks before plotting it - check out the sample program for
more detailed info on how to do this!

If we forget to check for this we will cause several register overflows
with our STOSW and STOSB - remember that these asm commands automatically
increases the DI register when called! The result : the computer will most
likely hang :)



*************************** END OF TRAP TWO ******************************
**************************************************************************


One last thing about pixel plotting : When in 24/32bit modes the RGB
components are 8bits each, making the maximum values 255 - instead of the
normal 6bit values (ranging from 0 to 63) we know from 8bit modes.
But in 15/16bit modes the RGB components are often scaled to 5bits.

What I try to say is that you should always operate with 255 as maximum
values in 24/32bit modes but only use 63 in 15/16bit modes!

Oh, and before I forget! The same rules applies to VESA coding as to the good
old mode 13h. Always try to increment offset values instead of recalculating
an offset that is 1 greater than the last offset we used :)
It's ALWAYS slow to use Putpixel to draw pixels if there is just TWO pixels
next to each other! So if you're coding with sprites try to draw one whole
scanline of the sprite at at time - increasing the offset instead of
calculating it for each pixel!
Also, the Conv15b and Conv16b routines are NOT something you should call each
pixel - instead try to use 15/16 bit data (textures and so...) when in 15bit
modes!

I have a nice trick for you : If you're working with 8bit images in a 15/16bit
mode you can create what I call a "virtual pallette" for the images - ie. a
table containing the 256 8bit pallette entries "translated" into words.
This way each 8bit image can have it's own pallette, and you can plot the
8bit pixels QUICK in 15/16bit modes by looking the word values up in the
tables!



WHEW!!  GOT PAST THAT UGLY PUTPIXEL PART - WHAT NOW?
-----------------------------------------------------

Well, I think now is the time to say a (FEW) words about the little library
I have included with this text!


 * The library CAN be very general and easy to use - in theory you could
   get by with only 1 Putpixel, 1 Clear and 1 Setmode.
   But there are a few other features available to those who wants it.


 * There is some initialization code which is executed automatically when
   you start a program which uses the unit. This includes :
      - Checks if VESA is available, and requests VBE information
      - Scans through the modes and makes a list of available modes.
        This list is called ModeLookupTable and is an array of the following
        record type :
                        ModeRecordT = Record
                          Modenr : word;
                          XResolution  : word;
                          YResolution  : word;
                          BitsPerPixel : byte;
                        End;

      - Initializes the variable TMVESA_NR_MODES; so you know when to stop
        when you scan through the ModeLookUpTable :)


 * SetUpVESAModeNr(nr : word) : Sets a specific VESA mode based on the mode
   number. (not recommended - see function below!)

 * SetUpVESAMode(Xres, Yres : word; BPP : byte) : Searches for the mode
   specified and sets it. (Preferred because mode numbers might change with
   different VBE/SDD versions)

 * ShutVESADown : Sets the good old textmode.

 * There are a few GLOBAL variables available in the unit. They all start
   with the word TMVESA to keep them from matching names YOU might have used.
   They are :
     - TMVESA_RGB_MODE : BOOLEAN,  Shows if the active mode is a direct color
       mode (hi-/truecolor) and if the RGB components are in the right order
       on the board. If they are not, you should exit your program or modify
       the routines to handle different RGB placements.
       Should be considered a READ ONLY variable for you!

     - TMVESA_INDEXED_MODE : BOOLEAN, shows if we are in a 8bit mode.
       Also READ ONLY.

     - TMVESA_BIT_DEPTH : BYTE, shows the bit depth of the current mode.
       READ ONLY

     - TMVESA_MODE_CORRECTION : BOOLEAN, now this is a small feature I added
       because of some problems with my Matrox Millenium. Somehow the BIOS
       reported some 15bit modes as being 16bit modes - so if this switch is
       set (by default it is) the unit will scan through the ModeLookUpTable
       to find so called "safe modes" - ie. modenumbers where we are pretty
       sure we KNOW what the bit-depth is. It will then insert the correct
       values into the ModeLookUpTable.
       Some of you might not want this, or it might cause problems on other
       cards - so to disable just set this variable to false in the unit-code.


 * Putpixels :
      - VESAPutPixel(x,y, R,G,B) : Works in ALL resolutions and ALL bit
        depths. This routine is easy to use - but of course not as fast as
        the not-so-generalized ones! One thing to notice though - YOU are
        responsible for passing the correct information to the routine. IE.
        as we talked about : correct maximum values of RGB.
        NOTE! In 8bit mode the color-index must be passed in R
        (fx. VESAPutPixel(25,25, 55, 0, 0) will plot color 55);

      - VESAPutPixel8b (x,y : integer; color : byte) : 8bit putpixel.

      - VESAPutPixel16b(x,y : integer; color : word) : 15/16bit putpixel.
        You must must convert the RGB values to the right format depending
        on which mode you are in by using Conv15b(R,G,B) or Conv16b(R,G,B).
        (Fx - VESAPutPixel(25,25, Conv15b(45, 34, 20)); )

      - VESAPutPixel24b (x,y : integer; R,G,B : byte) : 24bit putpixel.
        Call this with 255 as maximum R,G,B values!

      - VESAPutPixel32b (x,y : integer; R,G,B : byte) : 32bit putpixel.
        Call this with 255 as maximum R,G,B values!

 * VESAClearScreen; : guess this one for your self!



FINAL WORDS
------------

Well, that's about all for now.
Hope you found this doc useful - and BTW : If you DO make anything public using
these techniques please mention me in your greets or where ever you see fit.
I DO love to see my name in a greeting :=)

This is BTW a very important release for me :) It's the 10'th trainer article
(8 PXDTUTS and 2 PXDTECHS)... 10 is a nice number, it's a goal to strive for,
somehow 10 is so much bigger a number than fx. 9 :)
Who would have thought that I would reach that far when the first tut were
released - not me, I can assure you :) Well, that was 10 down the drain -
on with the NEXT 10 :)

If any of you should care : I have now fully devoted the next 5 years of my
life to the sweet art of math and programming :) I have started on the
university of Aarhus (a "big" city in Denmark), where I'm currently studying
Linear Algebra, Complex functions and numbers and JAVA programming.
It's tough - but I kinda like it anyway :)



What I have learned from doing VESA code is that even though you THINK that
THIS time you have made the perfect, 100% bugfree VESA code you always ends
up with people who cannot use the code!
I'm pretty sure that will also happen this time. I have really tried to test
this code on as many graphic boards I could get my hands on, using different
system setups and all.
And even though 95% of the modes works perfectly there are still a few things
which really puzzles me :)

For example :

I had my good buddie X-oTiC^Subtrance test the routines (especially the 24bit
modes which are unavailable on MY card). What he told me was that ALL modes,
in ALL resolutions worked perfectly. Then he installed Scitech Display doctor
- now all modes worked EXCEPT 640X480X24bit, which puts the screen in
Sleep-mode?? (OK - I know the testscreen is boring, but still... :)

On another friend's computer the 640X480X24bit worked perfectly - but there
the card could not handle the 15bit modes.

On my own computer there is no trouble at all with any modes - both with and
without Scitech Display Doctor - except that the card reports the 15 bit modes
as being 16bit modes!

So, bottom line is : go experiment. Use Display Doctor if you have to, but
if the mode you need is available without it, then DON'T install it!
If you experience any problems please mail me, so that perhaps we can fix
them together. (As a side effect of this, this document might be updated
on the homepage once in a while! Check!)


Hmm... next time you'll hear from me will probably be from the PXDTUT series
where I'm going to do another 3d tutorial on advanced matrix rotations,
camera and that kind of stuff..
or you'll get a PXDTECH on Sound Blaster..





Until then - have fun

Telemachos^PXD
(16/9/1998)


Greetz goes to :

X-oTiC^SubTranCe : for taking the time to test the VBE code...
Dieffel^Lost     : for being a nice guy - and for once giving that meltgoa.pas
                   (BTW - your new demo puts MY screen in sleepmode :)
Ecco             : For being a nice guy, and for running the official PXD
                   BBS in Holland....
Xray, Pop        : For nice email conversations :)
Vulture^OT       : For being a nice person & for being a fellow tut-writer!

All at DMC and comp.lang.pascal.borland



