Subject: Memory Addressing 59. ***** Q: 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. --------------------------------------------------------------------