DECLARING PROCEDURES, FUNCTIONS AND MACROS

Types of Procedures, Functions and Macros

There are two main types of procedures, stack and REG procedures.  Stack
procedures pass parameters on the stack and REG procedures pass the
parameters via registers.  Both stack and REG procedures can act as functions
by returning values, via the return() command.  Only REG procedures declared
as dynamic can be used as macros.


1)  Stack Procedures

Stack procedures are defined by using an identifer that contains at least one
lower case letter, thus they can be easily distingushed from REG procedures
for REG procedure names may not contain any lower case letters.

Parameters for stack procedures, if any, may be of any type (specified by
'byte', 'char', 'word', 'int', 'dword' or 'long').  Parameters are passed
using a Pascal-like calling convention, that is, the first parameter is
pushed first and the second parameter is pushed second, and so on.  The
Pascal-calling convention does not support variable number of parameters,
so you have to be sure to pass the proper number of parameters to a stack
procedure.

The following example stack procedure returns the sum as a 'word' of all it
parameters, which are of different types:

    word add_them_all (int a,b,c; byte d,e; word x,y)
    {
    return( a+b+c+d+e+x+y );
    }
2)  REG Procedures

REG procedures are defined by using an identifer that does not contain any
lower case letters.

As mentioned, the parameters (if any) for a REG procedure are passed via
registers.  REG procedures have a maximum of 6 parameters.  The registers
used if the parameters are of type 'int' or 'word', in order, are AX, BX, CX,
DX, DI, and SI.  The first four parameters can also be of the type 'char' or
'byte', in this case AL, BL, CL and DL are used respectively.  Any of the six
parameters can be of type 'long' or 'dword', in which case EAX, EBX, ECX,
EDX, EDI, or ESI would be used.

An example of a REG procedure named 'TOGETHER' that returns a 'word' value
which is the first parameter multiplied to the second parameter, both
parameters are 'word's:

    word TOGETHER ()  /* AX = first param, BX = second param */
    {
    return( AX * BX );
    }

An example of a REG procedure named 'SHOW_NUM' that does not return any value
but writes the first parameter (which is an 'int') followed by the second
parameter (which is a 'byte') to the screen separated by a ':':

    void SHOW_NUM ()  /* AX = first number, BL = second number */
    {
    ? PUSH BX
    WRITEINT(int AX);
    WRITE(':');
    ? POP BX
    WRITEWORD(BL);
    }

In order for a REG procedure to be used as a macro, it must be declared as a
dynamic procedure.  Dynamic procedures are described in the following
sub-section.

3)  Dynamic Procedures

Dynamic procedures are procedures which are defined but only inserted into
the program code if called.  Only REG procedures defined as dynamic
procedures may be used as macros.

Dynamic procedures are specified by a preceding ':'.

Since dynamic procedures can be relocated anywhere in the code and possibly
in more than one location, several restrictions are nessasary.  These
restrictions are:

    - Only macros may be used within the dynamic procedure.  Calls to REG
      or stack procedures are illegal.

    - Constant strings cannot be defined within a dynamic procedure.  To
      use a constant string within a dynamic procedure, define the
      contant string as a global variable.

    - Only local jump labels may be defined within a dynamic procedure.
      Global jump labels are illegal.

    - Jumps to addresses outside the dynamic procedure are illegal.  No
      error message will be given, but the results, as you can imagine,
      are unpredictable.

    - For dynamic REG procedures that are going to be used as macros, the
      'return()' command can not be used.  See the end of sub-section
      1.5.5 for more info.

An example of a dynamic stack procedure:

    : void setvideomode (byte mode)
    {
    AL = mode;
    AH = 0;
    $ INT 0x10
    }

An example of a dynamic REG procedure (and could be used as a macro):

    : int ABS ()  /* AX = number to get absolute value of */
    {
    IF(int AX < 0 )
        -AX;
    } 

 

Return Values

Return values from functions are returned via registers, below is a table
showing what register is used for each return type:

      return type  |  register returned in
    ----------------------------------------
         byte      |        AL
         word      |        AX
         dword     |        EAX
         char      |        AL
         int       |        AX
         long      |        EAX

The easiest way to return a value from a function is to use the 'return()'
command, but the appropriate register can also be assigned the required
return value instead.  For example, the following two functions return the
same value:

    byte proc_one ()
    {
    return( 42 );
    }

    byte proc_two ()
    {
    AL = 42;
    }

Take note, for dynamic REG procedures that you wish to use as macros, the
'return()' command cannot be used, for the 'return()' also executes a 'RET'
command.  Thus for macros that are functions, the appropriate return value
register must be assigned directly.

Post a Comment

0 Comments