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). This chapter discusses how HLA implements procedures. It begins by discussing HLA's high-level syntax for procedure declarations and invocations, but it also describes the low-level implementation of procedures at the machine level. At this point, you should be getting comfortable with assembly language programming, so it's time to start presenting "pure" assembly language rather than continuing to rely on HLA's high-level syntax as a crutch.
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 and the procedure returns to the caller with the ret
instruction. For example, the following 80x86 instruction calls the HLA Standard Library stdout.newln
routine:[70]
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 ever 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:
procedureProcName
; << Local declarations >> beginProcName
; << Procedure statements >> endProcName
;
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 (and unique) 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 ); mov( 256, ecx ); repeat mov( eax, [ebx] ); add( 4, ebx ); dec( ecx ); until( @z ); // That is, until ecx=0. end zeroBytes;
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 whoever called it and begins executing the first instruction beyond the call
instruction. The program in Example 5-1 provides an example of a call to the zeroBytes
routine.
Example 5-1. Example of a simple procedure
program zeroBytesDemo; #include( "stdlib.hhf" ) procedure zeroBytes; begin zeroBytes; mov( 0, eax ); mov( 256, ecx ); repeat mov( eax, [ebx] ); // Zero out current dword. add( 4, ebx ); // Point ebx at next dword. dec( ecx ); // Count off 256 dwords. until( ecx = 0 ); // Repeat for 256 dwords. end zeroBytes; static dwArray: dword[256]; begin zeroBytesDemo; lea( ebx, dwArray ); call zeroBytes; end zeroBytesDemo;
As you may have noticed when calling HLA Standard Library procedures, you don't have 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 high-level extension that lets you call a procedure by simply specifying the procedure's name followed by an empty set of parentheses.[71] For example, either of the following statements will call the HLA Standard Library stdout.newln
procedure:
call stdout.newln; stdout.newln();
Likewise, either of the following statements will call the zeroBytes
procedure in Example 5-1:
call zeroBytes; zeroBytes();
The choice of calling mechanism is strictly up to you. Most people, however, find the high-level syntax easier to read.