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.