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)
DEF SEG
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
DEF SEG
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.
Conclusion
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. |
|