Retro video games delivered to your door every month!
Click above to get retro games delivered to your door ever month!
X-Hacker.org- Watcom C/C++ User's Guide - by default, floating-point arguments are passed on the 80x86 stack. the http://www.X-Hacker.org [<<Previous Entry] [^^Up^^] [Next Entry>>] [Menu] [About The Guide]
By default, floating-point arguments are passed on the 80x86 stack.  The
80x86 registers are never used to pass floating-point arguments when a
function is compiled with the "fpi" or "fpi87" option.  However, they can be
used to pass arguments whose type is not floating-point such as arguments of
type "int".

The following form of the auxiliary pragma can be used to describe the
registers that are to be used to pass arguments to functions.

+--------------------------------------------------------------------------+
|      #pragma aux sym parm {reg_set} [;]                                  |
|                                                                          |
+--------------------------------------------------------------------------+

where
    description

sym
    is a function name.

reg_set
    is a register set.  The register set can contain 80x86 registers and/or
    the string "8087".

Notes:

 1. If an empty register set is specified, all arguments, including
    floating-point arguments, will be passed on the 80x86 stack.

When the string "8087" appears in a register set, it simply means that
floating-point arguments can be passed in 80x87 floating-point registers if
the source file is compiled with the "fpi" or "fpi87" option.  Before
discussing argument passing in detail, some general notes on the use of the
80x87 floating-point registers are given.

The 80x87 contains 8 floating-point registers which essentially form a
stack.  The stack pointer is called ST and is a number between 0 and 7
identifying which 80x87 floating-point register is at the top of the stack.
ST is initially 0.  80x87 instructions reference these registers by
specifying a floating-point register number.  This number is then added to
the current value of ST.  The sum (taken modulo 8) specifies the 80x87
floating-point register to be used.  The notation ST(n), where "n" is
between 0 and 7, is used to refer to the position of an 80x87 floating-point
register relative to ST.

When a floating-point value is loaded onto the 80x87 floating-point register
stack, ST is decremented (modulo 8), and the value is loaded into ST(0).
 When a floating-point value is stored and popped from the 80x87
floating-point register stack, ST is incremented (modulo 8) and ST(1)
becomes ST(0).  The following illustrates the use of the 80x87
floating-point registers as a stack, assuming that the value of ST is 4 (4
values have been loaded onto the 80x87 floating-point register stack).


                 +----------------+
           0     | 4th from top   |  ST(4)
                 +----------------+
           1     | 5th from top   |  ST(5)
                 +----------------+
           2     | 6th from top   |  ST(6)
                 +----------------+
           3     | 7th from top   |  ST(7)
                 +----------------+
     ST -> 4     | top of stack   |  ST(0)
                 +----------------+
           5     | 1st from top   |  ST(1)
                 +----------------+
           6     | 2nd from top   |  ST(2)
                 +----------------+
           7     | 3rd from top   |  ST(3)
                 +----------------+

Starting with version 9.5, the Watcom compilers use all eight of the 80x87
registers as a stack.  The initial state of the 80x87 register stack is
empty before a program begins execution.

Note:
    For compatibility with code compiled with version 9.0 and earlier, you
    can compile with the "fpr" option.  In this case only four of the eight
    80x87 registers are used as a stack.  These four registers were used to
    pass arguments.  The other four registers form what was called the 80x87
    cache.  The cache was used for local floating-point variables.  The
    state of the 80x87 registers before a program began execution was as
    follows.

     1. The four 80x87 floating-point registers that form the stack are
        uninitialized.

     2. The four 80x87 floating-point registers that form the 80x87 cache
        are initialized with zero.

    Hence, initially the 80x87 cache was comprised of ST(0), ST(1), ST(2)
    and ST(3).  ST had the value 4 as in the above diagram.  When a
    floating-point value was pushed on the stack (as is the case when
    passing floating-point arguments), it became ST(0) and the 80x87 cache
    was comprised of ST(1), ST(2), ST(3) and ST(4).  When the 80x87 stack
    was full, ST(0), ST(1), ST(2) and ST(3) formed the stack and ST(4),
    ST(5), ST(6) and ST(7) formed the 80x87 cache.  Version 9.5 and later no
    longer use this strategy.

The rules for passing arguments are as follows.

 1. If the argument is not floating-point, use the procedure described
    earlier in this chapter.

 2. If the argument is floating-point, and a previous argument has been
    assigned a position on the 80x86 stack (instead of the 80x87 stack), the
    floating-point argument is also assigned a position on the 80x86 stack.
    Otherwise proceed to the next step.

 3. If the string "8087" appears in a register set in the pragma, and if the
    80x87 stack is not full, the floating-point argument is assigned
    floating-point register ST(0) (the top element of the 80x87 stack).  The
    previous top element (if there was one) is now in ST(1).  Since
    arguments are pushed on the stack from right to left, the leftmost
    floating-point argument will be in ST(0).  Otherwise the floating-point
    argument is assigned a position on the 80x86 stack.

Consider the following example.


     #pragma aux myrtn parm [8087];

     void main()
     {
         float    x;
         double   y;
         int      i;
         long int j;

         x = 7.7;
         i = 7;
         y = 77.77;
         j = 77;
         myrtn( x, i, y, j );
     }

myrtn is an assembly language function that requires four arguments.  The
first argument of type float (4 bytes), the second argument is of type int
(4 bytes), the third argument is of type double (8 bytes) and the fourth
argument is of type long int (4 bytes).  These arguments will be passed to
myrtn in the following way.

 1. Since "8087" was specified in the register set, the first argument,
    being of type float, will be passed in an 80x87 floating-point register.

 2. The second argument will be passed on the stack since no 80x86 registers
    were specified in the register set.

 3. The third argument will also be passed on the stack.  Remember the
    following rule:  once an argument is assigned a position on the stack,
    all remaining arguments will be assigned a position on the stack.  Note
    that the above rule holds even though there are some 80x87
    floating-point registers available for passing floating-point arguments.

 4. The fourth argument will also be passed on the stack.

Let us change the auxiliary pragma in the above example as follows.


     #pragma aux myrtn parm [eax 8087];

The arguments will now be passed to myrtn in the following way.

 1. Since "8087" was specified in the register set, the first argument,
    being of type float will be passed in an 80x87 floating-point register.

 2. The second argument will be passed in register EAX, exhausting the set
    of available 80x86 registers for argument passing.

 3. The third argument, being of type double, will also be passed in an
    80x87 floating-point register.

 4. The fourth argument will be passed on the stack since no 80x86 registers
    remain in the register set.

Online resources provided by: http://www.X-Hacker.org --- NG 2 HTML conversion by Dave Pearson