ParameterImplementation.pdf

(404 KB) Pobierz
Advanced Parameter Implementation
Advanced Parameter Implementation
4.1
Chapter Overview
Chapter Four
This chapter discusses advanced parameter passing techniques in assembly language. Both low-level
and high-level syntax appears in this chapter. This chapter discusses the more advanced pass by value/result,
pass by result, pass by name, and pass by lazy evaluation parameter passing mechanisms. This chapter also
discusses how to pass parameters in a low-level manner and describes where you can pass such parameters.
4.2
Parameters
Although there is a large class of procedures that are totally self-contained, most procedures require
some input data and return some data to the caller. Parameters are values that you pass to and from a proce-
dure. There are many facets to parameters. Questions concerning parameters include:
• where is the data coming from?
• how 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’ve mainly used the 80x86 hardware stack to pass parameters. In a few examples
we’ve used machine registers to pass parameters to a procedure. In this section we explore several different
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
Chapter Four
Volume Five
4.3.1 Passing Parameters in (Integer) Registers
Where you pass parameters depends, to a great extent, on the size and number of those parameters. If
you are passing a small number of bytes to a procedure, then the registers are an excellent place to pass
parameters. The registers are an ideal place to pass value parameters to a procedure. If you are passing a sin-
gle parameter to a procedure you should use the following registers for the accompanying data types:
Data Size
Byte:
Word:
Double Word:
Quad Word:
Pass in this Register
al
ax
eax
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
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
Last
In this example,
varname
represents the parameter’s name,
typename
is the type of the parameter, and
regis-
ter
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 register if you pass the parameter in a register. In other words,
chToSwap
in the previous code is equivalent to "al" (indeed, within the procedure HLA actually defines
chToSwap
as a TEXT constant initialized with the string "al"). Also, since the parameter was passed in a
register rather than on the stack, there is no need to build a stack frame for this procedure; hence the absence
of the standard entry and exit sequences in the code above. Note that the code above is exactly equivalent to
the following code:
Page 1342
© 2000, By Randall Hyde
Version: 9/12/02
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;
Whenever you call the
swapcase
procedure with some actual (byte sized) parameter, HLA will generate
the appropriate code to move that character value into the AL register prior to the call (assuming you don’t
specify AL as the parameter, in which case HLA doesn’t generate any extra code at all). Consider the fol-
lowing 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
// at the memory location specified by EDI:
procedure memfill
(
charVal: char in al;
count: uns32 in ecx;
var
dest: byte in edi
);
nodisplay; noframe;
begin memfill;
pushfd();
push( ecx );
push( edi );
cld();
rep.stosb();
pop( edi );
pop( ecx );
popfd();
ret();
end memfill;
// Save D flag;
// Preserve other registers.
Volume Five
// dest is passed by reference
// increment EDI on string operation.
// Store ECX copies of AL starting at EDI.
// Note that there are no parameters on the stack!
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();
push( ecx );
push( edi );
cld();
mov( dest, edi );
rep.stosb();
pop( edi );
pop( ecx );
popfd();
end memfill;
// Save D flag;
// Preserve other registers.
// increment EDI on string operation.
// get dest address into EDI for STOSB.
// Store ECX copies of AL starting at EDI.
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/12/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 );
fld( s );
fld( r );
// t -> ST0, but ultimately winds up in ST2.
// s -> ST0, but ultimately winds up in ST1.
// 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