Académique Documents
Professionnel Documents
Culture Documents
that of Proc.V. }
Valu := 4;
END;
Procedure Proc;
VAR V : BYTE;
BEGIN
Proc1(3); CC Either a constant or a variable's value can be used in a call
Proc1(V); CO to Proc1(). Note that, here, V has not been initialized.
Proc2(V); CCC A variable address must be supplied to Proc2.
After Proc2 is called V will equal 4.
END;
In order to understand the difference between passing a value, as in the call
to Proc1, and passing a pointer, as in the call to Proc2, we will have to look
more closely at how data types are organized in memory.
Let's look at a sequence of bytes in memory having the hexadecimal values
76 54 32 10
in order to see how the concept of data typing relates to the spatial
organization of these values in memory. First, let's decide that the address of
the first byte (the 76h) is in segment 0C32h somewhere; say, at offset 2104h.
This memory address is then the sum of C320h (remember the segment is shifted
left four bit positions) and 2104h, which is E424h.
Let's show a pointer which we will call varPtr, which we will decide is
located at another address... say, at offset 1FE0h in the same segment. The
address of varPtr can then be written in segment:offset form, as is commonly
done, as 0C32:1FE0.
0C32:1FE0
R
varPtr
This value, 0C32:1FE0, is a pointer, but we haven't given it a name (although
I suppose we could). However, varPtr is a pointer because we decided that's
what it would be, and in a PASCAL program we would declare our decision by the
statement
VAR varPtr : POINTER;
although, if we did that, we wouldn't have any choice about where varPtr would
be located; chances are it wouldn't be at 0C32:1FE0, but it would probably be
somewhere in the area. Locations of variables in memory cannot always be known
beforehand; in fact, the only way to be certain that a variable is at a
predefined location is to use the ABSOLUTE clause. We might have forced varPtr
to be at 0C32:1FE0 by declaring it as
VAR varPtr : POINTER ABSOLUTE $0C32:$1FE0;
Now let's decide where we will point varPtr; i.e., what address we will store
in memory at 0C32:1FE0. Why not make it point at itself? (The '@' symbol is
the address operator in Turbo PASCAL.)
varPtr := @varPtr;
R
R
varPtr
We say that varPtr is 'referencing' itself.
What? Well, we wrote the segment and offset as decimal numbers, which is
really the wrong way to go about it because, for all we know from just looking
at those two sets of digits, they could be hexadecimal numbers!
We need a subroutine to write them out as hexadecimal numbers, because there
isn't a built-in PASCAL routine which does this. (You'll see one in the
examples that follow later on.) For now it's enough to know that if we aren't
careful we could confuse ourselves by mixing decimal numbers in where they
really aren't either fitting or of much practical importance.
At any rate, our subroutines could show us the following representations for
the value of varPtr:
LONGINT
58112d
E300h
POINTER
--------0C32:1FE0
Seg(varPtr)
3122d
0C32h
Ofs(varPtr)
8160d
1FE0h
0C32:1FE0
varPtr
76 54 32 10
0C32:2104 R R R
0C32:2105 R R
0C32:2106 R
0C32:2107
Now, varPtr^ equals 76. Such a generic pointer points to a single byte of
memory, but we can define other types of pointers which point to specific data
types of various sizes:
TYPE
CharPtr
BytePtr
WordPtr
LongPtr
PtrPtr
=
=
=
=
=
^CHAR;
^BYTE;
^WORD;
^LONGINT;
^POINTER;
Then, we can use typecasting to impose these three types of order on the
sequence of bytes referenced by varPtr:
CharPtr(varPtr)^
BytePtr(varPtr)^
WordPtr(varPtr)^
LongPtr(varPtr)^
PtrPtr(varPtr)^
{
{
{
{
{
This
This
This
This
This
equals
equals
equals
equals
equals
'v'
76h
5476h
10325476h
1032:5476
}
}
}
}
}
Note that 'v' is ASCII character 118, and 118d equals 76h. If, instead of
referencing varPtr as above we use the @ address operator to reference the
address of varPtr, this typecasting produces different results:
CharPtr(@varPtr)^
BytePtr(@varPtr)^
WordPtr(@varPtr)^
LongPtr(@varPtr)^
PtrPtr(@varPtr)^
{
{
{
{
{
This
This
This
This
This
equals
equals
equals
equals
equals
'2'
32h
0C32h
1FE00C32h
1FE0:0C32
}
}
}
}
}
v : LONGINT;
BEGIN
v := $01020304;
BufPtr := @v;
LBufPtr^[1] := v;
END.
{ Assign 16909060 to v }
{ Point BufPtr at v }
{ Assign v to LBufPtr^[1] }
With these type definitions and assignments in mind, the following typecasts
illustrate how to address the memory area occupied by v.
LSB
MSB
CCCCCCCCCCCCCCCC]
^ 04 ^ 03 ^ 02 ^ 01 ^
v = $01020304
_CCCC`CCCC`CCCC`CCCCO
_d_d_d_d
^
^
^
_CCCCCCCCCCCCCCCCCCCCCCO ^ ^ ^
^
^
_CCCCCCCCCCCCCCCCCCCCCCCCCCCCCO ^ ^
^
_CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCO ^
_CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCO
BYTE(v) = BytePtr(BufPtr)^ = $04
CCCC] CC
CC
CC
^ 04 ^
^
^
^
_CCCCO CC
CC
CC
WORD(v) = WordPtr(BufPtr)^ = $0304
CCCCCCCC] CC
CC
^ 04 ^ 03 ^
^
^
_CCCC`CCCCO CC
CC
BufPtr^[1] = LONGINT(BufPtr^[1]) = $04
CCCC] CC
CC
CC
^ 04 ^
^
^
^
_CCCCO CC
CC
CC
BufPtr^[4] = BytePtr(@BufPtr^[4])^ = $01
CC
CC
CC CCCC]
^
^
^
^ 01 ^
CC
CC
CC _CCCCO
WordPtr(BufPtr^[1]) = Ptr($0102,$0304)
CCCCCCCCCCCCCCCC]
^ 04 ^ 03 ^ 02 ^ 01 ^
CCCC`CCCCCCCC`CCCCd
^
OFS
^
SEG
^
_CCCCCCCCC`CCCCCCCCCO
WordPtr(@BufPtr^[3])^ = $0102
CC
CC CCCCCCCC]
^
^
^ 02 ^ 01 ^
CC
CC _CCCC`CCCCO
LongPtr(BufPtr)^ = $01020304 = LongPtr(LBufPtr^[1])^
CCCCCCCCCCCCCCCC]
^ 04 ^ 03 ^ 02 ^ 01 ^
_CCCC`CCCC`CCCC`CCCCO
The following TYPE definitions and diagrams illustrate multiple levels of
indirection in pointer references, with the ultimate object to which the
pointer chain finally leads being of type INTEGER or an ARRAY of INTEGER.
IntPtr = ^INTEGER;
INTEGER
CCCCCCCCCCCCCC]
CCCCCCCC]
^ OFS ^ SEG ^ 76 ^ 54 ^
_CCCCCCC`CCCCCCCO
_CCCC`CCCCO
IntPtrPtr = ^IntPtr;
CCCCCCCCCCCCCC]
^ OFS ^ SEG
_CCCCCCC`CCCCCCCO
IntPtr = ^INTEGER;
INTEGER
CCCCCCCCCCCCCC]
CCCCCCCC]
^ OFS ^ SEG ^ 43 ^ 21 ^
_CCCCCCC`CCCCCCCO
_CCCC`CCCCO
IntPtrPtrArray
= ARRAY [1..3]
OF IntPtrPtr;
CCCCCCCCCCCCCC]
1 ^ OFS ^ SEG
CCCCCCCCCCCCCCd
2 ^ OFS ^ SEG
CCCCCCCCCCCCCCd
3 ^ OFS ^ SEG
_CCCCCCC`CCCCCCCO
IntPtr = ^INTEGER;
CCCCCCCCCCCCCC]
^ OFS ^ SEG
_CCCCCCC`CCCCCCCO
CCCCCCCCCCCCCC]
^ OFS ^ SEG
_CCCCCCC`CCCCCCCO
CCCCCCCCCCCCCC]
^ OFS ^ SEG
_CCCCCCC`CCCCCCCO
INTEGER
CCCCCCCC]
^ 98 ^ 76 ^
_CCCC`CCCCO
CCCCCCCC]
^ 32 ^ 10 ^
_CCCC`CCCCO
CCCCCCCC]
^ 76 ^ 54 ^
_CCCC`CCCCO
IntPtrPtrPtr
IntPtrPtr
IntPtr
= ^IntPtrPtr;
= ^IntPtr;
= ^INTEGER;
INTEGER
CCCCCCCCCCCCCC]
CCCCCCCCCCCCCC]
CCCCCCCCCCCCCC]
CCCCCCCC]
^ OFS ^ SEG ^ OFS ^ SEG ^ OFS ^ SEG ^ 76 ^ 54 ^
_CCCCCCC`CCCCCCCO
_CCCCCCC`CCCCCCCO
_CCCCCCC`CCCCCCCO
_CCCC`CCCCO
IntArrPtr
= ^IntArray;
CCCCCCCCCCCCCC]
^ OFS ^ SEG
_CCCCCCC`CCCCCCCO
IntArrayArrayPtr
= ^IntArrayArray;
CCCCCCCCCCCCCC]
^ OFS ^ SEG
_CCCCCCC`CCCCCCCO
IntArray =
ARRAY [1..3] OF INTEGER
CCCCCCCCCCCCCCCCCC]
^ BA98 ^ 7654 ^ 3210 ^
_CCCCCC`CCCCCC`CCCCCCO
1
2
3
IntPtrArrayPtr
= ^IntPtrArray;
CCCCCCCCCCCCCC]
^ OFS ^ SEG
_CCCCCCC`CCCCCCCO
0
1
2
IntArrayArray
= ARRAY [1..3] OF IntArray;
CCCCCCCCCCCCCCCCCC]
^ BA98 ^ 7654 ^ 3210 ^
CCCCCCCCCCCCCCCCCCd
^ FEDC ^ A987 ^ 5432 ^
CCCCCCCCCCCCCCCCCCd
^ CBA9 ^ 8765 ^ 4321 ^
_CCCCCC`CCCCCC`CCCCCCO
IntPtrArray
= ARRAY [0..2]
OF IntPtr;
CCCCCCCCCC]
^ SEG ^ OFS
CCCCCCCCCCd
^ SEG ^ OFS
CCCCCCCCCCd
^ SEG ^ OFS
_CCCCC`CCCCCO
INTEGER
CCCCCCCC]
^ 98 ^ 76 ^
_CCCC`CCCCO
CCCCCCCC]
^ 32 ^ 10 ^
_CCCC`CCCCO
CCCCCCCC]
^ 76 ^ 54 ^
_CCCC`CCCCO
Now that we have looked at how the basic one, two, and four-byte PASCAL types
are organized in memory, we will examine the intricacies of addressing and
procedure calls in detail.
The following PASCAL program defines pointer types, declares variables,
initializes the variables, and then calls each one of the example external
assembly language procedures; when run under control of the debugger, it will
show clearly how parameters are passed and how stack operations are performed
for NEAR and FAR calls and how the various parameters are passed. Note that
the {$F+} compiler directive is used to force a far call to those subroutines
which have been declared as FAR in the assembly-language module.
The assembly language module, which is assumed to be assembled by TASM.EXE to
ADDRS.OBJ, follows the PASCAL program. The procedures and functions declared
EXTERNAL in the PASCAL module are contained in the assembly-language module.
EXAMPLE
1.A
ADDRS.PAS
Program Addrs;
CONST
Filler : STRING[12] = 'ABCD'#4#3#2#1'abcd';
TYPE
BytePtr = ^BYTE;
WordPtr = ^WORD;
LongPtr = ^LONGINT;
StrPtr = ^STRING;
Buffer = ARRAY [1..4] OF BYTE;
BufferPtr = ^Buffer;
VAR
B : BYTE;
W : WORD;
L : LONGINT;
S : STRING;
P : POINTER;
PreBuf,Buf,PostBuf : Buffer;
pB : BytePtr;
pW : WordPtr;
pL : LongPtr;
pS : StrPtr;
BufP : BufferPtr;
{
Init loads values into B, W, L, S, and the 12-byte area occupied by the
PreBuf, Buf, and PostBuf variables. It also assigns addresses to the
typed pointers pB, pW, pL, pS, and BufP, so that they reference B, W,
L, S, and Buf, respectively.
}
Procedure Init;
BEGIN
B := $11; W := $22; L := $33; S := 'STRING'; P := @S;
pB := @B; pW := @W; pL := @L; pS := @S; BufP := @Buf;
Move(Filler[1],PreBuf,12);
END;
BEGIN
{ This is the main program body, which calls the various procedures. }
Init;
NearByValue(B,W,L,S);
NearByRef(B,W,L,S);
NearPtrs(pB,pW,pL,pS);
FarByValue(B,W,L,S);
FarByRef(B,W,L,S);
FarPtrs(pB,pW,pL,pS);
END.
ADDRS.ASM
LOCALS @@
COMMENT !
Note that each procedure here begins with a set of instructions which save the
BP and DS registers on the stack and places the stack pointer in BP so that
SS:BP can be used within the procedure for addressing the parameters pushed onto
the stack by the calling program. Each also ends with a set of three
instructions which restore DS, SP, and BP.
Each procedure heading is followed by a parameter list giving labels for the
locations on the stack where values or addresses are located, beginning with
the first word of the stack above the return address. Since PASCAL pushes the
variables or addresses onto the stack in the order in which they appear in the
PASCAL procedure declaration, the last item to be pushed onto the stack will be
at the stack location just above the return address because of the fact that the
stack is extended downward in memory as values are PUSHed onto it.
The equals sign following the argument list causes the label following it to be
equated to the size of the stack area occupied by the procedural parameters as
calculated by the assembler. This label is then placed after the RET
instruction so that this number of bytes will be popped from the stack when
;
;
;
;
;
;
;
;
;
;
;
COMMENT !
Procedure NearByValue(B : BYTE; W : WORD; L : LONGINT; S : STRING);
-- This PASCAL procedure heading causes the stack to be prepared by pushing
the following information onto the stack. Each successive WORD pushed onto
the stack is at a memory address 2 bytes lower than the previous one.
B
W
L - High WORD
L - Low WORD
SEG(S)
OFS(S)
<<<
...first to be pushed
...high word is always pushed by PASCAL before low word
...segment is always pushed by PASCAL before offset
The Stack Pointer (SP) points to this WORD before the CALL
The CALL instruction causes the offset part of the return address to be
pushed onto the stack, so that when the PUSH BP at the beginning of the
assembly-language subroutine is ready to be executed, one more WORD will
have been pushed onto the stack. Thus, OFS(S) will no longer be at [SP]
but will be at [SP+2] because the value of IP will have been pushed onto
the stack at [SP], as shown here, for a NEAR call:
B
W
L - High WORD
L - Low WORD
SEG(S)
OFS(S)
After the call, the parameters begin at SP+2
IP
<<< After the CALL, SP points to the return address
Note that even though S is not declared as a VAR parameter in the PASCAL
procedure heading, its actual address is pushed onto the stack, and S could
thus be modified by the subroutine as though it were in fact a VAR parameter.
Our example does not change S, nor does it do anything significant with it;
it merely places each byte of S in turn in the low half of AX, just to show
the basic idea behind the use of a string instruction using the REP repeat
prefix.
!
; Stack space required:
4 bytes
4 bytes
2 bytes
2 bytes
;
^
^
^
^
NearByValue PROC NEAR @@S:DWORD,@@L:DWORD,@@W:WORD,@@B:BYTE:2 = NBVsiz
;
^
^
^
^
; Location of argument: _ BP+2
_ BP+6
_ BP+10 _ BP+12
; At this point, SP points to the value of IP on the stack.
PUSH BP
MOV BP,SP
PUSH DS
; Save BP register
; Move SP into BP for stack addressing
; Save PASCAL's data segment location
MOV
MOV
MOV
MOV
LDS
;
;
;
;
;
;
AL,[@@B]
AX,[@@W]
AX,WORD PTR [@@L]
DX,WORD PTR [@@L][2]
SI,[@@S]
CLD
LODSB
XOR CH,CH
MOV CL,AL
REP LODSB
POP
MOV
POP
RET
DS
SP,BP
BP
NBVsiz
;
;
;
;
;
AL
AX
AX
DX
DS
SI
<<<<<<-
B located at SS:BP+12
W located at SS:BP+10
Low WORD of L located at SS:BP+6
High WORD of L located at SS:BP+8
SEG(S) located at SS:BP+4
OFS(S) located at SS:BP+2
NearByValue ENDP
COMMENT !
Procedure NearByRef(VAR B : BYTE; VAR W : WORD;
VAR L : LONGINT; VAR S : STRING);
This PASCAL procedure heading causes the following information to be pushed
onto the stack:
SEG(B)
OFS(B)
SEG(W)
OFS(W)
SEG(L)
OFS(L)
SEG(S)
OFS(S)
The return address is pushed onto the stack by the CALL instruction, just as in
the previous example.
!
;
;
;
;
;
;
;
;
;
;
;
;
DS
BX
AL
DS
BX
AX
DS
BX
AX
DX
DS
SI
<<<<<<<<<<<<-
DS
SP,BP
BP
NBRsiz
NearByRef ENDP
COMMENT !
Procedure NearPtrs(pB : BytePtr; pW : WordPtr; pL : LongPtr; pS : StrPtr);
This PASCAL procedure heading causes the following information to be pushed
onto the stack:
High word of pB
Low word of pB
High word of pW
Low word of pW
High word of pL
Low word of pL
High word of pS
Low word of pS
=
=
=
=
=
=
=
=
SEG(pB^)
OFS(pB^)
SEG(pW^)
OFS(pW^)
SEG(pL^)
OFS(pL^)
SEG(pS^)
OFS(pS^)
The return address is pushed onto the stack by the CALL instruction, just as in
the previous examples.
!
NearPtrs PROC NEAR @@pS:DWORD,@@pL:DWORD,@@pW:DWORD,@@pB:DWORD = NPsiz
; At this point, SP points to the value of IP on the stack.
PUSH BP
MOV BP,SP
PUSH DS
LDS BX,[@@pB]
MOV AL,BYTE PTR [BX]
LDS BX,[@@pW]
MOV AX,[BX]
LDS BX,[@@pL]
MOV AX,[BX]
MOV DX,[BX+02]
LDS SI,[@@pS]
POP
MOV
POP
RET
;
;
;
;
AX
AX
DS
SI
<<<<-
DS
SP,BP
BP
NPsiz
NearPtrs ENDP
COMMENT !
{$F+}
Procedure FarByValue(B : BYTE; W : WORD; L : LONGINT; S : STRING);
{$F-}
This PASCAL procedure heading causes the following information to be pushed
onto the stack:
B
W
L - High WORD
L - Low WORD
SEG(S)
OFS(S)
CS (Code Segment) register
Note that the value of the Code Segment (CS) register is placed on the stack
before the CALL instruction is executed because the PASCAL procedure heading
declares this external procedure as FAR.
The return address is pushed onto the stack by the CALL instruction, just as in
the previous examples. Since the assembly language procedure is FAR, it must
be called from the PASCAL program with a FAR call using the {$F+} compiler
directive, which ensures that both CS and IP will be placed onto the stack.
B
W
L - High WORD
L - Low WORD
SEG(S)
OFS(S)
CS (Code Segment) register
IP (Instruction Pointer) register
When a far call is made, the first parameter is at SP+4 because the code segment
register (CS) is pushed onto the stack.
When declared as PROC FAR, the procedure
the RET instruction is executed, instead
is of the utmost importance to make sure
FAR, it is called using the correct call
!
; Stack space required: 4 bytes
4 bytes
2 bytes
2 bytes
;
^
^
^
^
FarByValue PROC FAR @@S:DWORD,@@L:DWORD,@@W:WORD,@@B:BYTE:2 = FBVsiz
;
^
^
^
^
; Argument location: _ BP+4
_ BP+8
_ BP+12 _ BP+14
; At this point, SP points to the value of IP on the stack.
PUSH BP
MOV BP,SP
PUSH DS
MOV
MOV
MOV
MOV
LDS
AL,[@@B]
AX,[@@W]
AX,WORD PTR [@@L]
DX,WORD PTR [@@L][2]
SI,[@@S]
POP
MOV
POP
RET
DS
SP,BP
BP
FBVsiz
;
;
;
;
;
;
AL
AX
AX
DX
DS
SI
<<<<<<-
B located at SS:BP+14
W located at SS:BP+12
Low WORD of L located at SS:BP+8
High WORD of L located at SS:BP+10
SEG(S) located at SS:BP+6
OFS(S) located at SS:BP+4
FarByValue ENDP
COMMENT !
{$F+}
Procedure FarByRef(VAR B : BYTE; VAR W : WORD;
VAR L : LONGINT; VAR S : STRING);
{$F-}
This PASCAL procedure heading causes the following information to be pushed
onto the stack:
SEG(B)
OFS(B)
SEG(W)
OFS(W)
SEG(L)
OFS(L)
SEG(S)
OFS(S)
CS
The return address is pushed onto the stack by the CALL instruction, just as in
the previous examples.
!
; Stack space needed: 4 bytes
4 bytes
4 bytes
4 bytes
;
^
^
^
^
FarByRef PROC FAR
@@S:DWORD,@@L:DWORD,@@W:DWORD,@@B:DWORD = FBRsiz
;
^
^
^
^
; Argument location: _ BP+4
_ BP+8
_ BP+12
_ BP+16
; At this point, SP points to the value of IP on the stack.
PUSH BP
MOV BP,SP
PUSH DS
LDS BX,[@@B]
MOV AL,BYTE PTR [BX]
LDS BX,[@@W]
MOV AX,[BX]
LDS BX,[@@L]
MOV AX,[BX]
MOV DX,[BX+02]
LDS SI,[@@S]
POP
MOV
POP
RET
;
;
;
;
;
;
;
;
;
;
;
;
DS
BX
AL
DS
BX
AX
DS
BX
AX
DX
DS
SI
<<<<<<<<<<<<-
DS
SP,BP
BP
FBRsiz
FarByRef ENDP
COMMENT !
{$F+}
Procedure FarPtrs(pB : BytePtr; pW : WordPtr; pL : LongPtr; pS : StrPtr);
{$F-}
This PASCAL procedure heading causes the following information to be pushed
onto the stack:
High word of pB =
Low word of pB =
High word of pW =
Low word of pW =
High word of pL =
Low word of pL =
High word of pS =
Low word of pS =
CS (Code Segment)
SEG(pB^)
OFS(pB^)
SEG(pW^)
OFS(pW^)
SEG(pL^)
OFS(pL^)
SEG(pS^)
OFS(pS^)
register
The return address is pushed onto the stack by the CALL instruction, just as in
the previous examples.
!
FarPtrs PROC FAR @@pS:DWORD,@@pL:DWORD,@@pW:DWORD,@@pB:DWORD = FPsiz
; At this point, SP points to the value of IP on the stack.
PUSH BP
MOV BP,SP
PUSH DS
LDS BX,[@@pB]
DS
SP,BP
BP
FPsiz
FarPtrs ENDP
CODE ENDS
END
In the examples above, the intent is not to show the usage of the data types,
either within the contex of PASCAL or ASM source code, but to show the basic
methods of accessing the simple variable types in memory through assembly
language instructions.
Demonstration of the use of these simple data types must be done within the
context of particular instruction sequences. Our first step will be to examine
in detail the way in which the processor responds to the pattern and sequence of
the bytes which represent each instruction in the instruction stream. We will
show how the BYTE, WORD, and DWORD data types are used as instruction operands
in response to individual bits in the machine instructions (which, in binary or
hexadecimal form are known collectively as 'object code'), and how data movement
between memory locations and microprocessor regsters is constrained by inbuilt
'addressing modes'. Throughout the following discussion, you should find each
item mentioned in the instruction chart, and study these carefully as you read.