11.9 Declaring parameters for register call functions
------------------------------------------------------
There used to be a specific register corresponding strictly to each
parameter of a register function. For instance, for variables of type 'int'
or 'word' the first parameters was transferred via AX, the second via BX,
the third via CX, the fourth via DX, the fifth via DI, and the sixth via
SI. Therefore in order to transfer only one parameter via SI, you first had
to write five commas before it. The following example is a call to the
function 'STRCPY':
void main ()
{
STRCPY ( , , , , #dest, #sourc ) ;
}
Now, however, registers can be used in any way when transferring
parameters. You only need to tell the compiler which register is assigned
to each parameter of a function. After this declaration, the compiler will
take care of which register a function parameter is transferred to, its
size, and the number of parameters being transferred. Here is an example
of the declaration and use of 'STRCPY':
void STRCPY ( DI, SI ); // function declaration
void main ()
{
STRCPY ( #dest, #sourc ); // function call
}
Instead of declaring a function, you can indicate the position of the
registers in the function heading. But then the function can be called only
after it has been defined. Here is an example of a function which displays
several identical characters:
void PUTNCHAR(AL,CX,BL,BH)
/* 1 parameter to AL - code for character to be displayed
2 parameter to CX - number of characters to be displayed
3 parameter to BL - color attribute
4 parameter to BH - video mode
*/
{
AH=9;
$INT 0x10
}
When declaring a register function, you can also indicate the type of
variable expected by the function (signed, unsigned, or float). The default
is signed. But it only makes sense to indicate signed if the parameter is
transferred via registers AL, AX, or EAX. The variable is always
transferred via other registers as unsigned. Here is an example of
declaring a register function with indication of type:
int fastcall Exampl(word CX, int AX, DX, float ESI ) ;
| | | | | | |
| | | | | | |---- 4 param. has a type float
| | | | | | and re-d. over register ESI.
| | | | | |-------- 3 param. has on default type
| | | | | word and re-d. over DX.
| | | | |------------ 2 param. has a type int and
| | | | is transferred over register AX.
| | | |---------------------- 1 param. has a type word and
| | | is transferred over register CX.
| | |------------------------------- Name declared function.
| |---------------------------------------- Modifier, pointing that
| this funct. register.
|--------------------------------------------- Function returns change.
type int.
If the function registers were declared, the compiler will keep strict
track of the number of these parameters when calling the function and will
give error messages if there are too few or too many. On one hand this is
good - if it tracks them, nothing will be forgotten and and nothing extra
will be added when calling a function. On the other hand, there are
sometimes unnecessary parameters and these will need to be registered. But
if you neglect to register one parameter when calling a function, the
compiler will give an error message. This makes it possible to initialize
the register via which the parameters are being transferred apart from
calling the function. If one paremeter is registered, however, all the
others will need to be registered or else the compiler will think it was
accidentally omitted and will give an error message.
If registers were not declared either during declaration of a register
function or in the heading of the function itself, the compiler will think
that the parameters are to be transferred to this function by the old
method. This makes for perfect compatibility with the previous version of
the compiler.