summaryrefslogtreecommitdiffstatshomepage
path: root/docs/source/techspecs/device_memory_interface.rst
blob: 1f8e2b1c37b3cdaea7cb7bbd5f13b9de28dc955c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
The device_memory_interface
===========================

.. contents:: :local:


1. Capabilities
---------------

The device memory interface provides devices with the capability of
creating address spaces, to which address maps can be associated.
It’s used for any device that provides a (logical) address/data bus
that other devices can be connected to.  That’s mainly, but not solely,
CPUs.

The interface allows for an unlimited set of address spaces, numbered
with small, non-negative values.  The IDs index vectors, so they should
stay small to keep the lookup fast.  Spaces numbered 0-3 have associated
constant name:

+----+---------------+
| ID | Name          |
+====+===============+
| 0  | AS_PROGRAM    |
+----+---------------+
| 1  | AS_DATA       |
+----+---------------+
| 2  | AS_IO         |
+----+---------------+
| 3  | AS_OPCODES    |
+----+---------------+

Spaces 0 and 3, i.e. ``AS_PROGRAM`` and ``AS_OPCODES``, are special for
the debugger and some CPUs.  ``AS_PROGRAM`` is use by the debugger and
the CPUs as the space from which the CPU reads its instructions for the
disassembler.  When present, ``AS_OPCODES`` is used by the debugger and
some CPUs to read the opcode part of the instruction.  What opcode means
is device-dependant, for instance for the Z80 it's the initial byte(s)
which are read with the M1 signal asserted, while for the 68000 is means
every instruction word plus PC-relative accesses.  The main, but not
only, use of ``AS_OPCODES`` is to implement hardware decryption of
instructions separately from data.


2. Setup
--------

.. code-block:: C++

    std::vector<std::pair<int, const address_space_config *>> memory_space_config() const;

The device must override that method to provide a vector of pairs
comprising of a space number and an associated ``address_space_config``
describing its configuration.  Some examples to look up when needed:

* Standard two-space vector:
  `v60_device <https://git.redump.net/mame/tree/src/devices/cpu/v60/v60.cpp?h=mame0226>`_
* Conditional ``AS_OPCODES``:
  `z80_device <https://git.redump.net/mame/tree/src/devices/cpu/z80/z80.cpp?h=mame0226>`_
* Inherit configuration and add a space:
  `hd647180x_device <https://git.redump.net/mame/tree/src/devices/cpu/z180/hd647180x.cpp?h=mame0226>`_
* Inherit configuration and modify a space:
  `tmpz84c011_device <https://git.redump.net/mame/tree/src/devices/cpu/z80/tmpz84c011.cpp?h=mame0226>`_

.. code-block:: C++

    bool has_configured_map(int index = 0) const;

The ``has_configured_map`` method allows to test whether an
``address_map`` has been associated with a given space in the
``memory_space_config`` method.  That allows optional memory spaces to
be implemented, such as ``AS_OPCODES`` in certain CPU cores.


3. Associating maps to spaces
-----------------------------
Associating maps to spaces is done at the machine configuration level,
after the device is instantiated.

.. code-block:: C++

    void set_addrmap(int spacenum, T &obj, Ret (U::*func)(Params...));
    void set_addrmap(int spacenum, Ret (T::*func)(Params...));
    void set_addrmap(int spacenum, address_map_constructor map);

These function associate a map with a given space.  Address maps
associated with non-existent spaces are ignored (no warning given).  The
first form takes a reference to an object and a method to call on that
object.  The second form takes a method to call on the current device
being configured.  The third form takes an ``address_map_constructor``
to copy.  In each case, the function must be callable with reference to
an ``address_map`` object as an argument.

To remove a previously configured address map, call ``set_addrmap`` with
a default-constructed ``address_map_constructor`` (useful for removing a
map for an optional space in a derived machine configuration).

As an example, here’s the address map configuration for the main CPU in
the Hana Yayoi and Hana Fubuki machines, with all distractions removed:

.. code-block:: C++

    class hnayayoi_state : public driver_device
    {
    public:
        void hnayayoi(machine_config &config);
        void hnfubuki(machine_config &config);

    private:
	required_device<cpu_device> m_maincpu;

	void hnayayoi_map(address_map &map);
	void hnayayoi_io_map(address_map &map);
	void hnfubuki_map(address_map &map);
    };

    void hnayayoi_state::hnayayoi(machine_config &config)
    {
        Z80(config, m_maincpu, 20000000/4);
        m_maincpu->set_addrmap(AS_PROGRAM, &hnayayoi_state::hnayayoi_map);
        m_maincpu->set_addrmap(AS_IO, &hnayayoi_state::hnayayoi_io_map);
    }

    void hnayayoi_state::hnfubuki(machine_config &config)
    {
        hnayayoi(config);

        m_maincpu->set_addrmap(AS_PROGRAM, &hnayayoi_state::hnfubuki_map);
        m_maincpu->set_addrmap(AS_IO, address_map_constructor());
    }


4. Accessing the spaces
-----------------------

.. code-block:: C++

    address_space &space(int index = 0) const;

Returns the specified address space post-initialization.  The specified
address space must exist.

.. code-block:: C++

    bool has_space(int index = 0) const;

Indicates whether a given space actually exists.


5. MMU support for disassembler
-------------------------------

.. code-block:: C++

    bool translate(int spacenum, int intention, offs_t &address, address_space *&target_space);

Does a logical to physical address translation through the device's
MMU.  spacenum gives the space number, intention for the type of the
future access (``TR_(READ\|WRITE\|FETCH)``), address is an in/out
parameter holding the address to translate on entry and the translated
version on return, and finally target_space is the actual space the
access would end up in, which may be in a different device.  Should
return ``true`` if the translation went correctly, or ``false`` if the
address is unmapped.  The call must not change the state of the
device.

Note that for some historical reason, the device itself must override
the virtual method ``memory_translate`` with the same signature.
ord.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
/*============================================================================
This source file is an extension to the SoftFloat IEC/IEEE Floating-point
Arithmetic Package, Release 2b, written for Bochs (x86 achitecture simulator)
floating point emulation.

THIS SOFTWARE IS DISTRIBUTED AS IS, FOR FREE.  Although reasonable effort has
been made to avoid it, THIS SOFTWARE MAY CONTAIN FAULTS THAT WILL AT TIMES
RESULT IN INCORRECT BEHAVIOR.  USE OF THIS SOFTWARE IS RESTRICTED TO PERSONS
AND ORGANIZATIONS WHO CAN AND WILL TAKE FULL RESPONSIBILITY FOR ALL LOSSES,
COSTS, OR OTHER PROBLEMS THEY INCUR DUE TO THE SOFTWARE, AND WHO FURTHERMORE
EFFECTIVELY INDEMNIFY JOHN HAUSER AND THE INTERNATIONAL COMPUTER SCIENCE
INSTITUTE (possibly via similar legal warning) AGAINST ALL LOSSES, COSTS, OR
OTHER PROBLEMS INCURRED BY THEIR CUSTOMERS AND CLIENTS DUE TO THE SOFTWARE.

Derivative works are acceptable, even for commercial purposes, so long as
(1) the source code for the derivative work includes prominent notice that
the work is derivative, and (2) the source code includes prominent notice with
these four paragraphs for those parts of this code that are retained.
=============================================================================*/

/*============================================================================
 * Written for Bochs (x86 achitecture simulator) by
 *            Stanislav Shwartsman [sshwarts at sourceforge net]
 * ==========================================================================*/

#define FLOAT128

#define USE_estimateDiv128To64
#include "mamesf.h"
#include "softfloat.h"
//#include "softfloat-specialize"
#include "fpu_constant.h"

static const floatx80 floatx80_one = packFloatx80(0, 0x3fff, 0x8000000000000000U);
static const floatx80 floatx80_default_nan = packFloatx80(0, 0xffff, 0xffffffffffffffffU);

#define packFloat2x128m(zHi, zLo) {(zHi), (zLo)}
#define PACK_FLOAT_128(hi,lo) packFloat2x128m(LIT64(hi),LIT64(lo))

#define EXP_BIAS 0x3FFF

/*----------------------------------------------------------------------------
| Returns the fraction bits of the extended double-precision floating-point
| value `a'.
*----------------------------------------------------------------------------*/

INLINE bits64 extractFloatx80Frac( floatx80 a )
{
	return a.low;

}

/*----------------------------------------------------------------------------
| Returns the exponent bits of the extended double-precision floating-point
| value `a'.
*----------------------------------------------------------------------------*/

INLINE int32 extractFloatx80Exp( floatx80 a )
{
	return a.high & 0x7FFF;

}

/*----------------------------------------------------------------------------
| Returns the sign bit of the extended double-precision floating-point value
| `a'.
*----------------------------------------------------------------------------*/

INLINE flag extractFloatx80Sign( floatx80 a )
{
	return a.high>>15;

}

/*----------------------------------------------------------------------------
| Takes extended double-precision floating-point  NaN  `a' and returns the
| appropriate NaN result. If `a' is a signaling NaN, the invalid exception
| is raised.
*----------------------------------------------------------------------------*/

INLINE floatx80 propagateFloatx80NaNOneArg(floatx80 a)
{
	if (floatx80_is_signaling_nan(a))
		float_raise(float_flag_invalid);

	a.low |= 0xC000000000000000U;

	return a;
}

/*----------------------------------------------------------------------------
| Normalizes the subnormal extended double-precision floating-point value
| represented by the denormalized significand `aSig'.  The normalized exponent
| and significand are stored at the locations pointed to by `zExpPtr' and
| `zSigPtr', respectively.
*----------------------------------------------------------------------------*/

void normalizeFloatx80Subnormal(uint64_t aSig, int32_t *zExpPtr, uint64_t *zSigPtr)
{
	int shiftCount = countLeadingZeros64(aSig);
	*zSigPtr = aSig<<shiftCount;
	*zExpPtr = 1 - shiftCount;
}

/* reduce trigonometric function argument using 128-bit precision
   M_PI approximation */
static uint64_t argument_reduction_kernel(uint64_t aSig0, int Exp, uint64_t *zSig0, uint64_t *zSig1)
{
	uint64_t term0, term1, term2;
	uint64_t aSig1 = 0;

	shortShift128Left(aSig1, aSig0, Exp, &aSig1, &aSig0);
	uint64_t q = estimateDiv128To64(aSig1, aSig0, FLOAT_PI_HI);
	mul128By64To192(FLOAT_PI_HI, FLOAT_PI_LO, q, &term0, &term1, &term2);
	sub128(aSig1, aSig0, term0, term1, zSig1, zSig0);
	while ((int64_t)(*zSig1) < 0) {
		--q;
		add192(*zSig1, *zSig0, term2, 0, FLOAT_PI_HI, FLOAT_PI_LO, zSig1, zSig0, &term2);
	}
	*zSig1 = term2;
	return q;
}

static int reduce_trig_arg(int expDiff, int &zSign, uint64_t &aSig0, uint64_t &aSig1)
{
	uint64_t term0, term1, q = 0;

	if (expDiff < 0) {
		shift128Right(aSig0, 0, 1, &aSig0, &aSig1);
		expDiff = 0;
	}
	if (expDiff > 0) {
		q = argument_reduction_kernel(aSig0, expDiff, &aSig0, &aSig1);
	}
	else {
		if (FLOAT_PI_HI <= aSig0) {
			aSig0 -= FLOAT_PI_HI;
			q = 1;
		}
	}

	shift128Right(FLOAT_PI_HI, FLOAT_PI_LO, 1, &term0, &term1);
	if (! lt128(aSig0, aSig1, term0, term1))
	{
		int lt = lt128(term0, term1, aSig0, aSig1);
		int eq = eq128(aSig0, aSig1, term0, term1);

		if ((eq && (q & 1)) || lt) {
			zSign = !zSign;
			++q;
		}
		if (lt) sub128(FLOAT_PI_HI, FLOAT_PI_LO, aSig0, aSig1, &aSig0, &aSig1);
	}

	return (int)(q & 3);
}

#define SIN_ARR_SIZE 11
#define COS_ARR_SIZE 11

static float128 sin_arr[SIN_ARR_SIZE] =
{
	PACK_FLOAT_128(0x3fff000000000000, 0x0000000000000000), /*  1 */
	PACK_FLOAT_128(0xbffc555555555555, 0x5555555555555555), /*  3 */
	PACK_FLOAT_128(0x3ff8111111111111, 0x1111111111111111), /*  5 */
	PACK_FLOAT_128(0xbff2a01a01a01a01, 0xa01a01a01a01a01a), /*  7 */
	PACK_FLOAT_128(0x3fec71de3a556c73, 0x38faac1c88e50017), /*  9 */
	PACK_FLOAT_128(0xbfe5ae64567f544e, 0x38fe747e4b837dc7), /* 11 */
	PACK_FLOAT_128(0x3fde6124613a86d0, 0x97ca38331d23af68), /* 13 */
	PACK_FLOAT_128(0xbfd6ae7f3e733b81, 0xf11d8656b0ee8cb0), /* 15 */
	PACK_FLOAT_128(0x3fce952c77030ad4, 0xa6b2605197771b00), /* 17 */
	PACK_FLOAT_128(0xbfc62f49b4681415, 0x724ca1ec3b7b9675), /* 19 */
	PACK_FLOAT_128(0x3fbd71b8ef6dcf57, 0x18bef146fcee6e45)  /* 21 */
};

static float128 cos_arr[COS_ARR_SIZE] =
{
	PACK_FLOAT_128(0x3fff000000000000, 0x0000000000000000), /*  0 */
	PACK_FLOAT_128(0xbffe000000000000, 0x0000000000000000), /*  2 */
	PACK_FLOAT_128(0x3ffa555555555555, 0x5555555555555555), /*  4 */
	PACK_FLOAT_128(0xbff56c16c16c16c1, 0x6c16c16c16c16c17), /*  6 */
	PACK_FLOAT_128(0x3fefa01a01a01a01, 0xa01a01a01a01a01a), /*  8 */
	PACK_FLOAT_128(0xbfe927e4fb7789f5, 0xc72ef016d3ea6679), /* 10 */
	PACK_FLOAT_128(0x3fe21eed8eff8d89, 0x7b544da987acfe85), /* 12 */
	PACK_FLOAT_128(0xbfda93974a8c07c9, 0xd20badf145dfa3e5), /* 14 */
	PACK_FLOAT_128(0x3fd2ae7f3e733b81, 0xf11d8656b0ee8cb0), /* 16 */
	PACK_FLOAT_128(0xbfca6827863b97d9, 0x77bb004886a2c2ab), /* 18 */
	PACK_FLOAT_128(0x3fc1e542ba402022, 0x507a9cad2bf8f0bb)  /* 20 */
};

extern float128 OddPoly (float128 x, float128 *arr, unsigned n);

/* 0 <= x <= pi/4 */
INLINE float128 poly_sin(float128 x)
{
	//                 3     5     7     9     11     13     15
	//                x     x     x     x     x      x      x
	// sin (x) ~ x - --- + --- - --- + --- - ---- + ---- - ---- =
	//                3!    5!    7!    9!    11!    13!    15!
	//
	//                 2     4     6     8     10     12     14
	//                x     x     x     x     x      x      x
	//   = x * [ 1 - --- + --- - --- + --- - ---- + ---- - ---- ] =
	//                3!    5!    7!    9!    11!    13!    15!
	//
	//           3                          3
	//          --       4k                --        4k+2
	//   p(x) = >  C  * x   > 0     q(x) = >  C   * x     < 0
	//          --  2k                     --  2k+1
	//          k=0                        k=0
	//
	//                          2
	//   sin(x) ~ x * [ p(x) + x * q(x) ]
	//

	return OddPoly(x, sin_arr, SIN_ARR_SIZE);
}

extern float128 EvenPoly(float128 x, float128 *arr, unsigned n);

/* 0 <= x <= pi/4 */
INLINE float128 poly_cos(float128 x)
{
	//                 2     4     6     8     10     12     14
	//                x     x     x     x     x      x      x
	// cos (x) ~ 1 - --- + --- - --- + --- - ---- + ---- - ----
	//                2!    4!    6!    8!    10!    12!    14!
	//
	//           3                          3
	//          --       4k                --        4k+2
	//   p(x) = >  C  * x   > 0     q(x) = >  C   * x     < 0
	//          --  2k                     --  2k+1
	//          k=0                        k=0
	//
	//                      2
	//   cos(x) ~ [ p(x) + x * q(x) ]
	//

	return EvenPoly(x, cos_arr, COS_ARR_SIZE);
}

INLINE void sincos_invalid(floatx80 *sin_a, floatx80 *cos_a, floatx80 a)
{
	if (sin_a) *sin_a = a;
	if (cos_a) *cos_a = a;
}

INLINE void sincos_tiny_argument(floatx80 *sin_a, floatx80 *cos_a, floatx80 a)
{
	if (sin_a) *sin_a = a;
	if (cos_a) *cos_a = floatx80_one;
}

static floatx80 sincos_approximation(int neg, float128 r, uint64_t quotient)
{
	if (quotient & 0x1) {
		r = poly_cos(r);
		neg = 0;
	} else  {
		r = poly_sin(r);
	}

	floatx80 result = float128_to_floatx80(r);
	if (quotient & 0x2)
		neg = ! neg;

	if (neg)
		result = floatx80_chs(result);

	return result;
}

// =================================================
// SFFSINCOS               Compute sin(x) and cos(x)
// =================================================

//
// Uses the following identities:
// ----------------------------------------------------------
//
//  sin(-x) = -sin(x)
//  cos(-x) =  cos(x)
//
//  sin(x+y) = sin(x)*cos(y)+cos(x)*sin(y)
//  cos(x+y) = sin(x)*sin(y)+cos(x)*cos(y)
//
//  sin(x+ pi/2)  =  cos(x)
//  sin(x+ pi)    = -sin(x)
//  sin(x+3pi/2)  = -cos(x)
//  sin(x+2pi)    =  sin(x)
//

int sf_fsincos(floatx80 a, floatx80 *sin_a, floatx80 *cos_a)
{
	uint64_t aSig0, aSig1 = 0;
	int32_t aExp, zExp, expDiff;
	int aSign, zSign;
	int q = 0;

	aSig0 = extractFloatx80Frac(a);
	aExp = extractFloatx80Exp(a);
	aSign = extractFloatx80Sign(a);

	/* invalid argument */
	if (aExp == 0x7FFF) {
		if ((uint64_t) (aSig0<<1)) {
			sincos_invalid(sin_a, cos_a, propagateFloatx80NaNOneArg(a));
			return 0;
		}

		float_raise(float_flag_invalid);
		sincos_invalid(sin_a, cos_a, floatx80_default_nan);
		return 0;
	}

	if (aExp == 0) {
		if (aSig0 == 0) {
			sincos_tiny_argument(sin_a, cos_a, a);
			return 0;
		}

//        float_raise(float_flag_denormal);

		/* handle pseudo denormals */
		if (! (aSig0 & 0x8000000000000000U))
		{
			float_raise(float_flag_inexact);
			if (sin_a)
				float_raise(float_flag_underflow);
			sincos_tiny_argument(sin_a, cos_a, a);
			return 0;
		}

		normalizeFloatx80Subnormal(aSig0, &aExp, &aSig0);
	}

	zSign = aSign;
	zExp = EXP_BIAS;
	expDiff = aExp - zExp;

	/* argument is out-of-range */
	if (expDiff >= 63)
		return -1;

	float_raise(float_flag_inexact);

	if (expDiff < -1) {    // doesn't require reduction
		if (expDiff <= -68) {
			a = packFloatx80(aSign, aExp, aSig0);
			sincos_tiny_argument(sin_a, cos_a, a);
			return 0;
		}
		zExp = aExp;
	}
	else {
		q = reduce_trig_arg(expDiff, zSign, aSig0, aSig1);
	}

	/* **************************** */
	/* argument reduction completed */
	/* **************************** */

	/* using float128 for approximation */
	float128 r = normalizeRoundAndPackFloat128(0, zExp-0x10, aSig0, aSig1);

	if (aSign) q = -q;
	if (sin_a) *sin_a = sincos_approximation(zSign, r,   q);
	if (cos_a) *cos_a = sincos_approximation(zSign, r, q+1);

	return 0;
}

int floatx80_fsin(floatx80 &a)
{
	return sf_fsincos(a, &a, 0);
}

int floatx80_fcos(floatx80 &a)
{
	return sf_fsincos(a, 0, &a);
}

// =================================================
// FPTAN                 Compute tan(x)
// =================================================

//
// Uses the following identities:
//
// 1. ----------------------------------------------------------
//
//  sin(-x) = -sin(x)
//  cos(-x) =  cos(x)
//
//  sin(x+y) = sin(x)*cos(y)+cos(x)*sin(y)
//  cos(x+y) = sin(x)*sin(y)+cos(x)*cos(y)
//
//  sin(x+ pi/2)  =  cos(x)
//  sin(x+ pi)    = -sin(x)
//  sin(x+3pi/2)  = -cos(x)
//  sin(x+2pi)    =  sin(x)
//
// 2. ----------------------------------------------------------
//
//           sin(x)
//  tan(x) = ------
//           cos(x)
//

int floatx80_ftan(floatx80 &a)
{
	uint64_t aSig0, aSig1 = 0;
	int32_t aExp, zExp, expDiff;
	int aSign, zSign;
	int q = 0;

	aSig0 = extractFloatx80Frac(a);
	aExp = extractFloatx80Exp(a);
	aSign = extractFloatx80Sign(a);

	/* invalid argument */
	if (aExp == 0x7FFF) {
		if ((uint64_t) (aSig0<<1))
		{
			a = propagateFloatx80NaNOneArg(a);
			return 0;
		}

		float_raise(float_flag_invalid);
		a = floatx80_default_nan;
		return 0;
	}

	if (aExp == 0) {
		if (aSig0 == 0) return 0;
//        float_raise(float_flag_denormal);
		/* handle pseudo denormals */
		if (! (aSig0 & 0x8000000000000000U))
		{
			float_raise(float_flag_inexact | float_flag_underflow);
			return 0;
		}
		normalizeFloatx80Subnormal(aSig0, &aExp, &aSig0);
	}

	zSign = aSign;
	zExp = EXP_BIAS;
	expDiff = aExp - zExp;

	/* argument is out-of-range */
	if (expDiff >= 63)
		return -1;

	float_raise(float_flag_inexact);

	if (expDiff < -1) {    // doesn't require reduction
		if (expDiff <= -68) {
			a = packFloatx80(aSign, aExp, aSig0);
			return 0;
		}
		zExp = aExp;
	}
	else {
		q = reduce_trig_arg(expDiff, zSign, aSig0, aSig1);
	}

	/* **************************** */
	/* argument reduction completed */
	/* **************************** */

	/* using float128 for approximation */
	float128 r = normalizeRoundAndPackFloat128(0, zExp-0x10, aSig0, aSig1);

	float128 sin_r = poly_sin(r);
	float128 cos_r = poly_cos(r);

	if (q & 0x1) {
		r = float128_div(cos_r, sin_r);
		zSign = ! zSign;
	} else {
		r = float128_div(sin_r, cos_r);
	}

	a = float128_to_floatx80(r);
	if (zSign)
		a = floatx80_chs(a);

	return 0;
}

//                            2         3         4               n
// f(x) ~ C + (C * x) + (C * x) + (C * x) + (C * x) + ... + (C * x)
//         0    1         2         3         4               n
//
//          --       2k                --        2k+1
//   p(x) = >  C  * x           q(x) = >  C   * x
//          --  2k                     --  2k+1
//
//   f(x) ~ [ p(x) + x * q(x) ]
//

float128 EvalPoly(float128 x, float128 *arr, unsigned n)
{
	float128 x2 = float128_mul(x, x);
	unsigned i;

	assert(n > 1);

	float128 r1 = arr[--n];
	i = n;
	while(i >= 2) {
		r1 = float128_mul(r1, x2);
		i -= 2;
		r1 = float128_add(r1, arr[i]);
	}
	if (i) r1 = float128_mul(r1, x);

	float128 r2 = arr[--n];
	i = n;
	while(i >= 2) {
		r2 = float128_mul(r2, x2);
		i -= 2;
		r2 = float128_add(r2, arr[i]);
	}
	if (i) r2 = float128_mul(r2, x);

	return float128_add(r1, r2);
}

//                  2         4         6         8               2n
// f(x) ~ C + (C * x) + (C * x) + (C * x) + (C * x) + ... + (C * x)
//         0    1         2         3         4               n
//
//          --       4k                --        4k+2
//   p(x) = >  C  * x           q(x) = >  C   * x
//          --  2k                     --  2k+1
//
//                    2
//   f(x) ~ [ p(x) + x * q(x) ]
//

float128 EvenPoly(float128 x, float128 *arr, unsigned n)
{
		return EvalPoly(float128_mul(x, x), arr, n);
}

//                        3         5         7         9               2n+1
// f(x) ~ (C * x) + (C * x) + (C * x) + (C * x) + (C * x) + ... + (C * x)
//          0         1         2         3         4               n
//                        2         4         6         8               2n
//      = x * [ C + (C * x) + (C * x) + (C * x) + (C * x) + ... + (C * x)
//               0    1         2         3         4               n
//
//          --       4k                --        4k+2
//   p(x) = >  C  * x           q(x) = >  C   * x
//          --  2k                     --  2k+1
//
//                        2
//   f(x) ~ x * [ p(x) + x * q(x) ]
//

float128 OddPoly(float128 x, float128 *arr, unsigned n)
{
		return float128_mul(x, EvenPoly(x, arr, n));
}

/*----------------------------------------------------------------------------
| Scales extended double-precision floating-point value in operand `a' by
| value `b'. The function truncates the value in the second operand 'b' to
| an integral value and adds that value to the exponent of the operand 'a'.
| The operation performed according to the IEC/IEEE Standard for Binary
| Floating-Point Arithmetic.
*----------------------------------------------------------------------------*/

extern floatx80 propagateFloatx80NaN( floatx80 a, floatx80 b );

floatx80 floatx80_scale(floatx80 a, floatx80 b)
{
	sbits32 aExp, bExp;
	bits64 aSig, bSig;

	// handle unsupported extended double-precision floating encodings
/*    if (floatx80_is_unsupported(a) || floatx80_is_unsupported(b))
    {
        float_raise(float_flag_invalid);
        return floatx80_default_nan;
    }*/

	aSig = extractFloatx80Frac(a);
	aExp = extractFloatx80Exp(a);
	int aSign = extractFloatx80Sign(a);
	bSig = extractFloatx80Frac(b);
	bExp = extractFloatx80Exp(b);
	int bSign = extractFloatx80Sign(b);

	if (aExp == 0x7FFF) {
		if ((bits64) (aSig<<1) || ((bExp == 0x7FFF) && (bits64) (bSig<<1)))
		{
			return propagateFloatx80NaN(a, b);
		}
		if ((bExp == 0x7FFF) && bSign) {
			float_raise(float_flag_invalid);
			return floatx80_default_nan;
		}
		if (bSig && (bExp == 0)) float_raise(float_flag_denormal);
		return a;
	}
	if (bExp == 0x7FFF) {
		if ((bits64) (bSig<<1)) return propagateFloatx80NaN(a, b);
		if ((aExp | aSig) == 0) {
			if (! bSign) {
				float_raise(float_flag_invalid);
				return floatx80_default_nan;
			}
			return a;
		}
		if (aSig && (aExp == 0)) float_raise(float_flag_denormal);
		if (bSign) return packFloatx80(aSign, 0, 0);
		return packFloatx80(aSign, 0x7FFF, 0x8000000000000000U);
	}
	if (aExp == 0) {
		if (aSig == 0) return a;
		float_raise(float_flag_denormal);
		normalizeFloatx80Subnormal(aSig, &aExp, &aSig);
	}
	if (bExp == 0) {
		if (bSig == 0) return a;
		float_raise(float_flag_denormal);
		normalizeFloatx80Subnormal(bSig, &bExp, &bSig);
	}

	if (bExp > 0x400E) {
		/* generate appropriate overflow/underflow */
		return roundAndPackFloatx80(80, aSign,
							bSign ? -0x3FFF : 0x7FFF, aSig, 0);
	}
	if (bExp < 0x3FFF) return a;

	int shiftCount = 0x403E - bExp;
	bSig >>= shiftCount;
	sbits32 scale = bSig;
	if (bSign) scale = -scale; /* -32768..32767 */
	return
		roundAndPackFloatx80(80, aSign, aExp+scale, aSig, 0);
}