macemu/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-operations.hpp
Jonas Maebe 75b333f805 Add FP frsqrte instruction emulation
Also renamed the frsqrt opcode to frsqrte to match the manuals (the vector
version is also an estimate)
2019-12-23 18:19:49 +01:00

406 lines
10 KiB
C++

/*
* ppc-operations.cpp - PowerPC specific operations
*
* Kheperix (C) 2003-2005 Gwenole Beauchesne
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef PPC_OPERATIONS_H
#define PPC_OPERATIONS_H
#include <math.h>
#include "mathlib/mathlib.hpp"
/**
* Define an unary/binary/trinary operation
*
* NAME Name of the operation
* TYPE Type of operands and result
* EXPR C++ expression defining the operation, parameters are x/y/z/t
**/
#define DEFINE_ALIAS_OP(NAME, T_NAME, TYPE) \
typedef op_template_##T_NAME<TYPE> op_##NAME
#define DEFINE_OP1(NAME, TYPE, EXPR) \
struct op_##NAME { \
static inline TYPE apply(TYPE x) { \
return EXPR; \
} \
}
#define DEFINE_TEMPLATE_OP1(NAME, EXPR) \
template< class TYPE > \
DEFINE_OP1(template_##NAME, TYPE, EXPR)
#define DEFINE_OP2(NAME, TYPE, EXPR) \
struct op_##NAME { \
static inline TYPE apply(TYPE x, TYPE y) { \
return EXPR; \
} \
}
#define DEFINE_TEMPLATE_OP2(NAME, EXPR) \
template< class TYPE > \
DEFINE_OP2(template_##NAME, TYPE, EXPR)
#define DEFINE_OP3(NAME, TYPE, EXPR) \
struct op_##NAME { \
static inline TYPE apply(TYPE x, TYPE y, TYPE z) { \
return EXPR; \
} \
}
#define DEFINE_OP4(NAME, TYPE, EXPR) \
struct op_##NAME { \
static inline TYPE apply(TYPE x, TYPE y, TYPE z, TYPE t) { \
return EXPR; \
} \
}
// Basic operations
DEFINE_TEMPLATE_OP1(nop, x);
DEFINE_TEMPLATE_OP2(add, x + y);
DEFINE_TEMPLATE_OP2(sub, x - y);
DEFINE_TEMPLATE_OP2(mul, x * y);
DEFINE_TEMPLATE_OP2(div, x / y);
DEFINE_TEMPLATE_OP2(and, x & y);
DEFINE_TEMPLATE_OP2(or, x | y);
DEFINE_TEMPLATE_OP2(xor, x ^ y);
DEFINE_TEMPLATE_OP2(orc, x | ~y);
DEFINE_TEMPLATE_OP2(andc,x & ~y);
DEFINE_TEMPLATE_OP2(nand,~(x & y));
DEFINE_TEMPLATE_OP2(nor, ~(x | y));
DEFINE_TEMPLATE_OP2(eqv, ~(x ^ y));
// Integer basic operations
DEFINE_ALIAS_OP(nop, nop, uint32);
DEFINE_ALIAS_OP(add, add, uint32);
DEFINE_ALIAS_OP(sub, sub, uint32);
DEFINE_ALIAS_OP(mul, mul, uint32);
DEFINE_ALIAS_OP(smul,mul, int32);
DEFINE_ALIAS_OP(div, div, uint32);
DEFINE_ALIAS_OP(sdiv,div, int32);
DEFINE_OP1(neg, uint32, -x);
DEFINE_OP1(compl, uint32, ~x);
DEFINE_OP2(mod, uint32, x % y);
DEFINE_ALIAS_OP(and, and, uint32);
DEFINE_ALIAS_OP(or, or, uint32);
DEFINE_ALIAS_OP(xor, xor, uint32);
DEFINE_ALIAS_OP(orc, orc, uint32);
DEFINE_ALIAS_OP(andc,andc,uint32);
DEFINE_ALIAS_OP(nand,nand,uint32);
DEFINE_ALIAS_OP(nor, nor, uint32);
DEFINE_ALIAS_OP(eqv, eqv, uint32);
DEFINE_OP2(shll, uint32, x << y);
DEFINE_OP2(shrl, uint32, x >> y);
DEFINE_OP2(shra, uint32, (int32)x >> y);
DEFINE_OP2(rotl, uint32, ((x << y) | (x >> (32 - y))));
DEFINE_OP2(rotr, uint32, ((x >> y) | (x << (32 - y))));
DEFINE_OP4(ppc_rlwimi, uint32, (op_rotl::apply(x, y) & z) | (t & ~z));
DEFINE_OP3(ppc_rlwinm, uint32, (op_rotl::apply(x, y) & z));
DEFINE_OP3(ppc_rlwnm, uint32, (op_rotl::apply(x, (y & 0x1f)) & z));
DEFINE_ALIAS_OP(add_64, add, uint64);
DEFINE_ALIAS_OP(sub_64, sub, uint64);
DEFINE_ALIAS_OP(smul_64,mul, int64);
DEFINE_ALIAS_OP(and_64, and, uint64);
DEFINE_ALIAS_OP(andc_64,andc,uint64);
DEFINE_ALIAS_OP(or_64, or, uint64);
DEFINE_ALIAS_OP(nor_64, nor, uint64);
DEFINE_ALIAS_OP(xor_64, xor, uint64);
// Floating-point basic operations
DEFINE_OP1(fnop, double, x);
DEFINE_OP1(fabs, double, fabs(x));
DEFINE_OP2(fadd, double, x + y);
DEFINE_OP2(fdiv, double, x / y);
DEFINE_OP3(fmadd, double, mathlib_fmadd(x, y, z));
DEFINE_OP3(fmsub, double, mathlib_fmsub(x, y, z));
DEFINE_OP2(fmul, double, x * y);
DEFINE_OP1(fnabs, double, -fabs(x));
DEFINE_OP1(fneg, double, -x);
DEFINE_OP3(fnmadd, double, -mathlib_fmadd(x, y, z));
DEFINE_OP3(fnmsub, double, -mathlib_fmsub(x, y, z));
DEFINE_OP3(fnmadds, double, -(float)mathlib_fmadd(x, y, z));
DEFINE_OP3(fnmsubs, double, -(float)mathlib_fmsub(x, y, z));
DEFINE_OP2(fsub, double, x - y);
DEFINE_OP3(fsel, double, (x >= 0.0) ? y : z);
DEFINE_OP1(frim, double, floor(x));
DEFINE_OP1(frin, double, round(x));
DEFINE_OP1(frip, double, ceil(x));
DEFINE_OP1(friz, double, trunc(x));
DEFINE_OP2(fadds, float, x + y);
DEFINE_OP2(fsubs, float, x - y);
DEFINE_OP1(exp2, float, exp2f(x));
DEFINE_OP1(log2, float, log2f(x));
DEFINE_OP1(fres, float, 1 / x);
DEFINE_OP1(frsqrte, float, 1 / sqrt(x));
DEFINE_OP1(frsim, float, floorf(x));
DEFINE_OP1(frsin, float, roundf(x));
DEFINE_OP1(frsip, float, ceilf(x));
DEFINE_OP1(frsiz, float, truncf(x));
// Misc operations used in AltiVec instructions
template< class TYPE >
struct op_vrl {
static inline TYPE apply(TYPE v, TYPE n) {
const int sh = n & ((8 * sizeof(TYPE)) - 1);
return ((v << sh) | (v >> ((8 * sizeof(TYPE)) - sh)));
}
};
template< class TYPE >
struct op_vsl {
static inline TYPE apply(TYPE v, TYPE n) {
const int sh = n & ((8 * sizeof(TYPE)) - 1);
return v << sh;
}
};
template< class TYPE >
struct op_vsr {
static inline TYPE apply(TYPE v, TYPE n) {
const int sh = n & ((8 * sizeof(TYPE)) - 1);
return v >> sh;
}
};
template< uint16 round = 0 >
struct op_mhraddsh {
static inline int32 apply(int32 a, int32 b, int32 c) {
return (((a * b) + round) >> 15) + c;
}
};
struct op_cvt_fp2si {
static inline int64 apply(uint32 a, float b) {
// Delegate saturation to upper level
if (mathlib_isinf(b))
return ((int64)(b < 0 ? 0x80000000 : 0x7fffffff)) << 32;
if (mathlib_isnan(b))
return 0;
return (int64)(b * (1U << a));
}
};
template< class TYPE >
struct op_cvt_si2fp {
static inline float apply(uint32 a, TYPE b) {
return ((float)b) / ((float)(1U << a));
}
};
template< class TYPE >
struct op_max {
static inline TYPE apply(TYPE a, TYPE b) {
return (a > b) ? a : b;
}
};
template<>
struct op_max<float> {
static inline float apply(float a, float b) {
// XXX The maximum of any value and a NaN is a QNaN
if (mathlib_isnan(a))
return a;
if (mathlib_isnan(b))
return b;
return a > b ? a : b;
}
};
template< class TYPE >
struct op_min {
static inline TYPE apply(TYPE a, TYPE b) {
return (a < b) ? a : b;
}
};
template<>
struct op_min<float> {
static inline float apply(float a, float b) {
// XXX The minimum of any value and a NaN is a QNaN
if (mathlib_isnan(a))
return a;
if (mathlib_isnan(b))
return b;
return a < b ? a : b;
}
};
template< int nbytes >
struct op_all_ones {
static const uint32 value = (1U << (8 * nbytes)) - 1;
};
template<>
struct op_all_ones<4> {
static const uint32 value = 0xffffffff;
};
template< class VX >
struct op_cmp {
static const uint32 result = op_all_ones<sizeof(VX)>::value;
};
template< class VX >
struct op_cmp_eq {
static inline uint32 apply(VX a, VX b) {
return a == b ? op_cmp<VX>::result : 0;
}
};
template< class VX >
struct op_cmp_ge {
static inline uint32 apply(VX a, VX b) {
return a >= b ? op_cmp<VX>::result : 0;
}
};
template< class VX >
struct op_cmp_gt {
static inline uint32 apply(VX a, VX b) {
return a > b ? op_cmp<VX>::result : 0;
}
};
struct op_cmpbfp {
static inline uint32 apply(float a, float b) {
const bool le = a <= b;
const bool ge = a >= -b;
return (le ? 0 : (1 << 31)) | (ge ? 0 : (1 << 30));
}
};
DEFINE_OP3(vsel, uint32, ((y & z) | (x & ~z)));
DEFINE_OP3(vmaddfp, float, ((x * z) + y));
DEFINE_OP3(vnmsubfp, float, -((x * z) - y));
DEFINE_OP3(mladduh, uint32, ((x * y) + z) & 0xffff);
DEFINE_OP2(addcuw, uint32, ((uint64)x + (uint64)y) >> 32);
DEFINE_OP2(subcuw, uint32, (~((int64)x - (int64)y) >> 32) & 1);
DEFINE_OP2(avgsb, int8, (((int16)x + (int16)y + 1) >> 1));
DEFINE_OP2(avgsh, int16, (((int32)x + (int32)y + 1) >> 1));
DEFINE_OP2(avgsw, int32, (((int64)x + (int64)y + 1) >> 1));
DEFINE_OP2(avgub, uint8, ((uint16)x + (uint16)y + 1) >> 1);
DEFINE_OP2(avguh, uint16, ((uint32)x + (uint32)y + 1) >> 1);
DEFINE_OP2(avguw, uint32, ((uint64)x + (uint64)y + 1) >> 1);
#undef DEFINE_OP1
#undef DEFINE_OP2
#undef DEFINE_OP3
#undef DEFINE_OP4
#undef DEFINE_TEMPLATE_OP1
#undef DEFINE_TEMPLATE_OP2
#undef DEFINE_TEMPLATE_OP3
#undef DEFINE_ALIAS_OP
// Sign/Zero-extend operation
struct op_sign_extend_5_32 {
static inline uint32 apply(uint32 value) {
if (value & 0x10)
value -= 0x20;
return value;
}
};
struct op_sign_extend_16_32 {
static inline uint32 apply(uint32 value) {
return (uint32)(int32)(int16)value;
}
};
struct op_sign_extend_8_32 {
static inline uint32 apply(uint32 value) {
return (uint32)(int32)(int8)value;
}
};
struct op_zero_extend_16_32 {
static inline uint32 apply(uint32 value) {
return (uint32)(uint16)value;
}
};
struct op_zero_extend_8_32 {
static inline uint32 apply(uint32 value) {
return (uint32)(uint8)value;
}
};
struct op_sign_extend_16_32_shifted {
static inline uint32 apply(uint32 value) {
return op_sign_extend_16_32::apply(value) << 16;
}
};
struct op_zero_extend_16_32_shifted {
static inline uint32 apply(uint32 value) {
return op_zero_extend_16_32::apply(value) << 16;
}
};
struct op_sign_extend_BD_32 {
static inline uint32 apply(uint32 value) {
return op_sign_extend_16_32::apply(value << 2);
}
};
struct op_sign_extend_LI_32 {
static inline uint32 apply(uint32 value) {
if (value & 0x800000)
value |= 0xff000000;
return value << 2;
}
};
// And Word with Immediate value
template< uint32 value >
struct op_andi {
static inline uint32 apply(uint32 x) {
return x & value;
}
};
// Count Leading Zero Word
struct op_cntlzw {
static inline uint32 apply(uint32 x) {
uint32 n;
uint32 m = 0x80000000;
for (n = 0; n < 32; n++, m >>= 1)
if (x & m)
break;
return n;
}
};
#endif /* PPC_OPERATIONS_H */