/*
 * Copyright (c) 2013, Red Hat Inc.
 * All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code 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
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */

#ifndef _DECODE_H
#define _DECODE_H

#include <sys/types.h>
#include "cpustate_aarch64.hpp"

// bitfield immediate expansion helper

extern int expandLogicalImmediate(u_int32_t immN, u_int32_t immr,
				    u_int32_t imms, u_int64_t &bimm);


/*
 * codes used in conditional instructions
 *
 * these are passed to conditional operations to identify which
 * condition to test for
 */
enum CondCode {
  EQ = 0b0000, // meaning Z == 1
  NE = 0b0001, // meaning Z == 0
  HS = 0b0010, // meaning C == 1
  CS = HS,
  LO = 0b0011, // meaning C == 0
  CC = LO,
  MI = 0b0100, // meaning N == 1
  PL = 0b0101, // meaning N == 0
  VS = 0b0110, // meaning V == 1
  VC = 0b0111, // meaning V == 0
  HI = 0b1000, // meaning C == 1 && Z == 0
  LS = 0b1001, // meaning !(C == 1 && Z == 0)
  GE = 0b1010, // meaning N == V
  LT = 0b1011, // meaning N != V
  GT = 0b1100, // meaning Z == 0 && N == V
  LE = 0b1101, // meaning !(Z == 0 && N == V)
  AL = 0b1110, // meaning ANY
  NV = 0b1111  // ditto
};

/*
 * certain addressing modes for load require pre or post writeback of
 * the computed address to a base register
 */
enum WriteBack {
  Post = 0,
  Pre = 1
};

/*
 * certain addressing modes for load require an offset to
 * be optionally scaled so the decode needs to pass that
 * through to the execute routine
 */
enum Scaling {
  Unscaled = 0,
  Scaled = 1
};

/*
 * when we do have to scale we do so by shifting using
 * log(bytes in data element - 1) as the shift count.
 * so we don't have to scale offsets when loading
 * bytes.
 */
enum ScaleShift {
  ScaleShift16 = 1,
  ScaleShift32 = 2,
  ScaleShift64 = 3,
  ScaleShift128 = 4
};

/*
 * one of the addressing modes for load requires a 32-bit register
 * value to be either zero- or sign-extended for these instructions
 * UXTW or SXTW should be passed
 *
 * arithmetic register data processing operations can optionally
 * extend a portion of the second register value for these
 * instructions the value supplied must identify the portion of the
 * register which is to be zero- or sign-exended
 */
enum Extension {
  UXTB = 0,
  UXTH = 1,
  UXTW = 2,
  UXTX = 3,
  SXTB = 4,
  SXTH = 5,
  SXTW = 6,
  SXTX = 7
};

/*
 * arithmetic and logical register data processing operations
 * optionally perform a shift on the second register value
 */
enum Shift {
  LSL = 0,
  LSR = 1,
  ASR = 2,
  ROR = 3
};

/*
 * bit twiddling helpers for instruction decode
 */

// 32 bit mask with bits [hi,...,lo] set

static inline u_int32_t mask32(int hi = 31, int lo = 0)
{
  int nbits = (hi + 1) - lo;
  return ((1 << nbits) - 1) << lo;
}

static inline u_int64_t mask64(int hi = 63, int lo = 0)
{
  int nbits = (hi + 1) - lo;
  return ((1L << nbits) - 1) << lo;
}

// pick bits [hi,...,lo] from val
static inline u_int32_t pick32(u_int32_t val, int hi = 31, int lo = 0)
{
  return (val & mask32(hi, lo));
}

// pick bits [hi,...,lo] from val
static inline u_int64_t pick64(u_int64_t val, int hi = 31, int lo = 0)
{
  return (val & mask64(hi, lo));
}

// pick bits [hi,...,lo] from val and shift to [(hi-(newlo - lo)),newlo]
static inline u_int32_t pickshift32(u_int32_t val, int hi = 31,
				    int lo = 0, int newlo = 0)
{
  u_int32_t bits = pick32(val, hi, lo);
  if (lo < newlo) {
    return (bits << (newlo - lo));
  } else {
    return (bits >> (lo - newlo));
  }
}
// mask [hi,lo] and shift down to start at bit 0
static inline u_int32_t pickbits32(u_int32_t val, int hi = 31, int lo = 0)
{
  return (pick32(val, hi, lo) >> lo);
}

// mask [hi,lo] and shift down to start at bit 0
static inline u_int64_t pickbits64(u_int64_t val, int hi = 63, int lo = 0)
{
  return (pick64(val, hi, lo) >> lo);
}

/*
 * decode registers, immediates and constants of various types
 */

static inline GReg greg(u_int32_t val, int lo)
{
  return (GReg)pickbits32(val, lo + 4, lo);
}

static inline VReg vreg(u_int32_t val, int lo)
{
  return (VReg)pickbits32(val, lo + 4, lo);
}

static inline u_int32_t uimm(u_int32_t val, int hi, int lo)
{
  return pickbits32(val, hi, lo);
}

static inline int32_t simm(u_int32_t val, int hi = 31, int lo = 0) {
  union {
    u_int32_t u;
    int32_t n;
  };

  u = val << (31 - hi);
  n = n >> (31 - hi + lo);
  return n;
}

static inline int64_t simm(u_int64_t val, int hi = 63, int lo = 0) {
  union {
    u_int64_t u;
    int64_t n;
  };

  u = val << (63 - hi);
  n = n >> (63 - hi + lo);
  return n;
}

static inline Shift shift(u_int32_t val, int lo)
{
  return (Shift)pickbits32(val, lo+1, lo);
}

static inline Extension extension(u_int32_t val, int lo)
{
  return (Extension)pickbits32(val, lo+2, lo);
}

static inline Scaling scaling(u_int32_t val, int lo)
{
  return (Scaling)pickbits32(val, lo, lo);
}

static inline WriteBack writeback(u_int32_t val, int lo)
{
  return (WriteBack)pickbits32(val, lo, lo);
}

static inline CondCode condcode(u_int32_t val, int lo)
{
  return (CondCode)pickbits32(val, lo+3, lo);
}

/*
 * operation decode
 */
// bits [28,25] are the primary dispatch vector

static inline u_int32_t dispatchGroup(u_int32_t val)
{
  return pickshift32(val, 28, 25, 0);
}

/*
 * the 16 possible values for bits [28,25] identified by tags which
 * map them to the 5 main instruction groups LDST, DPREG, ADVSIMD,
 * BREXSYS and DPIMM.
 *
 * An extra group PSEUDO is included in one of the unallocated ranges
 * for simulator-specific pseudo-instructions.
 */
enum DispatchGroup {
  GROUP_PSEUDO_0000,
  GROUP_UNALLOC_0001,
  GROUP_UNALLOC_0010,
  GROUP_UNALLOC_0011,
  GROUP_LDST_0100,
  GROUP_DPREG_0101,
  GROUP_LDST_0110,
  GROUP_ADVSIMD_0111,
  GROUP_DPIMM_1000,
  GROUP_DPIMM_1001,
  GROUP_BREXSYS_1010,
  GROUP_BREXSYS_1011,
  GROUP_LDST_1100,
  GROUP_DPREG_1101,
  GROUP_LDST_1110,
  GROUP_ADVSIMD_1111
};

// bits [31, 29] of a Pseudo are the secondary dispatch vector

static inline u_int32_t dispatchPseudo(u_int32_t val)
{
  return pickshift32(val, 31, 29, 0);
}

/*
 * the 8 possible values for bits [31,29] in a Pseudo Instruction.
 * Bits [28,25] are always 0000.
 */

enum DispatchPseudo {
  PSEUDO_UNALLOC_000, // unallocated
  PSEUDO_UNALLOC_001, // ditto
  PSEUDO_UNALLOC_010, // ditto
  PSEUDO_UNALLOC_011, // ditto
  PSEUDO_UNALLOC_100, // ditto
  PSEUDO_UNALLOC_101, // ditto
  PSEUDO_CALLOUT_110, // CALLOUT -- bits [24,0] identify call/ret sig
  PSEUDO_HALT_111     // HALT -- bits [24, 0] identify halt code
};

// bits [25, 23] of a DPImm are the secondary dispatch vector

static inline u_int32_t dispatchDPImm(u_int32_t instr)
{
  return pickshift32(instr, 25, 23, 0);
}

/*
 * the 8 possible values for bits [25,23] in a Data Processing Immediate
 * Instruction. Bits [28,25] are always 100_.
 */

enum DispatchDPImm {
  DPIMM_PCADR_000,  // PC-rel-addressing
  DPIMM_PCADR_001,  // ditto
  DPIMM_ADDSUB_010,  // Add/Subtract (immediate)
  DPIMM_ADDSUB_011, // ditto
  DPIMM_LOG_100,    // Logical (immediate)
  DPIMM_MOV_101,    // Move Wide (immediate)
  DPIMM_BITF_110,   // Bitfield
  DPIMM_EXTR_111    // Extract
};

// bits [29,28:26] of a LS are the secondary dispatch vector

static inline u_int32_t dispatchLS(u_int32_t instr)
{
  return (pickshift32(instr, 29, 28, 1) |
	  pickshift32(instr, 26, 26, 0));
}

/*
 * the 8 possible values for bits [29,28:26] in a Load/Store
 * Instruction. Bits [28,25] are always _1_0
 */

enum DispatchLS {
  LS_EXCL_000,    // Load/store exclusive (includes some unallocated)
  LS_ADVSIMD_001, // AdvSIMD load/store (various -- includes some unallocated)
  LS_LIT_010,     // Load register literal (includes some unallocated)
  LS_LIT_011,     // ditto
  LS_PAIR_100,    // Load/store register pair (various)
  LS_PAIR_101,    // ditto
  LS_OTHER_110,   // other load/store formats
  LS_OTHER_111    // ditto
};

// bits [28:24:21] of a DPReg are the secondary dispatch vector

static inline u_int32_t dispatchDPReg(u_int32_t instr)
{
  return (pickshift32(instr, 28, 28, 2) |
	  pickshift32(instr, 24, 24, 1) |
	  pickshift32(instr, 21, 21, 0));
}

/*
 * the 8 possible values for bits [28:24:21] in a Data Processing
 * Register Instruction. Bits [28,25] are always _101
 */

enum DispatchDPReg {
  DPREG_LOG_000,     // Logical (shifted register)
  DPREG_LOG_001,     // ditto
  DPREG_ADDSHF_010,  // Add/subtract (shifted register)
  DPREG_ADDEXT_011,  // Add/subtract (extended register)
  DPREG_ADDCOND_100, // Add/subtract (with carry) AND
                     // Cond compare/select AND
                     // Data Processing (1/2 source)
  DPREG_UNALLOC_101, // Unallocated
  DPREG_3SRC_110, // Data Processing (3 source)
  DPREG_3SRC_111  // Data Processing (3 source)
};

// bits [31,29] of a BrExSys are the secondary dispatch vector

static inline u_int32_t dispatchBrExSys(u_int32_t instr)
{
  return pickbits32(instr, 31, 29);
}

/*
 * the 8 possible values for bits [31,29] in a Branch/Exception/System
 * Instruction. Bits [28,25] are always 101_
 */

enum DispatchBr {
  BR_IMM_000,     // Unconditional branch (immediate)
  BR_IMMCMP_001,  // Compare & branch (immediate) AND
                  // Test & branch (immediate)
  BR_IMMCOND_010, // Conditional branch (immediate) AND Unallocated
  BR_UNALLOC_011, // Unallocated
  BR_IMM_100,     // Unconditional branch (immediate)
  BR_IMMCMP_101,  // Compare & branch (immediate) AND
                  // Test & branch (immediate)
  BR_REG_110,     // Unconditional branch (register) AND System AND
                  // Excn gen AND Unallocated
  BR_UNALLOC_111  // Unallocated
};

/*
 * TODO still need to provide secondary decode and dispatch for
 * AdvSIMD Insructions with instr[28,25] = 0111 or 1111
 */

#endif // ifndef DECODE_H
