Using Graphics with Basic
Click here if you are stuck in someone else's frames.


If this page looks like garbage then you need one of these browsers...

Microsoft Internet Explorer

Netscape Navigator

Please sign my programmer's page guestbook.

Please sign my guestbook

View my guestbook entries

Understanding Graphics

In order to write good games, or to write any program that takes advantage of the graphics system of a computer, we need at the very least a basic understanding of graphics. Using BASIC, there is not a whole lot of machine level detail that we need to go into, you can for the most part, use the graphics functions that are made available by the language itself and save yourself much of the trouble of 're-inventing' the wheel.

However, you may find that some of the graphics functions supplied with the language are not completely adequate. In those cases we will have to go to a lower machine level programming to get around these limitations.


The graphics screen

The graphics screen that you choose is for the most part of your own choice as I have said before, when using BASIC, the commands themselves are real simple. However, many programmer's of games seem to prefer the screen mode 13H. This is equivalent to QBasic's SCREEN 13 and is a 320x200x256 VGA screen. The resolution is grainy compared to today's standards, but the biggest reason most programmer's choose it is because of it's ease of use in screen rendering.

The main properties that are real desirable in screen mode 13H (SCREEN 13) is the fact that the pixels are laid out sequentially in memory, byte by byte. That is, each pixel is equal to one byte and the bytes are laid out in the 320x200 grid just as the screen resolution itself. The screen occupies 64000 bytes of memory (320x200 = 64000) and the first pixel starts at memory location A000:0000.

This example can help clarify how the screen is laid out in memory, you can run this example using either the QBasic interpreter or the QuickBASIC compiler directly. Try it out and you should get a screen full of random colored dots.

' The example code of how to use BASIC's POKE statement
' to set pixel's in screen mode 13H (SCREEN 13 in QBasic)

DECLARE SUB SetPixel(xPos&, yPos&, col%)

SCREEN 13

CLS
WHILE INKEY$ <> "": WEND   ' Clear keyboard

DO UNTIL INKEY$ = CHR$(27) ' Press [ESC] to quit
  SetPixel RND * 320, RND * 200, RND * 256
LOOP
END

SUB setPixel(xPos&, yPos&, col%)
  DEF SEG = &HA000

  POKE yPos& * 320 + xPos&, col%
  DEF SEG
END SUB
    

This may seem a bit contrived, after all, you could've just as easily used the PSET statement to totally replace the SUB procedure, but this knowledge does open up some posibilities of using C or ASM code to help augment your BASIC code. You can write some more sophisticated screen rendering routines using a lower level language that can run like blazes compared to BASIC. However, if you plan to just stick with BASIC for all of your programming and nothing else. This won't mean much at all.

Regardless of your BASIC programming strategy, I would recommend that that you'd just stick with using PSET when setting pixels from within BASIC. Trust me, it runs much faster than the SUB procedure above will.

You are free to use any screen mode that you desire, however, most of the samples shown here will use screen mode 13H so I figured that it would be helpful that I would make you familiar with that mode.


Mode 13H? What's with the H?

Okay, I guess I better let on with the H part for those of you who may not know what it is all about. Actually, the screen mode that we are using is really screen mode 19 to the BIOS system. One thing I feel that I should point out is that obviously other versions of BASIC for DOS (Turbo Basic or Power Basic) may not be compatible with QBasic's (or QuickBASIC's) SCREEN 13. It may be a different screen number or a totally different command altogether to switch to the BIOS's screen mode 13H (or 19). The H, if you haven't figured it out yet, stands for hexadecimal and 13H is the number 19 in hexadecimal.

So, what if the version of BASIC you're using does not support the SCREEN statement or the VGA mode? Heaven forbid, what if you're using GW-BASIC?!? Okay, let's take a quick look at how the BIOS is actually told to switch screen modes, since the SCREEN statement is a QBasic function.

Okay, if your version of BASIC supports interrupt calls to the system, then you can code a function that looks similar to this.

' This is an example of how to use QuickBasic to switch
' to mode 13H using BIOS.  This example will not work
' under QBasic.
'
' Some other versions of BASIC require that you use
' Int86 instead of
' INTERRUPT.

' $INCLUDE: 'QB.BI'

DIM Regs AS RegType

' Screen mode to switch (mode 13H)
Regs.ax = &H13
' Switch screen mode
CALL INTERRUPT(&H10, Regs, Regs)

' Show a single blue dot in the middle of the screen.

DEF SEG = &HA000
POKE 100 * 320 + 160, 1
DEF SEG

SLEEP

' Screen mode to switch (text mode)
Regs.ax = &H3
' Switch screen mode
CALL INTERRUPT(&H10, Regs, Regs)
END
    

This example, when run under QuickBASIC (cannot run under QBasic) will show a single blue dot in the VGA mode, press a key to return to the text mode. Note that the print statements will not function correctly when you by-pass BASIC's SCREEN statements for switching the screen modes.

This example seems rather pointless though if you have a BASIC that already supports the VGA 320x200x256 mode with it's own commands. For you GW-BASIC programmer's (are there really any left?), here's a way you can get to mode 13H with assembly.

; Assembler code to activate mode 13H

mov    ax,13h  ; Screen mode 13H
int    10h     ; switch to the screen mode
ret
    

You can use GW-BASIC's SCREEN 0 statement to return to the text mode (or replace the 13h in the mov command to 3h). Be careful when using this assembly listing, it has not been tested but this snippet should work although you might need some extra code for the CALL procedure. You can use either CALL, or DEF USR (Hey, I used to use that on my old CoCo!!!)

You're going to have a real tough time using GW-Basic to code programs that write to VGA. In order to set pixels in the VGA screen buffer, you need to be able to store values in memory locations A000:0000 to A000:F9FF. The only way to accomplish this in BASIC is with the POKE statement, but that will now work in GW-Basic since you need to be able to do it in the A000 segment and GW-Basic does not support QBasic's DEF SEG command.

If you are one of the unfortunate few that still live in GW-Basic land, time to move on to QBasic so that you can be up to speed with us. I do not plan to offer much more help for GW-Basic users because to me the languuage is just too dated to even be consider.


The VGA Palette

Before we wrap up discussion on the graphics system and our preffered video mode (13H) that has been shown, there is still one more point to look over. That is the VGA's Palette itself. Now most programmer's fluent with QBasic and QuickBASIC alike is familiar with the PALETTE statement, QuickBASIC programmer's were also given a little hint in the online help as to how to properly use it in the VGA modes. (That minor little detail was left out for you QBasic user's.) Well here it is for those of you using the freebie interpreter that came with DOS 5.0.

In the VGA 256 mode you have three color registers for each pixel, a blue, a green, and a red. Each of these registers could hold a value between 0 and 63. To use this in the QBasic's PALETTE statement, you pass a LONG integer to the color parameter as listed below.

' Changing the color PALETTE of the VGA screen

DIM Blue AS LONG
DIM Green AS LONG
DIM Red AS LONG

DIM palColor AS LONG

' Switch to screen 13H (using the QBasic function)

SCREEN 13

' Let's make palette one WHITE

Blue = 63
Green = 63
Red = 63

palColor = Blue * 65536 + Green * 256 + Red
PALETTE 1, palColor

' Draw a line with the new color
LINE (0, 0)-(319, 199), 1
END
    

That should work in both QBasic and QuickBASIC, now I feel I kind of went too quickly here and may need to back up a little for those of you who have no earthly idea of what a palette is.

Essientally, when you plot a pixel on the screen and identify it's color, the number you use to identify it's color is not the actual color of the pixel. For example, in mode 13H, when we use the statement...

PSET (160, 100), 1
    

one is not the actual color code, it instead refers to a color code or an index in the VGA's palette table. By default, palette one is set to blue within the VGA card. In mode 13H, the VGA card has 256 of these palette entries indexed from 0 to 255. Mode 13H can display 256 colors on the screen at one time, however a single palette entry can display one of 262,144 different colors (or 256K colors). Since there are only 256 palette entries, only 256 of these colors can be shown on the screen at any given time.

As stated above, each palette consists of three color registers, each that can hold a value of 0 to 63 or up to 64 different values. If you multiply the 64 three times (64 x 64 x 64) you will arrive at the 256K colors possible for a single pixel. Using QBasic, you can set each color palette using the PALETTE statement shown above.


Accessing the Palette directly

Of course, I'm sure some of you folks in GW-Basic land want to know if it is even possible to do this in your chosen language. Of course, in fact, the code snippets I show below will work for you QBasic programmer's too. In fact, I have encounter some QBasic interpreter's that will not use the PALETTE statement to set the blue and green color registers (only the red). Of course this is not a problem in QuickBASIC.

To change these palette entries without using the PALETTE statement, we must communicate with the VGA card through the I/O ports. These are the four ports that we use to accomplish this.

PM = &H3C6
PR = &H3C7
PW = &H3C8
PD = &H3C9
    

Okay, a little explanation on those 2 character GW-Basic variables. The PM variable holds the port address for the VGA's palette mask port. The PR variable holds the address for the VGA's palette read port. The PW variable holds the address to the VGA's palette write port. Finally, the PD variable holds the address to the VGA's palette data port.

To implement these variables we must 'talk' to the VGA card in the following way:

  1. We must inform the VGA card that we want to access it's color registers. We do that by sending 255 to the palette mask port (PM).
    OUT PM, 255 -or- OUT &H3C6, &HFF

  2. Tell the VGA card which palette we want to access.
    If we want to change (write to) the palette we use
    OUT PW, n -or- OUT &H3C8, n
    Whereas n is the number of the palette that we wish to change.

    Of course, we can read the palette entry as well (not possible using a QBasic graphics function). To tell the card which entry we want to read you would use the following statement instead:
    OUT PR, n -or- OUT &H3C7, n
    Whereas n is the number of the palette that we wish to read.

  3. Now we can read or write the data, depending on whether or not we wish to read or write the palette we would either use the OUT statement with the VGA's palette data port (PD) or use the INP function instead.

Let's put together a couple of code snippets to do each, first, we'll save the value of the palette number one (by default it is blue), then change the palette color to WHITE, then draw a line in that color. Then restore that palette color and draw another line in it's restore color.

' This code was written for QBasic, but will function
' in GW-Basic by adding line numbers.

' This code assumes that the screen was already put
' in mode 13H VGA. In QBasic, you can use the SCREEN 13
' statement.  In GW-Basic, you will need to make a
' machine language subroutine call to the assembled
' assembly code shown earlier.

' First, let's get the value of palette number one.

' Tell the VGA that we wish to access the palette
OUT &H3C6, &HFF

' Tell VGA that we wish to read palette 1
OUT &H3C7, 1

' Now let's read the palette.
'  Please note that it is imperative that we read the
'  DATA port 3 times or the VGA card may go into an
'  unpredictable state.
ORed = INP(&H3C9)
OGreen = INP(&H3C9)
OBlue = INP(&H3C9)

' Next, let's change palette one to WHITE.

Red = 63
Green = 63
Blue = 63

' Tell the VGA that we wish to access the palette
OUT &H3C6, &HFF

' Tell VGA that we wish to change palette 1
OUT &H3C8, 1

' Now write the three color values
'  Please note that the same port is used to send
'  each color value
OUT &H3C9, Red
OUT &H3C9, Green
OUT &H3C9, Blue

' Now draw the line
LINE (0, 0)-(319, 199), 1

' Now let's restore palette one back to it's old color

' Tell the VGA that we wish to access the palette
OUT &H3C6, &HFF

' Tell VGA that we wish to change palette 1
OUT &H3C8, 1
' Now write the three color values
'  Please note that the same port is used to send
'  each color value
OUT &H3C9, ORed
OUT &H3C9, OGreen
OUT &H3C9, OBlue

' Let's draw the second line to ensure that the
' change took effect

LINE (0, 199)-(319, 0), 1
END
    

Wrapping up the basics

Now you should have a bit more of an understanding of the VGA screen and mode 13H which is the easiest (though not always the best) mode to program in. You should also be able to write code that can access this screen mode and access or even change the color registers no matter what version of Basic you are using. So long as we are doing this on a PC under DOS.

Now I did not cover the actual layout of this screen mode itself, or the very basic's such as how to plot a single pixel or draw a line. I was assuming that you at least had this much knowledge, if not you may want to consort you QBasic online reference (or GW-Basic manual) for more info. I will admit that they may not have a lot to offer in this general category.


Send your questions, comments, or ideas here.

This page hosted by GeoCities Get your own Free Home Page
Next Back |

1