/**
 ******************************************************************************
 * Xenia : Xbox 360 Emulator Research Project                                 *
 ******************************************************************************
 * Copyright 2013 Ben Vanik. All rights reserved.                             *
 * Released under the BSD license - see LICENSE in the root for more details. *
 ******************************************************************************
 */

#ifndef XENIA_CPU_HIR_OPCODES_H_
#define XENIA_CPU_HIR_OPCODES_H_

#include <cstdint>

namespace xe {
namespace cpu {
namespace hir {

enum CallFlags {
  CALL_TAIL = (1 << 1),
  CALL_POSSIBLE_RETURN = (1 << 2),
};

enum BranchFlags {
  BRANCH_LIKELY = (1 << 1),
  BRANCH_UNLIKELY = (1 << 2),
};

enum RoundMode {
  // to zero/nearest/etc
  ROUND_TO_ZERO = 0,
  ROUND_TO_NEAREST,
  ROUND_TO_MINUS_INFINITY,
  ROUND_TO_POSITIVE_INFINITY,
  ROUND_DYNAMIC,  // Round based on the host's rounding mode.
};

enum LoadStoreFlags {
  LOAD_STORE_BYTE_SWAP = 1 << 0,
};

enum CacheControlType {
  CACHE_CONTROL_TYPE_DATA_TOUCH,
  CACHE_CONTROL_TYPE_DATA_TOUCH_FOR_STORE,
  CACHE_CONTROL_TYPE_DATA_STORE,
  CACHE_CONTROL_TYPE_DATA_STORE_AND_FLUSH,
};

enum ArithmeticFlags {
  ARITHMETIC_UNSIGNED = (1 << 2),
  ARITHMETIC_SATURATE = (1 << 3),
};

constexpr uint32_t MakePermuteMask(uint32_t sel_x, uint32_t x, uint32_t sel_y,
                                   uint32_t y, uint32_t sel_z, uint32_t z,
                                   uint32_t sel_w, uint32_t w) {
  return ((x & 0x3) << 0) | (sel_x << 2) | ((y & 0x3) << 8) | (sel_y << 10) |
         ((z & 0x3) << 16) | (sel_z << 18) | ((w & 0x3) << 24) | (sel_w << 26);
}

enum PermuteMasks : uint32_t {
  kIdentityPermuteMask = MakePermuteMask(0, 0, 0, 1, 0, 2, 0, 3),
};

constexpr uint32_t MakeSwizzleMask(uint32_t x, uint32_t y, uint32_t z,
                                   uint32_t w) {
  return ((x & 0x3) << 0) | ((y & 0x3) << 2) | ((z & 0x3) << 4) |
         ((w & 0x3) << 6);
}

enum Swizzles {
  SWIZZLE_XYZW_TO_XYZW = MakeSwizzleMask(0, 1, 2, 3),
  SWIZZLE_XYZW_TO_YZWX = MakeSwizzleMask(1, 2, 3, 0),
  SWIZZLE_XYZW_TO_ZWXY = MakeSwizzleMask(2, 3, 0, 1),
  SWIZZLE_XYZW_TO_WXYZ = MakeSwizzleMask(3, 0, 1, 2),
};

enum PackType : uint16_t {
  // Special types:
  PACK_TYPE_D3DCOLOR = 0,
  PACK_TYPE_FLOAT16_2 = 1,
  PACK_TYPE_SHORT_4 = 2,
  PACK_TYPE_FLOAT16_4 = 3,
  PACK_TYPE_SHORT_2 = 4,
  PACK_TYPE_UINT_2101010 = 5,
  PACK_TYPE_ULONG_4202020 = 6,

  // Types which use the bitmasks below for configuration:
  PACK_TYPE_8_IN_16 = 7,
  PACK_TYPE_16_IN_32 = 8,

  PACK_TYPE_MODE = 0x000F,  // just to get the mode
                            // Unpack to low or high parts.
  PACK_TYPE_TO_LO = 0 << 12,
  PACK_TYPE_TO_HI = 1 << 12,

  // Input/output arithmetic flags:
  PACK_TYPE_IN_SIGNED = 0 << 13,
  PACK_TYPE_IN_UNSIGNED = 1 << 13,
  PACK_TYPE_OUT_SIGNED = 0 << 14,
  PACK_TYPE_OUT_UNSIGNED = 1 << 14,
  PACK_TYPE_OUT_UNSATURATE = 0 << 15,
  PACK_TYPE_OUT_SATURATE = 1 << 15,
};

inline bool IsPackToHi(uint32_t flags) {
  return (flags & PACK_TYPE_TO_HI) == PACK_TYPE_TO_HI;
}
inline bool IsPackToLo(uint32_t flags) { return !IsPackToHi(flags); }
inline bool IsPackInUnsigned(uint32_t flags) {
  return (flags & PACK_TYPE_IN_UNSIGNED) == PACK_TYPE_IN_UNSIGNED;
}
inline bool IsPackOutUnsigned(uint32_t flags) {
  return (flags & PACK_TYPE_OUT_UNSIGNED) == PACK_TYPE_OUT_UNSIGNED;
}
inline bool IsPackOutSaturate(uint32_t flags) {
  return (flags & PACK_TYPE_OUT_SATURATE) == PACK_TYPE_OUT_SATURATE;
}

enum Opcode {
  OPCODE_COMMENT,
  OPCODE_NOP,
  OPCODE_SOURCE_OFFSET,
  OPCODE_DEBUG_BREAK,
  OPCODE_DEBUG_BREAK_TRUE,
  OPCODE_TRAP,
  OPCODE_TRAP_TRUE,
  OPCODE_CALL,
  OPCODE_CALL_TRUE,
  OPCODE_CALL_INDIRECT,
  OPCODE_CALL_INDIRECT_TRUE,
  OPCODE_CALL_EXTERN,
  OPCODE_RETURN,
  OPCODE_RETURN_TRUE,
  OPCODE_SET_RETURN_ADDRESS,
  OPCODE_BRANCH,
  OPCODE_BRANCH_TRUE,
  OPCODE_BRANCH_FALSE,
  OPCODE_ASSIGN,
  OPCODE_CAST,
  OPCODE_ZERO_EXTEND,
  OPCODE_SIGN_EXTEND,
  OPCODE_TRUNCATE,
  OPCODE_CONVERT,
  OPCODE_ROUND,
  // Note that 2147483648.0 + (src & 0x7FFFFFFF) is not a correct way of
  // performing the uint -> float conversion for large numbers on backends where
  // only sint -> float is available.
  //
  // Take 0b11000000000000000000000101000001 as an example,
  // or 1.1000000000000000000000101000001 * 2^31.
  // This one has 31 mantissa bits (excluding the implicit 1.), and needs to be
  // rounded to 23 bits - 8 mantissa bits need to be dropped:
  // 10000000000000000000001_01000001
  //
  // Rounding to the nearest even (the only rounding mode that exists on
  // AltiVec, and the likely rounding mode in the implementations) should be
  // done downwards - 01000001 of 1_01000001 is in [00000000, 01111111].
  // The correct mantissa in this case is:
  // 1.10000000000000000000001 * 2^31.
  //
  // With a two-step conversion, rounding is done twice instead, which gives an
  // incorrect result.
  //
  // First, converting the low 31 bits to float:
  // The number is 0.1000000000000000000000101000001 * 2^31.
  // Normalizing it, we get 1.000000000000000000000101000001 (30 significand
  // bits).
  // We need to round 30 bits to 23 - 7 bits need to be dropped:
  // 00000000000000000000010_1000001
  //
  // Rounding to the nearest even is done upwards in this case - 1000001 of
  // 0_1000001 is in [1000001, 1111111].
  // The result of the sint -> float conversion is:
  // 1.00000000000000000000011 * 2^30.
  //
  // Now 2147483648.0 (1 * 2^31) needs to be added. Aligning the exponents, we
  // get:
  //   0.|10000000000000000000001|1 * 2^31
  // + 1.|00000000000000000000000|  * 2^31
  // = 1.|10000000000000000000001|1 * 2^31
  //
  // At "infinite precision", the result has 24 significand bits, but only 23
  // can be stored, thus rounding to the nearest even needs to be done. 1_1 is
  // (odd + 0.5). 0.5 is ambiguous, thus tie-breaking to the nearest even -
  // which is above in this case - is done. The result is:
  // 1.10000000000000000000010 * 2^31.
  //
  // This is incorrect - larger than the correctly rounded result, which is:
  // 1.10000000000000000000001 * 2^31.
  //
  // Test cases checked on real hardware via vcfux: 0xFFFDFF7E, 0xFFFCFF7D -
  // should be 0x4F7FFDFF and 0x4F7FFCFF respectively, not 0x4F7FFE00 and
  // 0x4F7FFD00.
  OPCODE_VECTOR_CONVERT_I2F,
  OPCODE_VECTOR_CONVERT_F2I,
  OPCODE_LOAD_VECTOR_SHL,
  OPCODE_LOAD_VECTOR_SHR,
  OPCODE_LOAD_CLOCK,
  OPCODE_LOAD_LOCAL,
  OPCODE_STORE_LOCAL,
  OPCODE_LOAD_CONTEXT,
  OPCODE_STORE_CONTEXT,
  OPCODE_CONTEXT_BARRIER,
  OPCODE_LOAD_MMIO,
  OPCODE_STORE_MMIO,
  OPCODE_LOAD_OFFSET,
  OPCODE_STORE_OFFSET,
  OPCODE_LOAD,
  OPCODE_STORE,
  OPCODE_MEMSET,
  OPCODE_CACHE_CONTROL,
  OPCODE_MEMORY_BARRIER,
  OPCODE_MAX,
  OPCODE_VECTOR_MAX,
  OPCODE_MIN,
  OPCODE_VECTOR_MIN,
  OPCODE_SELECT,
  OPCODE_IS_TRUE,
  OPCODE_IS_FALSE,
  OPCODE_IS_NAN,
  OPCODE_COMPARE_EQ,
  OPCODE_COMPARE_NE,
  OPCODE_COMPARE_SLT,
  OPCODE_COMPARE_SLE,
  OPCODE_COMPARE_SGT,
  OPCODE_COMPARE_SGE,
  OPCODE_COMPARE_ULT,
  OPCODE_COMPARE_ULE,
  OPCODE_COMPARE_UGT,
  OPCODE_COMPARE_UGE,
  OPCODE_DID_SATURATE,
  OPCODE_VECTOR_COMPARE_EQ,
  OPCODE_VECTOR_COMPARE_SGT,
  OPCODE_VECTOR_COMPARE_SGE,
  OPCODE_VECTOR_COMPARE_UGT,
  OPCODE_VECTOR_COMPARE_UGE,
  OPCODE_ADD,
  OPCODE_ADD_CARRY,
  OPCODE_VECTOR_ADD,
  OPCODE_SUB,
  OPCODE_VECTOR_SUB,
  OPCODE_MUL,
  OPCODE_MUL_HI,  // TODO(benvanik): remove this and add INT128 type.
  OPCODE_DIV,
  OPCODE_MUL_ADD,
  OPCODE_MUL_SUB,
  OPCODE_NEG,
  OPCODE_ABS,
  OPCODE_SQRT,
  OPCODE_RSQRT,
  OPCODE_RECIP,
  OPCODE_POW2,
  OPCODE_LOG2,
  OPCODE_DOT_PRODUCT_3,
  OPCODE_DOT_PRODUCT_4,
  OPCODE_AND,
  OPCODE_OR,
  OPCODE_XOR,
  OPCODE_NOT,
  OPCODE_SHL,
  OPCODE_VECTOR_SHL,
  OPCODE_SHR,
  OPCODE_VECTOR_SHR,
  OPCODE_SHA,
  OPCODE_VECTOR_SHA,
  OPCODE_ROTATE_LEFT,
  OPCODE_VECTOR_ROTATE_LEFT,
  OPCODE_VECTOR_AVERAGE,
  OPCODE_BYTE_SWAP,
  OPCODE_CNTLZ,
  OPCODE_INSERT,
  OPCODE_EXTRACT,
  OPCODE_SPLAT,
  OPCODE_PERMUTE,
  OPCODE_SWIZZLE,
  OPCODE_PACK,
  OPCODE_UNPACK,
  OPCODE_ATOMIC_EXCHANGE,
  OPCODE_ATOMIC_COMPARE_EXCHANGE,
  OPCODE_SET_ROUNDING_MODE,
  __OPCODE_MAX_VALUE,  // Keep at end.
};

enum OpcodeFlags {
  OPCODE_FLAG_BRANCH = (1 << 1),
  OPCODE_FLAG_MEMORY = (1 << 2),
  OPCODE_FLAG_COMMUNATIVE = (1 << 3),
  OPCODE_FLAG_VOLATILE = (1 << 4),
  OPCODE_FLAG_IGNORE = (1 << 5),
  OPCODE_FLAG_HIDE = (1 << 6),
  OPCODE_FLAG_PAIRED_PREV = (1 << 7),
};

enum OpcodeSignatureType {
  // 3 bits max (0-7)
  OPCODE_SIG_TYPE_X = 0,
  OPCODE_SIG_TYPE_L = 1,
  OPCODE_SIG_TYPE_O = 2,
  OPCODE_SIG_TYPE_S = 3,
  OPCODE_SIG_TYPE_V = 4,
};

enum OpcodeSignature {
  OPCODE_SIG_X = (OPCODE_SIG_TYPE_X),
  OPCODE_SIG_X_L = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_L << 3),
  OPCODE_SIG_X_O = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_O << 3),
  OPCODE_SIG_X_O_V =
      (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_O << 3) | (OPCODE_SIG_TYPE_V << 6),
  OPCODE_SIG_X_O_V_V = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_O << 3) |
                       (OPCODE_SIG_TYPE_V << 6) | (OPCODE_SIG_TYPE_V << 9),
  OPCODE_SIG_X_O_O_V = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_O << 3) |
                       (OPCODE_SIG_TYPE_O << 6) | (OPCODE_SIG_TYPE_V << 9),
  OPCODE_SIG_X_S = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_S << 3),
  OPCODE_SIG_X_V = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_V << 3),
  OPCODE_SIG_X_V_L =
      (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_V << 3) | (OPCODE_SIG_TYPE_L << 6),
  OPCODE_SIG_X_V_L_L = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_V << 3) |
                       (OPCODE_SIG_TYPE_L << 6) | (OPCODE_SIG_TYPE_L << 9),
  OPCODE_SIG_X_V_O =
      (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_V << 3) | (OPCODE_SIG_TYPE_O << 6),
  OPCODE_SIG_X_V_S =
      (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_V << 3) | (OPCODE_SIG_TYPE_S << 6),
  OPCODE_SIG_X_V_V =
      (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_V << 3) | (OPCODE_SIG_TYPE_V << 6),
  OPCODE_SIG_X_V_V_V = (OPCODE_SIG_TYPE_X) | (OPCODE_SIG_TYPE_V << 3) |
                       (OPCODE_SIG_TYPE_V << 6) | (OPCODE_SIG_TYPE_V << 9),
  OPCODE_SIG_V = (OPCODE_SIG_TYPE_V),
  OPCODE_SIG_V_O = (OPCODE_SIG_TYPE_V) | (OPCODE_SIG_TYPE_O << 3),
  OPCODE_SIG_V_V = (OPCODE_SIG_TYPE_V) | (OPCODE_SIG_TYPE_V << 3),
  OPCODE_SIG_V_O_O =
      (OPCODE_SIG_TYPE_V) | (OPCODE_SIG_TYPE_O << 3) | (OPCODE_SIG_TYPE_O << 6),
  OPCODE_SIG_V_V_O =
      (OPCODE_SIG_TYPE_V) | (OPCODE_SIG_TYPE_V << 3) | (OPCODE_SIG_TYPE_O << 6),
  OPCODE_SIG_V_V_O_V = (OPCODE_SIG_TYPE_V) | (OPCODE_SIG_TYPE_V << 3) |
                       (OPCODE_SIG_TYPE_O << 6) | (OPCODE_SIG_TYPE_V << 9),
  OPCODE_SIG_V_V_V =
      (OPCODE_SIG_TYPE_V) | (OPCODE_SIG_TYPE_V << 3) | (OPCODE_SIG_TYPE_V << 6),
  OPCODE_SIG_V_V_V_O = (OPCODE_SIG_TYPE_V) | (OPCODE_SIG_TYPE_V << 3) |
                       (OPCODE_SIG_TYPE_V << 6) | (OPCODE_SIG_TYPE_O << 9),
  OPCODE_SIG_V_V_V_V = (OPCODE_SIG_TYPE_V) | (OPCODE_SIG_TYPE_V << 3) |
                       (OPCODE_SIG_TYPE_V << 6) | (OPCODE_SIG_TYPE_V << 9),
};

#define GET_OPCODE_SIG_TYPE_DEST(sig) (OpcodeSignatureType)(sig & 0x7)
#define GET_OPCODE_SIG_TYPE_SRC1(sig) (OpcodeSignatureType)((sig >> 3) & 0x7)
#define GET_OPCODE_SIG_TYPE_SRC2(sig) (OpcodeSignatureType)((sig >> 6) & 0x7)
#define GET_OPCODE_SIG_TYPE_SRC3(sig) (OpcodeSignatureType)((sig >> 9) & 0x7)

typedef struct {
  uint32_t flags;
  uint32_t signature;
  const char* name;
  Opcode num;
} OpcodeInfo;

#define DEFINE_OPCODE(num, name, sig, flags) extern const OpcodeInfo num##_info;
#include "xenia/cpu/hir/opcodes.inl"
#undef DEFINE_OPCODE

}  // namespace hir
}  // namespace cpu
}  // namespace xe

#endif  // XENIA_CPU_HIR_OPCODES_H_
