### Examples Using the _System Calling Convention (OS/2)

The following examples are for illustration only. They have not been optimized. The examples assume that you are familiar with programming in assembler. Note that, in the examples, the stack grows toward the bottom of the page, and ESP always points to the top of the stack.

For the call

       m = func(a,b,c);

a, b, and c are 32-bit integers and func has two local variables, x and y (both 32-bit integers).

The stack for the call to func would look like this:

The instructions used to create this activation record on the stack look like this on the calling side:

       PUSH    c
       PUSH    b
       PUSH    a
       MOV     AL, 3H
       CALL    func
           .
           .
       ADD     ESP, 12    ; Cleaning up the parameters
           .
           .
       MOV     m, EAX
           .
           .

Note: the MOV AL,3H instruction is not present unless the /Gp+ compiler option is set (to support parmdwords).

For the callee, the code looks like this:

    func PROC
       PUSH    EBP
       MOV     EBP, ESP       ;  Allocating 8 bytes of storage
       SUB     ESP, 8         ;  for two local variables.
       PUSH    EDI            ;  These would only be
       PUSH    ESI            ;  pushed if they were used
       PUSH    EBX            ;  in this function.
       .
       .
       MOV     EAX, [EBP - 8]    ; Load y into EAX
       MOV     EBX, [EBP + 12]   ; Load b into EBX
       .
       .
       XOR     EAX, EAX           ; Zero the return value
       POP     EBX                ; Restore the saved registers
       POP     ESI
       POP     EDI
       LEAVE                      ; Equivalent to  MOV   ESP, EBP
                                  ;                POP   EBP
       RET
    func ENDP

The saved register set is EBX, ESI, and EDI. The other registers (EAX, ECX, and EDX) can have their contents changed by a called routine.

Floating-point results are returned in ST(0), which is the top of the floating-point register stack. If there is no numeric coprocessor installed in the system, the OS/2 operating system emulates the coprocessor.

Floating-point parameters are pushed on the run-time stack.

Under some circumstances, the compiler will not use EBP to access automatic and parameter values, thus increasing the efficiency of the application. Whether it is used or not, EBP will not change across the call.

When passing structures _System convention as value parameters, the compiler generates code to copy the structure on to the run-time stack. If the size of the structure is larger than a run-time page size (4K), the compiler generates code to copy the structure backward. (That is, the last byte in the structure is the first to be copied.) This operation ensures that the OS/2 guard page _System linkage method of stack growth will function properly in the presence of large structures being passed by value.

Structures are not returned on the stack. The caller pushes the address where the returned structure is to be placed as a lexically first hidden parameter. A function that returns a structure must be aware that all parameters are 4 bytes farther away from EBP than they would be if no structure return were involved. The address of the returned structure is returned in EAX.

In the most common case, where the return from a function is simply assigned to a variable, the compiler merely pushes the address of the variable as the hidden parameter. For example:

     struct test_tag
           {
           int a;
           int some_array[100];
           } test_struct;

    struct test_tag test_function(struct test_tag test_parm)
    {
       test_parm.a = 42;
       return test_parm;
    }

    int main(void)
    {
       test_struct = test_function(test_struct);
       return test_struct.a;
    }

The code generated for this program would be:

    test_function  PROC
       PUSH    ESI
       PUSH    EDI
       MOV     DWORD PTR [ESP+0cH], 02aH     ; test_parm.a
       MOV     EAX, [ESP+08H]                ; Get the target of the return value
       MOV     EDI, EAX                      ; Value
       LEA     ESI, [ESP+0cH]                ; test_parm
       MOV     ECX, 065H
       REP MOVSD
       POP     EDI
       POP     ESI
       RET
    test_function  ENDP

       PUBLIC  main
    main  PROC
       PUSH    EBP
       MOV     EBP, ESP
       PUSH    ESI
       PUSH    EDI

       SUB     ESP, 0194H                  ; Adjust the stack pointer
       MOV     EDI, ESP
       MOV     ESI, OFFSET FLAT: test_struct
       MOV     ECX, 065H
       REP MOVSD                           ; Copy the parameter
       MOV     AL, 065H
       PUSH    OFFSET FLAT: test_struct    ; Push the address of the target
       CALL    test_function
       ADD     ESP, 0198H

       MOV     EAX, DWORD PTR test_struct  ; Take care of the return
       POP     EDI                         ;  from main
       POP     ESI
       LEAVE
       RET
    main   ENDP

Note: The MOV AL, 065H is not present unless the /Gp+ compiler option is specified.

In a slightly different case, where only one field of the structure is used by the caller (as shown in the following example), the compiler allocates sufficient temporary storage in the caller's local storage area on the stack to contain a copy of the structure. The address of this temporary storage will be pushed as the target for the return value. Once the call is completed, the desired member of the structure can be accessed as an offset from EAX, as can be seen in the code generated for the example:

     struct test_tag
           {
           int a;
           int some_array[100];
           } test_struct;

    struct test_tag test_function(struct test_tag test_parm)
    {
       test_parm.a = 42;
       return test_parm;
    }

    int main(void)
    {
       return test_function(test_struct).a;
    }

The code generated for this example would be:

       PUBLIC  main
    main   PROC
       PUSH    EBP
       MOV     EBP, ESP
       SUB     ESP, 0194H     ; Allocate space for compiler-generated
       PUSH    ESI            ;  temporary variable
       PUSH    EDI
       SUB     ESP, 0194H
       MOV     EDI, ESP
       MOV     ESI, OFFSET FLAT: test_struct
       MOV     ECX, 065H
       REP MOVSD
       LEA     EAX, [ESP+019cH]
       PUSH    EAX
       MOV     AL, 065H
       CALL    test_function
       ADD     ESP, 0198H
       MOV     EAX, [EAX]      ; Note the convenience of having the
       POP     EDI             ;  address of the returned structure
       POP     ESI             ;  in EAX
       LEAVE
       RET
    main   ENDP

Again, the MOV AL,3H instruction is not present unless the /Gp+ compiler option is specified.

Calling Conventions
Stack Allocation

_System Calling Convention in OS/2