ParameterImplementation.pdf

(407 KB) Pobierz
AoA.book
Advanced Parameter Implementation
Ad
vanced Parameter Implementation
Chapter Four
4.1
Chapter Overview
This chapter discusses adv
anced parameter passing techniques in assembly language. Both lo
w-le
v
el
and high-le
v
el syntax appears in this chapter
.
This chapter discusses the more adv
anced pass by v
alue/result,
pass by result, pass by name, and pass by lazy e
v
aluation parameter passing mechanisms.
This chapter also
discusses ho
w to pass parameters in a lo
w-le
v
el manner and describes where you can pass such parameters.
4.2
Parameters
Although there is a lar
ge class of procedures that are totally self-contained, most procedures require
some input data and return some data to the caller
. P
arameters are v
alues that you pass to and from a proce
-
dure.
There are man
y f
acets to parameters. Questions concerning parameters include:
where is the data coming from?
• ow do you pass and return data?
• what is the amount of data to pass?
Previous chapters have touched on some of these concepts (see the chapters on beginning and interme-
diate procedures as well as the chapter on Mixed Language Programming). This chapter will consider
parameters in greater detail and describe their low-level implementation.
4.3
Where You Can Pass Parameters
Up to this point we’
v
e mainly used the 80x86 hardw
are stack to pass parameters. In a fe
w e
xamples
we’
v
e used machine re
gisters to pass parameters to a procedure. In this section we e
xplore se
v
eral dif
ferent
places where we can pass parameters. Common places are
in registers,
• in FPU or MMX registers,
• in global memory locations,
• on the stack,
• in the code stream, or
• in a parameter block referenced via a pointer.
Finally, the amount of data has a direct bearing on where and how to pass it. For example, it’s generally a
bad idea to pass large arrays or other large data structures by value because the procedure has to copy that
data onto the stack when calling the procedure (when passing parameters on the stack). This can be rather
slow. Worse, you cannot pass large parameters in certain locations; for example, it is not possible to pass a
16-element int32 array in a register.
Some might argue that the only locations you need for parameters are the register and the stack. Since
these are the locations that high level languages use, surely they should be sufficient for assembly language
programmers. However, one advantage to assembly language programming is that you’re not as constrained
as a high level language; this is one of the major reasons why assembly language programs can be more effi-
cient than compiled high level language code. Therefore, it’s a good idea to explore different places where
we can pass parameters in assembly language.
This section discusses six different locations where you can pass parameters. While this is a fair num-
ber of different places, undoubtedly there are many other places where one can pass parameters. So don’t let
this section prejudice you into thinking that this is the only way to pass parameters.
Beta Draft - Do not distribute
© 2000, By Randall Hyde
Page
1341
286680672.001.png
Chapter Four
Volume Five
4.3.1
Passing Parameters in (Integer) Registers
Where you pass parameters depends, to a great e
xtent, on the size and number of those parameters. If
you are passing a small number of bytes to a procedure, then the re
gisters are an e
xcellent place to pass
parameters.
The re
gisters are an ideal place to pass v
alue parameters to a procedure
. If you are passing a sin
-
gle parameter to a procedure you should use the follo
wing re
gisters for the accompan
ying data types:
Data Size
Pass in this Register
Byte:
al
Word:
ax
Double Word:
eax
Quad Word:
edx:eax
This is, by no means, a hard and fast rule. If you find it more convenient to pass 32 bit values in the ESI or
EBX register, by all means do so. However, most programmers use the registers above to pass parameters.
If you are passing several parameters to a procedure in the 80x86’s registers, you should probably use
up the registers in the following order:
First
Last
eax, edx, esi, edi, ebx, ecx
In general, you should avoid using EBP register. If you need more than six parameters, perhaps you should
pass your values elsewhere.
HLA provides a special high level syntax that lets you tell HLA to pass parameters in one or more of the
80x86 integer registers. Consider the following syntax for an HLA parameter declaration:
varname
:
typename
in
register
In this e
xample,
varname
represents the parameter’s name,
typename
is the type of the parameter, and
regis-
is one of the 80x86’s eight-, 16-, or 32-bit integer registers. The size of the data type must be the same as
the size of the register (e.g., "int32" is compatible with a 32-bit register). The following is a concrete exam-
ple of a procedure that passes a character value in a register:
procedure swapcase( chToSwap: char in al ); nodisplay; noframe;
begin swapcase;
if( chToSwap in ’a’..’z’ ) then
and( $5f, chToSwap ); // Convert lower case to upper case.
elseif( chToSwap in ’A’..’Z’ ) then
or( $20, chToSwap );
endif;
ret();
end swapcase;
There are a couple of important issues to note here. First, within the procedure’
s body
, the parameter’
s
name is an alias for the corresponding re
gister if you pass the parameter in a re
gister
. In other w
ords,
c
hT
oSwap
in the pre
vious code is equi
v
alent to "al" (indeed, within the procedure HLA actually defi
nes
c
hT
oSwap
as a
TEXT constant initialized with the string "al").
Also, since the parameter w
as passed in a
re
gister rather than on the stack, there is no need to b
uild a stack frame for this procedure; hence the absence
of the standard entry and e
xit sequences in the code abo
v
e. Note that the code abo
v
e is e
xactly equi
v
alent to
the follo
wing code:
Page
1342
© 2000, By Randall Hyde
Version:
9/9/02
ter
Advanced Parameter Implementation
// Actually, the following parameter list is irrelevant and
// you could remove it. It does, however, help document the
// fact that this procedure has a single character parameter.
procedure swapcase( chToSwap: char in al ); nodisplay; noframe;
begin swapcase;
if( al in ’a’..’z’ ) then
and( $5f, al ); // Convert lower case to upper case.
elseif( al in ’A’..’Z’ ) then
or( $20, al );
endif;
ret();
end swapcase;
Whene
v
er you call the
swapcase
procedure with some actual (byte sized) parameter
, HLA will generate
the appropriate code to mo
v
e that character v
alue into the
AL re
gister prior to the call (assuming you don’
t
specify
AL as the parameter
, in which case HLA doesn’
t generate an
y e
xtra code at all). Consider the fol
-
lo
wing calls that the corresponding code that HLA generates:
// swapcase( ’a’ );
mov( ’a’, al );
call swapcase;
// swapcase( charVar );
mov( charVar, al );
call swapcase;
// swapcase( (type char [ebx]) );
mov( [ebx], al );
call swapcase;
// swapcase( ah );
mov( ah, al );
call swapcase;
// swapcase( al );
call swapcase; // al’s value is already in al!
The examples above all use the pass by value parameter passing mechanism. When using pass by value
to pass parameters in registers, the size of the actual parameter (and formal parameter) must be exactly the
same size as the register. Therefore, you are limited to passing eight, sixteen, or thirty-two bit values in the
registers by value. Furthermore, these object must be scalar objects. That is, you cannot pass composite
(array or record) objects in registers even if such objects are eight, sixteen, or thirty-two bits long.
You can also pass reference parameters in registers. Since pass by reference parameters are four-byte
addresses, you must always specify a thirty-two bit register for pass by reference parameters. For example,
consider the following memfill function that copies a character parameter (passed in AL) throughout some
number of memory locations (specified in ECX), at the memory location specified by the value in EDI:
// memfill- This procedure stores <ECX> copies of the byte in AL starting
Beta Draft - Do not distribute
© 2000, By Randall Hyde
Page
1343
Chapter Four
Volume Five
// at the memory location specified by EDI:
procedure memfill
(
charVal: char in al;
count: uns32 in ecx;
var dest: byte in edi // dest is passed by reference
);
nodisplay; noframe;
begin memfill;
pushfd(); // Save D flag;
push( ecx ); // Preserve other registers.
push( edi );
cld(); // increment EDI on string operation.
rep.stosb(); // Store ECX copies of AL starting at EDI.
pop( edi );
pop( ecx );
popfd();
ret(); // Note that there are no parameters on the stack!
end memfill;
It is perfectly possible to pass some parameters in registers and other parameters on the stack to an HLA
procedure. Consider the following implementation of memfill that passes the dest parameter on the stack:
procedure memfill
(
charVal: char in al;
count: uns32 in ecx;
var dest: var
);
nodisplay;
begin memfill;
pushfd(); // Save D flag;
push( ecx ); // Preserve other registers.
push( edi );
cld(); // increment EDI on string operation.
mov( dest, edi ); // get dest address into EDI for STOSB.
rep.stosb(); // Store ECX copies of AL starting at EDI.
pop( edi );
pop( ecx );
popfd();
end memfill;
Of course, you don’t have to use the HLA high level procedure calling syntax when passing parameters
in the registers. You can manually load the values into registers prior to calling a procedure (with the CALL
instruction) and you can refer directly to those values via registers within the procedure. The disadvantage
to this scheme, of course, is that the code will be a little more difficult to write, read, and modify. The advan-
tage of the scheme is that you have more control and can pass any eight, sixteen, or thirty-two bit value
between the procedure and its callers (e.g., you can load a four-byte array or record into a 32-bit register and
call the procedure with that value in a single register, something you cannot do when using the high level
language syntax for procedure calls). Fortunately, HLA gives you the choice of whichever parameter pass-
Page 1344
© 2000, By Randall Hyde
Version: 9/9/02
Advanced Parameter Implementation
ing scheme is most appropriate, so you can use the manual passing mechanism when it’s necessary and use
the high level syntax whenever it’s not necessary.
There are other parameter passing mechanism beyond pass by value and pass by reference that we will
explore in this chapter. We will take a look at ways of passing parameters in registers using those parameter
passing mechanisms as we encounter them.
4.3.2 Passing Parameters in FPU and MMX Registers
Since the 80x86’s FPU and MMX registers are also registers, it makes perfect sense to pass parameters
in these locations if appropriate. Although using the FPU and MMX registers is a little bit more work than
using the integer registers, it’s generally more efficient than passing the parameters in memory (e.g., on the
stack). In this section we’ll discuss the techniques and problems associated with passing parameters in these
registers.
The first thing to keep in mind is that the MMX and FPU register sets are not independent. These two
register sets overlap, much like the eight, sixteen, and thirty-two bit integer registers. Therefore, you cannot
pass some parameters in FPU registers and other parameters in MMX registers to a given procedure. For
more details on this issue, please see the chapter on the MMX Instruction Set. Also keep in mind that you
must execute the EMMS instruction after using the MMX instructions before executing any FPU instruc-
tions. Therefore, it’s best to partition your code into sections that use the FPU registers and sections that use
the MMX registers (or better yet, use only one register set throughout your program).
The FPU represents a fairly special case. First of all, it only makes sense to pass real values through the
FPU registers. While it is technically possible to pass other values through the FPU registers, efficiency and
accuracy restrictions severely limit what you can do in this regard. This text will not consider passing any-
thing other than real values in the floating point registers, but keep in mind that it is possible to pass generic
groups of bits in the FPU registers if you’re really careful. Do keep in mind, though, that you need a very
detailed knowledge of the FPU if you’re going to attempt this (exceptions, rounding, and other issues can
cause the FPU to incorrectly manipulate your data under certain circumstances). Needless to say, you can
only pass objects by value through the FPU registers; pass by reference isn’t applicable here.
Assuming you’re willing to pass only real values through the FPU registers, some problems still remain.
In particular, the FPU’s register architecture does not allow you to load the FPU registers in an arbitrary
fashion. Remember, the FPU register set is a stack; so you have to push values onto this stack in the reverse
order you wish the values to appear in the register file. For example, if you wish to pass the real variables r,
s, and t in FPU registers ST0, ST1, and ST2, you must execute the following code sequence (or something
similar):
fld( t ); // t -> ST0, but ultimately winds up in ST2.
fld( s ); // s -> ST0, but ultimately winds up in ST1.
fld( r ); // r -> ST0.
You cannot load some floating point value into an arbitrary FPU register without a bit of work. Further-
more, once inside the procedure that uses real parameters found on the FPU stack, you cannot easily access
arbitrary values in these registers. Remember, FPU arithmetic operations automatically "renumber" the FPU
registers as the operations push and pop data on the FPU stack. Therefore, some care and thought must go
into the use of FPU registers as parameter locations since those locations are dynamic and change as you
manipulate items on the FPU stack.
By far, the most common use of the FPU registers to pass value parameters to a function is to pass a sin-
gle value parameter in the register so the procedure can operate directly on that parameter via FPU opera-
tions. A classic example might be a SIN function that expects its angle in degrees (rather than radians, and
the FSIN instruction expects). The function could convert the degree to radians and then execute the FSIN
instruction to complete the calculation.
Keep in mind the limited size of the FPU stack. This effectively eliminates the possibility of passing
real parameter values through the FPU registers in a recursive procedure. Also keep in mind that it is rather
difficult to preserve FPU register values across a procedure call, so be careful about using the FPU registers
Beta Draft - Do not distribute
© 2000, By Randall Hyde
Page 1345
Zgłoś jeśli naruszono regulamin