IntroductionToProcedures.pdf
(
400 KB
)
Pobierz
Introduction to Procedures
Introduction to Procedures
8.1
Chapter Overview
Chapter Eight
In a procedural programming language the basic unit of code is the
procedure
. A procedure is a set of
instructions that compute some value or take some action (such as printing or reading a character value). The
definition of a procedure is very similar to the definition of an
algorithm
. A procedure is a set of rules to fol-
low which, if they conclude, produce some result. An algorithm is also such a sequence, but an algorithm is
guaranteed to terminate whereas a procedure offers no such guarantee.
This chapter discusses how HLA implements procedures. This is actually the first of three chapters on
this subject in this text. This chapter presents HLA procedures from a high level language perspective. A
later chapter, Intermediate Procedures, discusses procedures at the machine language level. A whole volume
in this sequence, Advanced Procedures, covers advanced programming topics of interest to the very serious
assembly language programmer. This chapter, however, provides the foundation for all that follows.
8.2
Procedures
Most procedural programming languages implement procedures using the call/return mechanism. That
is, some code calls a procedure, the procedure does its thing, and then the procedure returns to the caller. The
call and return instructions provide the 80x86’s
procedure invocation mechanism
. The calling code calls a
procedure with the
CALL
instruction, the procedure returns to the caller with the
RET
instruction. For exam-
ple, the following 80x86 instruction calls the HLA Standard Library
stdout.newln
routine
1
:
call stdout.newln;
The
stdout.newln
procedure prints a newline sequence to the console device and returns control to the
instruction immediately following the “call stdout.newln;” instruction.
Alas, the HLA Standard Library does not supply all the routines you will need. Most of the time you’ll
have to write your own procedures. To do this, you will use HLA’s procedure declaration facilities. A basic
HLA procedure declaration takes the following form:
procedure
ProcName
;
<< Local declarations >>
begin
ProcName
;
<< procedure statements >>
end
ProcName
;
Procedure declarations appear in the declaration section of your program. That is, anywhere you can
put a STATIC, CONST, TYPE, or other declaration section, you may place a procedure declaration. In the
syntax example above,
ProcName
represents the name of the procedure you wish to define. This can be any
valid HLA identifier. Whatever identifier follows the PROCEDURE reserved word must also follow the
BEGIN and END reserved words in the procedure. As you’ve probably noticed, a procedure declaration
looks a whole lot like an HLA program. In fact, the only difference (so far) is the use of the PROCEDURE
reserved word rather than the PROGRAM reserved word.
Here is a concrete example of an HLA procedure declaration. This procedure stores zeros into the 256
double words that EBX points at upon entry into the procedure:
procedure zeroBytes;
begin zeroBytes;
mov( 0, eax );
1. Normally you would call newln using the “newln();” statement, but the CALL instruction works as well.
Beta Draft - Do not distribute
© 2001, By Randall Hyde
Page 541
Chapter Eight
mov( 256, ecx );
repeat
mov( eax, [ebx] );
add( 4, ebx );
dec( ecx );
until( @z );
end zeroBytes;
// That is, until ECX=0.
Volume Three
You can use the 80x86 CALL instruction to call this procedure. When, during program execution, the
code falls into the “end zeroBytes;” statement, the procedure returns to whomever called it and begins exe-
cuting the first instruction beyond the CALL instruction. The following program provides an example of a
call to the
zeroBytes
routine:
program zeroBytesDemo;
#include( “stdlib.hhf” );
procedure zeroBytes;
begin zeroBytes;
mov( 0, eax );
mov( 256, ecx );
repeat
mov( eax, [ebx] );
add( 4, ebx );
dec( ecx );
until( ecx = 0 );
end zeroBytes;
static
dwArray: dword[256];
begin zeroBytesDemo;
lea( ebx, dwArray );
call zeroBytes;
end zeroBytesDemo;
// Zero out current dword.
// Point ebx at next dword.
// Count off 256 dwords.
// Repeat for 256 dwords.
Program 8.1
Example of a Simple Procedure
As you may have noticed when calling HLA Standard Library procedures, you don’t always need to use
the CALL instruction to call HLA procedures. There is nothing special about the HLA Standard Library
procedures versus your own procedures. Although the formal 80x86 mechanism for calling procedures is to
use the CALL instruction, HLA provides a HLL extension that lets you call a procedure by simply specify-
ing that procedure’s name followed by an empty set of parentheses
2
. For example, either of the following
statements will call the HLA Standard Library
stdout.newln
procedure:
2. This assumes that the procedure does not have any parameters.
Page 542
© 2001, By Randall Hyde
Beta Draft - Do not distribute
Introduction to Procedures
call stdout.newln;
stdout.newln();
Likewise, either of the following statements will call the
zeroBytes
procedure in Program 8.1:
call zeroBytes;
zeroBytes();
The choice of calling mechanism is strictly up to you. Most people, however, find the HLL syntax easier to
read.
8.3
Saving the State of the Machine
Take a look at the following program:
program nonWorkingProgram;
#include( “stdlib.hhf” );
procedure PrintSpaces;
begin PrintSpaces;
mov( 40, ecx );
repeat
stdout.put( ‘ ‘ );
dec( ecx );
until( ecx = 0 );
end PrintSpaces;
begin nonWorkingProgram;
mov( 20, ecx );
repeat
PrintSpaces();
stdout.put( ‘*’, nl );
dec( ecx );
until( ecx = 0 );
end nonWorkingProgram;
// Print 1 of 40 spaces.
// Count off 40 spaces.
Program 8.2
Program with an Unintended Infinite Loop
This section of code attempts to print 20 lines of 40 spaces and an asterisk. Unfortunately, there is a sub-
tle bug that causes it to print 40 spaces per line and an asterisk in an infinite loop. The main program uses the
REPEAT..UNTIL loop
to call
PrintSpaces
20 times.
PrintSpaces
uses ECX to count off the 40 spaces it
prints.
PrintSpaces
returns with ECX containing zero. The main program then prints an asterisk, a newline,
decrements ECX, and then repeats because ECX isn’t zero (it will always contain $FFFF_FFFF at this
point).
Beta Draft - Do not distribute
© 2001, By Randall Hyde
Page 543
Chapter Eight
Volume Three
The problem here is that the
PrintSpaces
subroutine doesn’t preserve the ECX register. Preserving a
register means you save it upon entry into the subroutine and restore it before leaving. Had the
PrintSpaces
subroutine preserved the contents of the ECX register, the program above would have functioned properly.
Use the 80x86’s
PUSH
and
POP
instructions to preserve register values while you need to use them for
something else. Consider the following code for
PrintSpaces:
procedure PrintSpaces;
begin PrintSpaces;
push( eax );
push( ecx );
mov( 40, ecx );
repeat
stdout.put( ' ' );
dec( ecx );
until( ecx = 0 );
pop( ecx );
pop( eax );
end PrintSpaces;
// Print 1 of 40 spaces.
// Count off 40 spaces.
Note that
PrintSpaces
saves and restores EAX and ECX (since this procedure modifies these registers).
Also, note that this code pops the registers off the stack in the reverse order that it pushed them. The last-in,
first-out, operation of the stack imposes this ordering.
Either the caller (the code containing the CALL instruction) or the callee (the subroutine) can take
responsibility for preserving the registers. In the example above, the callee preserved the registers. The fol-
lowing example shows what this code might look like if the caller preserves the registers:
program callerPreservation;
#include( “stdlib.hhf” );
procedure PrintSpaces;
begin PrintSpaces;
mov( 40, ecx );
repeat
stdout.put( ‘ ‘ );
dec( ecx );
until( ecx = 0 );
end PrintSpaces;
begin callerPreservation;
mov( 20, ecx );
repeat
push( eax );
push( ecx );
PrintSpaces();
pop( ecx );
pop( eax );
stdout.put( ‘*’, nl );
dec( ecx );
// Print 1 of 40 spaces.
// Count off 40 spaces.
Page 544
© 2001, By Randall Hyde
Beta Draft - Do not distribute
Introduction to Procedures
until( ecx = 0 );
end callerPreservation;
Program 8.3
Demonstration of Caller Register Preservation
There are two advantages to callee preservation: space and maintainability. If the callee preserves all
affected registers, then there is only one copy of the PUSH and POP instructions, those the procedure con-
tains. If the caller saves the values in the registers, the program needs a set of PUSH and POP instructions
around every call. Not only does this make your programs longer, it also makes them harder to maintain.
Remembering which registers to push and pop on each procedure call is not something easily done.
On the other hand, a subroutine may unnecessarily preserve some registers if it preserves all the regis-
ters it modifies. In the examples above, the code needn’t save EAX. Although
PrintSpaces
changes AL, this
won’t affect the program’s operation. If the caller is preserving the registers, it doesn’t have to save registers
it doesn’t care about:
program callerPreservation2;
#include( “stdlib.hhf” );
procedure PrintSpaces;
begin PrintSpaces;
mov( 40, ecx );
repeat
stdout.put( ‘ ‘ );
dec( ecx );
until( ecx = 0 );
end PrintSpaces;
begin callerPreservation2;
mov( 10, ecx );
repeat
push( ecx );
PrintSpaces();
pop( ecx );
stdout.put( ‘*’, nl );
dec( ecx );
until( ecx = 0 );
// Print 1 of 40 spaces.
// Count off 40 spaces.
mov( 5, ebx );
while( ebx > 0 ) do
PrintSpaces();
stdout.put( ebx, nl );
dec( ebx );
Beta Draft - Do not distribute
© 2001, By Randall Hyde
Page 545
Plik z chomika:
jezuss
Inne pliki z tego folderu:
IntroductionToProcedures.pdf
(400 KB)
LexicalNesting.pdf
(297 KB)
Volume1.pdf
(29 KB)
DataRepresentation.pdf
(470 KB)
ClassesAndObjects.pdf
(472 KB)
Inne foldery tego chomika:
Linux - Administrator
Linux - net
Linux - Podrecznik Administratora Sieci
Linux podrecznik administratora - (PL) [PDF]
Pawel Krawczyk - Ruting IP w Linuxie 2.2
Zgłoś jeśli
naruszono regulamin