By Aaron Severn
This article is very advanced. I recommend using it only if you know a LOT about Qbasic- editor. Continued from Issue 1
2.3 Setting a screen mode
-------------------------
Once you have confirmed the presence of VESA support,
the next step is to set a mode. For a list of VESA defined screen modes
see appendix B. The set SVGA mode function is function &H02. You
will also need to run function &H01 to return info on the mode.
Like function &H00 it will fill a variable with info, this time on
the screen mode. The following is the type definition for the info
returned by function &H01.
TYPE ModeInfoBlock
ModeAttributes AS INTEGER
WinAAttributes AS STRING * 1
WinBAttributes AS STRING * 1
WinGranularity AS INTEGER
WinSize AS INTEGER
WinASegment AS INTEGER
WinBSegment AS INTEGER
WinFuncPtr AS LONG
BytesPerScanLine AS INTEGER
XResolution AS INTEGER
YResolution AS INTEGER
XCharSize AS STRING * 1
YCharSize AS STRING * 1
NumberOfPlanes AS STRING * 1
BitsPerPixel AS STRING * 1
NumberOfBanks AS STRING * 1
MemoryModel AS STRING * 1
BankSize AS STRING * 1
NumberOfImagePages AS STRING * 1
Rsvd AS STRING * 1
RedMaskSize AS STRING * 1
RedFieldPosition AS STRING * 1
GreenMaskSize AS STRING * 1
GreenFieldPosition AS STRING * 1
BlueMaskSize AS STRING * 1
BlueFieldPosition AS STRING * 1
RsvdMaskSize AS STRING * 1
DirectColorModeInfo AS STRING * 1
Reserved AS STRING * 216
END TYPE
For a complete description of what everything is for see appendix A. The important fields are as follows.
ModeAttributes - will let you know if the mode is available or not
WinGranularity - either 4 or 64, this defines the size of the banks
in the mode, more on this later.
WinASegment - the start segment of the graphics buffer, usually
&HA000
XResolution - speaks for itself
YResolution - what do you think?
BitsPerPixel - how many bits are required for one pixel, useful in
determining how many colours are available
colours = 2 ^ BitsPerPixel
NumberOfImagePages - the number of video memory pages available in the
mode, this changes from computer to computer
depending on how much video memory is available.
This value is actually the number of pages minus 1.
So, anyway, when setting an SVGA screen mode, the following should be done.
Again, sample code is available in appendix D.
1. DIM a variable of type ModeInfoBlock.
2. Set AX to &H4F01, VESA function &H01.
3. Set CX to the mode number (listed in appendix B).
4. Set ES to the segment address of the ModeInfoBlock variable.
5. Set DI to the offset address of the ModeInfoBlock variable.
6. Generate interrupt &H10, remember to use CALL INTERRUPTX for ES.
7. Check (ModeAttributes AND 1), this should be 0.
8. Set AX to &H4F02, VESA function &H02.
9. Set BX to the mode number.
10. Generate interrupt &H10.
11. Check the value returned in AX, it should be &H4F.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
3 Basic Graphics Functions
--------------------------
3.1 Setting pixels
------------------
3.1.1 Bank switching
--------------------
Remember way back in the introduction when I explained that you only had
64k of video memory in the early days of PC graphics. Well, in some ways
that hasn't changed. Now you can only have 64k of video memory at a time.
Since SVGA modes often require well over a megabyte of video memory, this
creates a problem. You can't possibly have access to all the video memory
at once. The solution is bank switching.
The SVGA video memory is arranged in banks of either 4k or 64k (determined
by the value in WinGranularity). When working with SVGA, you have to choose
which bank you want to write to. The VESA supplies a nice little function
that will let you do just that. It is VESA function &H05 and works as
follows.
1. Set AX to &H4F05.
2. BX does have a purpose, but generally you don't need to use it, thus
set BX to 0.
3. Set DX to the bank you want to write to.
4. Generate interrupt &H10.
There is one further detail that should be
discussed about bank switching. The window granularity plays a big part
in it. Some cards have 4k banks for the purpose of faster bank
switching, I won't go into detail on how this can be achieved.
Unfortunately, a lot of cards use 64k granularity, so if you write the
software specifically for 4k granularity it won't work on many
computers. Thus it's a good idea to simulate a 64k bank when you have a
4k bank. To simulate 64k banks in all granularities, divide 64 by
WinGranularity and save this value for later use (winGran = 64 /
WinGranularity). Then when switching banks, multiply the bank you want
by the value you calculated (bank = winGran * bank) and use that value
in DX. This will effectively simulate 64k banks even if 4k banks are in
use.
3.1.2 256 colour modes
----------------------
Pixels in 256 colour SVGA modes are stored
exactly like they are in VGA mode &H13 (SCREEN 13 in QBasic), one
byte = one pixel with pixels colours defined in the same palette that
mode &H13 uses. The only difference is the bank switching that must
be performed as explained above. So here's what you should do.
1. Make sure that the default segment set by DEF SEG is pointing at the
graphics buffer (usually &HA000, the value is contained in the field
WinASegment of the ModeInfoBlock).
2. Calculate the offset of the pixel the same way that you would in
SCREEN 13 (offset& = y * xRes + x). Note that offset& will be a
long integer, and must be treated as such.
3. Find out what bank the pixel is on by dividing the offset by &H10000,
effectively a bit shift of 16 to the left (bank = offset& \ &H10000).
Note integer division is used (backslash).
4. Compare the value found for the bank with a variable that holds the
current bank. If it is different then perform a bank switch the way
described above. Use the method that simulates 64k banks even if
they are 4k.
5. Change the offset so that it is within the range of an unsigned
integer (offset& = offset& AND &HFFFF&). Note that since QBasic uses
signed integers, you will still need to hold the offset in a long
integer variable.
6. POKE the colour at the desired offset.
Again, sample code is available in appendix D if you need help.
3.1.3 Direct colour modes
-------------------------
Direct colour modes are the SVGA modes that
allow up to 16.8 million colours. This section will discuss two types
of these modes, the 15-bit modes (32k colours) and the 16-bit modes
(64k colours). The name direct colour was chosen for a reason. Unlike
256 colour modes which have a customizable palette that can hold 256
different colours, direct colour modes have no palette but instead
accept red, green, and blue components (rgb) directly. In 15-bit modes,
each component is 5 bits long, thus it can have any value from 0 to 31,
with one bit left unused. 16-bit modes use the extra bit in the green
component which is 6 bits long and can have any value from 0 to 63.
Both of these mode types require 2 bytes of memory for each pixel, for
that reason the simple offset = y * xRes + x formula won't work.
Instead you need to use offset = (y * xRes * 2) + (x * 2). The
multiplications by 2 adjust for the fact that each pixel fills 2 bytes.
Other than that, setting pixels is the same as in 256 colour modes
until it comes to writing the actual values to the video memory. You
need to combine the red, green, and blue components into two bytes and
POKE them both in one after the other. Here's how to combine the
components.
For 15-bit modes:
highByte = red * 4 + green \ 8
lowByte = (green AND 7) * 32 + blue
For 16-bit modes:
highByte = red * 8 + green \ 8
lowByte = (green AND 7) * 32 + blue
That's just a series of bit shifts to put
the components in their proper places. Then you just POKE them in at
the calculated offset like so.
POKE offset&, lowByte
POKE offset& + 1, highByte
And that's how to set pixels in 15-bit and 16-bit direct colour modes.
3.2 Page flipping
-----------------
In order to achieve good smooth animation,
page flipping is almost a must. Many of us, though, don't know what it
is since we've grown up using QBasic's SCREEN 13 for the 256 colours,
which doesn't support page flipping. The concept is pretty simple,
usually screen modes don't use up the entire video memory, and often
they use less than half. Rather than let the rest of the memory go to
waste, it can be used a little like a second screen. You display one
page while drawing graphics on the other, then you display the page you
were drawing on and start drawing on the other one, and just keep on
swapping pages. The result is, the user can't see you drawing the
graphics so animation looks more smooth. As explained before, the
number of pages available in each screen mode depends on how much video
memory is available. To get the number of pages available take the
value in NumberOfImagePages of the ModeInfoBlock and add 1.
3.2.1 Setting the active page
-----------------------------
This is done with a bit of a trick. There's
no function that will choose which page you want to write to. You can
write to any page at any time. So here's the trick, when writing to
page 0, y = y, when writing to page 1, y = y + yResolution, when
writing to page 2, y = y + yResolution * 2, and so on. In general, y =
y + yResolution * page. This is really all you need to do.
3.2.2 Setting the visible page
------------------------------
Now this is a little more complicated.
There's a VESA function that lets you choose the first vertical scan
line that you want to view. In order to view page 0 this value would be
0, for page 1 it would be yResolution, for page 2 it would be
yResolution * 2, and so on. Again, in general terms firstLine = page *
yResolution. So here's the details.
1. Set AX to &H4F07, VESA function &H07.
2. Set BX and CX to 0. These do have a purpose, but for page flipping
they should be 0.
3. Set DX to page * yResolution.
4. Generate interrupt &H10.
3.3 Getting enough speed
------------------------
The pixel setting graphics functions
described above will work just fine, however they will be way too slow
for most practical purposes. The only solution is to write other, more
useful functions in assembly language. Then you will be able to achieve
enough speed. An example of a put routine for 256 colour SVGA modes is
available in appendix D as an example of what must be done to make
useful SVGA graphics functions. If you really know what you're doing,
you should be able to write your own such functions. If not, well I
guess you'll have to learn. If you've read all the way down to here you
must be committed to learning this, or you should be committed. Either
way, I hope this helped. Next issue, we'll provide some assembly code
and...an actual program for Qbasic SVGA!
Tune in!
Back to Top
This tutorial originally appeared in QBasic: The Magazine Issue 2.