Vous êtes sur la page 1sur 15



 DATA TYPES, POINTERS, AND MEMORY ADDRESSING 


Spatial Organization of LONGINT Data Type Pointer Types, Dereferencing,
and Typecasting of Simple Types PASCAL Type and Variable Declarations
PASCAL Near and FAR Procedures and Functions Passing Parameters by Value
and by Reference for Simple Types - Example 1.A (ADDRS.PAS/ADDRS.ASM)
8086 Instruction Formats OpCode Addressing Mode Byte
Width Bit <Reg> and <R/M> Fields Direction Bit
8086 Assembly Language Instruction Chart (1.1) Named Parameters and
Stack Offsets Nibbles in Hexadecimal and Binary Pencil and Paper
Errors Disassembly of Instructions and Demonstration of Pointer Addressing
from Example 1.A (Chart 1.2) Examples of Selected 8086 Instruction Formats
Sign-Extended Byte Displacements Binary Addition Two's Complement Form
Physical Address Calculation PC/XT Memory Map Simple Model of an Automaton
and Memory Segmentation Turbo PASCAL Memory Model Dynamic Variables
(Pointers) and System Crashes Stack Usage and Procedure Calls
Function Param() Turbo PASCAL System Pointers
Memory Usage - Example 1.B (MEMS.PAS) The 8086's Six Addressing Modes
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
This section shows how to address the basic PASCAL data types BYTE, WORD,
LONGINT, and STRING as well as 'typed pointers' which reference these types.
When a subroutine is called by a statement in a program, we speak of 'passing'
values of variables or their addresses 'from' the calling program 'to' the
subroutine. Both NEAR and FAR assembly-language procedures are exemplified,
and variables of these basic data types are passed both by value and by
reference (as VAR parameters).
A NEAR call to a procedure or function transfers program control only to
another location within the same code segment; only the IP register is changed,
and the CS register is unaffected. FAR calls, on the other hand, save both the
CS and IP registers, and are able to transfer control to a procedure or function
located in a different code segment. FAR calls are used in multiple-module
programs or large programs in which a single 64K segment is not large enough to
contain the entire program.
When a parameter of one of these types is passed by value, the actual value of
the variable is placed on the stack, with the exception of the STRING type. The
value can then be referenced directly using the SS:BP register pair.
When a parameter is a VAR parameter, a reference to it- its address -is
placed on the stack. This requires the assembly-language subroutine to load
this address pointer from the stack before it can reference the variable's
memory location.
A VAR parameter can return a value to the calling routine; a value parameter
cannot because it is simply a value.
Procedure Proc1(Valu : BYTE); { The value of Valu is on the stack }
BEGIN
{ Any reference to Proc1.Valu affects only a location on the stack.
Thus, Proc1.Value is temporary and local to Proc1. }
END;
Procedure Proc2(VAR Valu : BYTE); { The address of Valu is on the stack }
BEGIN
{ When Proc2 is called, below, any change in Proc2.Valu is actually a change
in the value of Proc.V, and the effective scope of Proc2.Valu is the same
as that of the address passed when Proc2 is called, which in this case is

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.

The 'value' of varPtr is now

$0C32:$1FE0, which is the SEGMENT:OFFSET form of the address or varPtr. Now,


we used the POINTER type to declare varPtr, so the size of the memory area
occupied by varPtr will be
SizeOf(POINTER)
which is four BYTEs, two WORDs, or one LONGINT. Since the size of a LONGINT and
a POINTER are the same, we can use a typecast to look at varPtr as if it were a
LONGINT type
Write('This pointer as a number is: ',LONGINT(varPtr));
and in that way could write the value of varPtr to the screen as 58112 decimal.
To write it as a segment:offset pair, we might use
Write('varPtr is located at ',Seg(varPtr),':',Ofs(varPtr));
and this would display:

varPtr is located at 3122:8160

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

When we 'de-reference' a pointer variable; that is to say, when we make use


of the address which it is storing, we do so by placing a caret symbol after the
variable name. We will use the HexFour subroutine that is shown later on, which
converts a two-byte value in memory to a four-digit hexadecimal number, so that
we don't add to our confusion:
Write('varPtr points to ',HexFour(Seg(varPtr^)),':',HexFour(Ofs(varPtr^)));
This would write the following:

varPtr points to 0C32:1FE0

Now let's make varPtr point to the sequence of bytes, 76 54 32 10 located at


0C32:2104.
varPtr := Ptr($0C32,$2104);

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

}
}
}
}
}

The following constant declaration sets up a typed constant somewhere in the


data segment and causes it to be initialized to the same four bytes we have
imagined as located starting at 0C32:2104.
CONST
CCCCCCCCCCCCCCCC]
LongVar : LONGINT
^ 76 ^ 54 ^ 32 ^ 10 ^
= $10325476;
_CCCC`CCCC`CCCC`CCCCO
^ ^ ^ ^
^
^
^
^
^ ^ ^ _CCCCCCCCCCCCCCCCCCCO
^
^
^
^ ^ _CCCCCCCCCCCCCCCCCCCCCCCCCCO
^
^
^ _CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCO
^
_CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCO
If we assigned varPtr the address of LongVar, the first group of typecasts
shown above would be the same, but the second group would reference the address
at which LongVar is located and would thus produce different results.
As another example of typecasting that illustrates how pointer dereferencing
and data types interact, consider the following:
TYPE
Buffer = ARRAY [1..4] OF BYTE;
BufferPtr = ^Buffer;
LBuffer = ARRAY [1..1] OF LONGINT;
LBufferPtr = ^LBuffer;
BytePtr = ^BYTE;
WordPtr = ^WORD;
LongPtr = ^LONGINT;
VAR
BufPtr : BufferPtr;
LBufPtr : LBufferPtr;

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;

Procedure NearByValue(B : BYTE; W : WORD; L : LONGINT; S : STRING);


EXTERNAL;
Procedure NearByRef(VAR B : BYTE; VAR W : WORD;
VAR L : LONGINT; VAR S : STRING);
EXTERNAL;
Procedure NearPtrs(pB : BytePtr; pW : WordPtr; pL : LongPtr; pS : StrPtr);
EXTERNAL;
{$F+}
Procedure FarByValue(B : BYTE; W : WORD; L : LONGINT; S : STRING);
EXTERNAL;
Procedure FarByRef(VAR B : BYTE; VAR W : WORD;
VAR L : LONGINT; VAR S : STRING);
EXTERNAL;
Procedure FarPtrs(pB : BytePtr; pW : WordPtr; pL : LongPtr; pS : StrPtr);
EXTERNAL;
{$F-}
{$L ADDRS.OBJ}

{ This compiler L directive tells the compiler to link


this module with the module in the ADDRS.OBJ file.
The linking process is what generates the values of the
segment registers required to transfer control and address
data properly in the various segments in use. }

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 @@

; This is so local variables can be prefixed with '@@'

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

the assembly language procedure terminates.


!
;
;
;
;
modules
;

CC This tells the assembler that this line is a segment declaration


^
CC Segment name is 'CODE'
^
^
CC Items in segment are to be aligned on byte boundaries
^
^
^
CC The segment address can be exported to other
^
^
^
^
SEGMENT CODE BYTE PUBLIC
ASSUME CS:CODE,DS:NOTHING,ES:NOTHING

;
;
;
;
;
;
;

The ASSUME directive is used to establish addressability for the


segment registers used in the segment; this particular statement
tells the assembler that CS must contain the segment address of
this segment at any time that a call is made to code within this
segment, and also states that DS and ES will have values which
cannot be known at the time the assembler processes this code
and that nothing is to be assumed about their values.
PUBLIC NearByValue,NearByRef,NearPtrs
PUBLIC FarByValue,FarByRef,FarPtrs

;
;
;
;

These PUBLIC directives tell the assembler that it will need to


calculate offset addresses relative to the start of the CODE
segment for each of the procedures named, because these addresses
will be exported to another module.

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

Automatically increment SI after each store operation


Store the length byte of S at S[0] into AL
Clear the high half of CX
Move the length of S into CL
Load each byte of S into AL in turn
; Restore PASCAL's data segment to DS
; Restore the stack pointer from BP
; Restore the previous value of BP

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)

<<< SP points to this WORD before the CALL

The return address is pushed onto the stack by the CALL instruction, just as in
the previous example.
!

; Stack space required: 4 bytes 4 bytes 4 bytes 4 bytes


;
^
^
^
^
NearByRef PROC NEAR @@S:DWORD,@@L:DWORD,@@W:DWORD,@@B:DWORD = NBRsiz
;
^
^
^
^
; Argument location: _ BP+2
_ BP+6
_ BP+10
_ BP+14
; 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

<<<<<<<<<<<<-

SEG(B) located at SS:BP+16


OFS(B) located at SS:BP+14
the value of B located at DS:[BX]
SEG(W) located at SS:BP+12
OFS(W) located at SS:BP+10
the value of W located at DS:[BX]
SEG(L) located at SS:BP+8
OFS(L) located at SS:BP+6
the low WORD of L located at DS:[BX]
the high WORD of L located at DS:[BX+2]
SEG(S) located at SS:BP+4
OFS(S) located at SS:BP+2

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^)

<<< SP points to this WORD before the CALL

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]

; DS <- SEG(pB^) located at SS:BP+16


; BX <- OFS(pB^) located at SS:BP+14
; AL <- the value of pB^ located at DS:[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 <- SEG(pW^) located at SS:BP+12


; BX <- OFS(pW^) located at SS:BP+10
; AX <- the value of pW^ located at DS:[BX]
; DS <- SEG(pL^) located at SS:BP+8
; BX <- OFS(pL^) located at SS:BP+6
the low WORD of pL^ located at DS:[BX]
the high WORD of pL^ located at DS:[BX+2]
SEG(S) located at SS:BP+4
OFS(S) located at SS:BP+2

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

<<< SP points to this WORD before the CALL

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

<<< After the CALL, SP points to this WORD

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

will pop 2 words from the stack when


of 1 word as in a NEAR procedure. It
that, whether a procedure is NEAR or
instruction.

!
; 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

<<< SP points to this WORD before the CALL

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

<<<<<<<<<<<<-

SEG(B) located at SS:BP+18


OFS(B) located at SS:BP+16
the value of B located at DS:[BX]
SEG(W) located at SS:BP+14
OFS(W) located at SS:BP+12
the value of W located at DS:[BX]
SEG(L) located at SS:BP+10
OFS(L) located at SS:BP+8
the low WORD of L located at DS:[BX]
the high WORD of L located at DS:[BX+2]
SEG(S) located at SS:BP+6
OFS(S) located at SS:BP+4

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

<<< SP points to this WORD before the CALL

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 <- SEG(pB^) located at SS:BP+18


; BX <- OFS(pB^) located at SS:BP+16
MOV AL,BYTE PTR [BX]
; AL <- the value of pB^ located at DS:[BX]
LDS BX,[@@pW]
; DS <- SEG(pW^) located at SS:BP+14
; BX <- OFS(pW^) located at SS:BP+12
MOV AX,[BX]
; AX <- the value of pW^ located at DS:[BX]
LDS BX,[@@pL]
; DS <- SEG(pL^) located at SS:BP+10
; BX <- OFS(pL^) located at SS:BP+8
MOV AX,[BX]
; AX <- the low WORD of pL^ located at DS:[BX]
MOV DX,[BX+02] ; AX <- the high WORD of pL^ located at DS:[BX+2]
LDS SI,[@@pS]
; DS <- SEG(S) located at SS:BP+6

; SI <- OFS(S) located at SS:BP+4


CLD
LODSB
XOR CH,CH
MOV CL,AL
REP LODSB
POP
MOV
POP
RET

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.

Vous aimerez peut-être aussi