59 Please explain
Turbo Pascal memory addressing to me.
A: This is far from an
easy question, but let's see what we can do.
The origins of the difficulties are in the design of the 8086 chip
which still restricts all Turbo Pascal applications (which contrary
to Borland Pascal use the original real mode). The 8086 (aka real
mode) addressing is based on 16-bit registers. As you probably
know
2^16 is 65536 which means that you cannot directly point to all
addresses of the lower and upper memory, which ranges from 0 to
1048575 (2^20-1). Thus all the memory addresses are pointed to
into
two parts in TP programs, the segment and the offset. The
following
example of the PC's memory illustrates.
Decimal Hexa-
address decimal Segment Offset What
0 $00000 $0000 $0000 Conventional memory starts
1043 $00413 $0040 $0013 Base memory in Kb, a word
655359 $9FFFF $9000 $FFFF Conventional memory ends
655360 $A0000 $A000 $0000 Upper memory begins
1048575 $FFFFF $F000 $FFFF Upper memory ends
To exemplify, let's look
at some alternative ways we could access
the information about the amount of the base memory. It is very
straight-forward, since in a PC that information is at the fixed
memory location show by the above table. We know this in
advance.
Using direct memory accessing we could write
var memsize : word;
memsize := MemW [$0040:$0013];
writeln (memsize);
{.. or ..}
var memsize : word absolute $0040:$0013;
writeln (memsize);
If you are not familiar with the true meaning of pointers, they may
feel confusing, but what they basically are is just what the name
indicates, pointers to memory locations. Study the following
example.
var memsizePtr : ^word; { A pointer to a word }
begin
memsizePtr := ptr ($40, $13); { Assign the pointer a value }
writeln (memsizePtr^); { Write what is in the address }
end. { that was pointed to }
This was relatively simple, since we knew in advance the location
of
the information. Lets look at a case where we do not know that.
Consider
var x : word;
begin
x := 1223;
writeln (x);
end.
We have a variable x somewhere in the memory, and we can refer
to it
without ever needing to know where the variable actually is in the
memory. But how does one find out if one for some reason wants
or
needs to know?
var x : word;
Segment : word;
Offset : word;
y : ^word;
begin
x := 1223;
writeln (x);
Segment := Seg(x);
Offset := Ofs(x);
writeln ('Variable x is at $', HEXFN(Segment), ':$', HEXFN
(Offset));
{... one test to ensure that the value really is in there ...}
writeln (MemW [Segment:Offset]);
{... another test to demonstrate that the value really is in there ...}
y := Addr(x);
writeln (y^);
end.
Next consider
var xPtr : ^word;
Segment : word;
Offset : word;
yPtr : ^word;
begin
xPtr^ := 1223;
writeln (xPtr^);
Segment := Seg(xPtr^);
Offset := Ofs(xPtr^);
writeln ('$', HEXFN(Segment), ':$', HEXFN(Offset));
{... a test to ensure that the value really is in there ...}
yPtr := Ptr (Segment, Offset);
writeln (yPtr^);
end.
A further aspect of pointers is that you can utilize them to put a
variables onto the heap instead of the data segment so that you
won't run so easily out of space.
var xPtr : ^word;
begin
{ Put it onto the heap }
New (xPtr);
xPtr^ := 1223;
writeln (xPtr^);
{ Get rid of it }
Dispose (xPtr); xPtr := nil;
readln;
end.
Let us return to the addressing. The formulas for converting
between
the addresses (sent in by Duncan Murdoch) are
Physical := longint(segment)*16 + offset;
{}
Segment := Physical div 16;
Offset := Physical mod 16; { This gives the normalized form }
There are multiple Segment:Offset pairs that refer to the same
address, e.g. $0000:$0413 and $0040:$0013. The normalized
addresses,
with the offset in the range of 0 to $F, are, however, unique. An
example $0041:$0003.