Chapter 13 -- Memory Access





Introduction

Hi! Nice to meet you again! This time I'd like to explain about the memory accesses in Pascal. Pascal lets you deal with memory directly. However, this lesson is NOT about Direct Memory Access (DMA). It is totally different. What I want to tell you is only about interfering memory directly. That's all.

What to Learn

Of course we learn how to modify memory directly. :-) This is very useful to detect the value of some memory location, usually located at BIOS data area. There are so many useful things to get, such as the standard peripheral detection, communication ports, memory amount, and most of "undetectable" keyboard checking (ctrl, alt, shift, etc).

Closely related to this topic is modifying the text mode screen directly, without write or writeln. Then, as an extra, I'd like to tell you how to access 256 color graphics. It is the famous mode 13h with the resolution 320x200. Nice and easy, but I don't discuss about how to develop graphic libraries here though. May be later.

Absolute Address

Before you can modify any memory, you must learn first about absolute address. It is not too hard defining absolute address. The first thing you need to know what type the memory you want to modify is. Is it byte, word, or double word (longint). Suppose the memory is a byte located at $0000:$0417, here's how to declare it:

var
  myloc : byte absolute $0000:$0417;

Then, you could access that location just treating it as a normal byte variable, myloc. By the way, location $0000:$0417 and $0000:$0418 is the memory location to detect the ctrl, alt, shift, and so keys.

You could also define array of bytes at an absolute location. Or words or longints, as you like. This sure makes easy for us to set different values to a group of adjacent memory locations. Here's how to declare it:

var
  scrbuf : array[1..4000] of byte absolute $B800:$0000;

Easy right? You can access the location just treating it as a normal array.

Direct Screen Modification

Some of the programmers prefer to modify the screen directly without the help of write or writeln. Since the video memory is located at $B800:$0000, you can modify it using absolute location. However, it must be declared in an array. The screen is divided into 25 rows and 80 columns, so total characters that can be held on the screen simultaneously is 25 x 80 = 2000. Each character can be contained in just one byte. Then, the screen also hold the value of the attribute (i.e. colors) accompanying each character. That attribute worth one byte too. So, actually the video memory eats up 2 x 2000 = 4000 bytes. That's why we need to declare the video memory as 4000-bytes array. You can see declaration above, the scrbuf.

The lay out of the screen buffer is like this. The first byte entry is the character on the top left screen (location 1,1). The second byte is the color accompanying it. The third byte is the character to the right of the first character (i.e. location 2,1). The fourth byte is the color accompanying the second character and so on. The first line is done first, then the second line and so on until the twenty-fifth line.

So, to modify the character on location 13,11, we would like to modify the entry number 10 x 2 x 80 + 2 x 12 = 1624. Easy right? Why we need to multiply it by 2? It is obviously because one location contains two bytes, one for the character itself then the color (attribute). Why should we need to subtract the location number? Well, it is actually the location of top-left corner of the screen is 0,0. Because we need to specify the top-left corner as the location 1,1 -- we need to subtract both coordinate number by 1.

This is one example of writechar procedure. What it does is write a character on the screen in a specified location (x,y).

procedure writechar(x,y : byte; c : char);
begin
  scrbuf[ (y-1)*2*80 + (x-1)*2 ] := ord(c);
end;

Easy, right?

Another Way to Access Memory

Beside absolute notation, Pascal has another way to modify memory locations. It is using mem, memw, and memL. Mem is a special array of bytes, memw is array of words, and memL is array of longints. We must specify the memory location inside the clause. Suppose we want to get to know the contents of location $0000:$0417 in byte, we would do: x := mem[$0000:$0417];

Nice and easy, right? If you want to access words, use memw, and if you want to access longints, just use memL. So, the writechar procedure can be rewritten using mem. Just erase the line scrbuf ... and replace it with this: mem[$b800: (y-1)*2*80 + (x-1)*2] := ord(c);

And it does the same thing. Try something else!

Ptr, Addr, Seg, and Ofs

These instructions are provided by Pascal to form pointers and to get to know the value of the segment and the offset. These are often used in memory locations. Ptr is a function to form a pointer. It takes two parameters the first is the segment value, the next is the offset value. So if we want to form pointer p as a pointer to location $0000:$0417, we would do: p:=ptr($0000,$0417);

Then seg is a function that returns the segment part. You can pass any variable to it and get the segment value of that variable address. You can pass a function or a procedure name too. The ofs function is do the same as seg, but it returns the offset part instead.

Addr is a function that returns the address (i.e. the pointer) of anything passed into it. You can pass variables or the name of a function or a procedures. Just anything addressable. It is the same as @ operator in registerbgidriver. Remember?

256 Colors Graphics Basics

It seems that you've been waiting this part. You got your prize. :-) First of all, you need to set up the graphic screen like this: Use interrupt 10h (video interrupt) service 0 with AL = 13h. Set up the reg variable as registers.

reg.ax:=$0013;
intr($10,reg);

That was it! You've arrived in 256 colors world. Congratulations. To get back to the text mode, use the video interrupt service 0 again, but with AL = 3h. Like this:

reg.ax:=$0003;
intr($10,reg);

Easy, right? Why did I put this part here? It's because it has something to do with memory access. This VGA 13h mode needs direct access to video screen to draw things. The video buffer for this mode is not the same as in that the text mode. It uses the address $A000:$0000.

The lay out of mode 13h video memory is like this. Every thing goes linearly. It has the resolution of 320x200. Each pixel is represented by bytes. Therefore, it contains 256 colors. Similar to the text mode, the top-left pixel is at the first byte. But, there are no attribute here. The second pixel to the right is at the second byte. The coordinate of graphic screen is usually began at (0,0) not (1,1) like text mode. Then, the lower right corner coordinate is (319,199). Therefore, you don't need to subtract the coordinates passed to your procedure in order to map them into video memory locations.

The access in 256 color is much faster than that in BGI graphics. So, it is much prefered for game programmers. You can say many titles of games made between 1990 to 1994 built in this mode. The only drawback is that Borland doesn't provide a graphic library to accomodate this need. So, you need to build one. Things in the libraries that you often need is: putpixel, getpixel, putimage, getimage, line, rectangle, fillpoly and drawpoly. Advanced programmers may need double buffering, palette settings, and other animation stuffs. You can build it your own version or just build it like BGI libraries. It's up to you.

You don't need to build too complex drawing procedures. You can draw a lot nicer in drawing applications, such as Paintbrush or so. You just build a procedure to load its graphic file format, such as BMP or PCX. I won't discuss that here. I just want to let you know only how to draw pixels and how to get pixels in the screen.

procedure putpixel(x, y : word; c : byte);
begin
  mem[$A000: y*320+x]:=c;
end;

function getpixel(x, y : word) : byte;
begin
  getpixel:=mem[$A000:y*320+x];
end;

Nice and easy. But that's pretty slow. It is all because of the multiplication. Just change the y*320 into (y shl 8)+(y shl 6). It would do a lot faster. Why? It is because the same instruction is replaced with shifting bits. It yields the same results. Remember the fact of y*320 = y * (256+64) = y * 256 + y * 64 ? Multiplying numbers with 256 is the same as shifting the numbers to the left eight times. (because 28 = 256). Multiplying numbers with 64 is the same as shifting the numbers to the left six times (because 26 = 64).

Now try this program to see the splendor of 256 colors:


uses crt, dos;

procedure init;
var  reg : registers;
begin
  reg.AX:=$0013;
  intr($10,reg);
end;

procedure destroy;
var  reg : registers;
begin
  reg.AX:=$0003;
  intr($10,reg);
end;

procedure putpixel(x,y : word; c : byte);
begin
  mem[$A000: (y shl 8)+(y shl 6)+x]:=c;
end;

function getpixel(x, y : word): byte;
begin
  getpixel := mem[$A000: (y shl 8)+(y shl 6)+x];
end;

{ Slow horizontal line routine }
procedure horizline(y : word; c : byte);
var  i : word;
begin
  for i:= 0 to 319 do putpixel(i,y,c);
end;

procedure show;
var  i : word;
begin
  for i:= 0 to 199 do horizline(i, i);
  readkey;
  randomize;
  repeat
    putpixel(random(320),random(199),random(255));
  until keypressed;
end;

begin
  clrscr;
  init;
  show;
  destroy;
end.


Notes

That's all folks! Seems that 256 color graphic is attractive. :-) Now, you could bring your PacMan into 256 color. Shall we go to the next lesson? Or you still don't understand? Mail me!


Where to go?

Back to main page
Back to Pascal Tutorial Lesson 2 contents
Quiz? No quiz. I think you are pretty mature in programming! :-)
To Chapter 14 about combining Pascal with assembly.
My page of programming link
Contact me here


By: Roby Joehanes, © 1997, 2000