issue #8

Graphical Memory

By WhiteShadow

Virtual Screens, memory addressing, and more!

What is memory?
Memory is where your computer stores information, like the variables in your programs. So when you type,

foo = 5

you're actually telling the computer to put 5 into the place in memory which has been 'given' ( or more technically, allocated ) to foo.


How does memory work?
Memory is very much like a street of houses - as each 'house' has it's own address, each place in memory has it's own address. These addresses have two parts - called segments and offsets ( for a more detailed explaination about segments and offsets, read Thav's article in issue 5). So an address in memory will have a segment and an offset.

How do I access them in QBASIC?
Here's how you access memory in QBASIC -

DEF SEG = Segment
POKE Offset, Value
Value = PEEK(Offset)

There, that simple! Now, let me explain in more detail. DEG SEG sets the segment to use when you use commands such as POKE and PEEK. POKE writes the value of Value to the Offset specified, and PEEK reads the value at Offset into Value. At the end we call DEF SEG again ( this time without an = after it ) to return to the default QBASIC segment ( because if we didn't then QBASIC might start writing stuff into the wrong places in memory and your computer might crash - now we wouldn't want that to happen would we? ).

101 ways to trash your PC
Wait! You can't just start writing stuff all over memory like that ( unless you really know what you're doing! )! You might start writing stuff over important things in memory. For instance, you might accidentally write over your character's x and y positions in your game and he might start jumping around the screen insanely! Or you could write over even more serious stuff. This is called trashing your PC! Now you know how to do it - DON'T DO IT!!! ( Oh yeah, like you won't! )

How to find a safe place to write to
When you create a variable, or DIMension an array, QBASIC creates a space in memory for it. This space in memory belongs to you, and you can do what you want with it! But how do you find it's address so that you can write to it? Use VARSEG() and VARPTR() of course!

' Get the segment of foo
Segment = VARSEG(foo)
' Get the offset of foo
Offset = VARPTR(foo)

Now you can change to the segment using DEF SEG, and read and write to it using PEEK and POKE! Finding the offset of the string however, is a little different. Instead of using VARPTR(), use SADD(). Note: When finding the segment and offset of an array, always tell it to find the address of element 0 eg. VARSEG(foo(0)), VARPTR(foo(0))

Saving your memory to a file -
QBASIC comes with two commands that enables you to save your memory to a file and load a file into memory! This is done by BSAVE and BLOAD. Here is how to use them -

DEF SEG = Segment
BSAVE FileName$, Offset, Length
BLOAD FileName$, Offset ' Offset is optional

Now why would you want to do this? Well for instance, you draw a sprite for your game and you GET it. However, drawing it and getting it every time you run the program makes for ugly and big code, so you save it to file and load it up at the start of the program instead! The important thing to remember is to create an array that is the size of the sprite to load it into ( remember what I said before - that you have to allocate the memory before you can use it ). Here's an example:

DIM Sprite%(51) ' The Array to store the sprite in
GET(0, 0)-(9, 9), Sprite% ' Get a 10 * 10 sprite
DEF SEG = VARSEG(Sprite%(0)) ' Set the segment to Sprite's segment
BSAVE FileName$, VARPTR(Sprite%(0)), 104
' Save the 104 bytes ( an integer in
' QBASIC takes up 2 bytes ) of memory
' allocated to Sprite into FileName$
DIM Sprite2%(100) ' The array to store the sprite in
BLOAD FileName$, VARPTR(Sprite2%(0))
' Load FileName$ into the place in
' memory allocated for Sprite2%

OK, you might have a few questions. First, you don't need to set the segment when you use BLOAD. Usually. Secondly, about the length of memory and the size of the array ( WARNING - this is complicated. Don't read unless you're ready for a challenge ). An integer in QBASIC takes up two bytes, OK? So, as our array is 51 integers long, it takes up 104 bytes in memory. In SCREEN 13 each pixel takes up one byte in memory, so we need 100 bytes ( 10 * 10 ) for that. But what's with the extra 4 bytes? Well that's the space that QB needs to store the sprite's width etc. ( You can visit QBASIC Help for info on getting your array exactly the right size for GET - or, if you use DirectQB, use DQBsize(x1, y1, x2, y2) and divide the result by 2! )

Writing directly to video memory
PSET and POINT are too slow. You don't want to use those evil libraries polluted with ( would you believe it? ) NON-QB CODE! How do you write to the screen quickly? Well, this is where all the junk I've bombarded you with in the previous chapters really makes sense in game programming. There is a specific place in memory reserved for the video screen. All that PSET actually does is write to the place in memory where the video screen is stored. So if you could write directly to that buffer, then it wouldn't it be much faster?

First thing to warn you about, is that you should only really do this in SCREEN 13 because in other modes it's much more complicated. This is one of the reasons why SCREEN 13 is so popular - because it's easy to use.

OK, you need the segment of where the video memory is. It's at &HA000, so,

DEF SEG = &HA000

Then you need the offset. Thankfully that's 0! Right then, now we just have to write to the memory. Some of you may have recognised a problem though. Memory goes in straight lines ( 1, 2, 3, 4 etc. ), but a SCREEN is two-dimensional! So, we have to work out how to convert 2D into 1D. This is actually easy. The SCREEN info is stored in bytes 0 - 319 for the first line, 320 - 639 for the second etc. The formula to work this out is ( prove this yourself if you want to ):

Offset = (Y * 320) + X

Now you have it! You can write quickly to video memory! Here's a few lines to help you out though...

DEF SEG = &HA000
Offset = (Y * 320) + X
Colour = PEEK(Offset) ' Like the POINT command
Offset = (Y * 320) + X
POKE Offset, Colour ' Like the PSET command

Saving Pictures
Using BSAVE and BLOAD, you can easily save and load pictures quickly. How? Well, tell BSAVE to save the contents of the video memory! This is great for storing pictures for your game. Here's some code...

DEF SEG = &HA000 BSAVE "picture1.bsv", 0, 64000 ... BLOAD "picture1.bsv"

There! You've saved a picture, and then, very quickly loaded it up!

Video screens
Here's the final thing that I'm going to teach you - how to make video screens in plain QBASIC, so you don't have to use libraries! Right, as you might already have guessed, you could create an array with 64000 elements and treat each one as a pixel. The only problem is that QBASIC won't let you do that, as it's too big! What can we do? Well, remember that an integer takes up 2 bytes, and 1 byte equals 1 pixel. So, if you create an array of 32000 integers, it's 64000 bytes in size so you can treat each byte as a pixel. However, to access each byte, you're going to need to use PEEK and POKE! Here's some code...

DIM VScreen(32000) ' Create the virtual screen
DEF SEG = VARSEG(VScreen(0)) ' Change to the segment of VScreen()
Colour = PEEK(VARPTR(VScreen(0) + (Y * 320) + X) ' Read from it
POKE(VARPTR(VScreen(0) + (Y * 320) + X), Colour ' Write to it

It's not wise to use too many virtual screens though as they will take up too much memory.

So, this is the end to your comprehensive guide to memory and QBASIC. Hopefully you will find BSAVEing and BLOADing sprites and pictures useful, as well as being able to write directly to video memory and even make your own virtual screens. However, I have gone over everything as quickly as possible to make this article short. If you need help on a particular topic, look at QB Help. You can also contact me on ICQ - my UIN is 25105257! I'm looking forward to hearing from you!

Feel free to email WhiteShadow to find out how to get color in SCREEN 0 at this address.


Back to Top