/* decode.h -- Prototypes for AArch64 simulator decoder functions.

   Copyright (C) 2015-2020 Free Software Foundation, Inc.

   Contributed by Red Hat.

   This file is part of GDB.

   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 3 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, see <http://www.gnu.org/licenses/>.  */

#ifndef _DECODE_H
#define _DECODE_H

#include <sys/types.h>
#include "cpustate.h"

/* Codes used in conditional instructions

   These are passed to conditional operations to identify which
   condition to test for.  */

typedef enum CondCode
{
  EQ = 0x0, /* meaning Z == 1 */
  NE = 0x1, /* meaning Z == 0 */
  HS = 0x2, /* meaning C == 1 */
  CS = HS,
  LO = 0x3, /* meaning C == 0 */
  CC = LO,
  MI = 0x4, /* meaning N == 1 */
  PL = 0x5, /* meaning N == 0 */
  VS = 0x6, /* meaning V == 1 */
  VC = 0x7, /* meaning V == 0 */
  HI = 0x8, /* meaning C == 1 && Z == 0 */
  LS = 0x9, /* meaning !(C == 1 && Z == 0) */
  GE = 0xa, /* meaning N == V */
  LT = 0xb, /* meaning N != V */
  GT = 0xc, /* meaning Z == 0 && N == V */
  LE = 0xd, /* meaning !(Z == 0 && N == V) */
  AL = 0xe, /* meaning ANY */
  NV = 0xf  /* ditto */
} CondCode;

/* Certain addressing modes for load require pre or post writeback of
   the computed address to a base register.  */

typedef enum WriteBack
{
  Post = 0,
  Pre = 1,
  NoWriteBack = -1
} WriteBack;

/* Certain addressing modes for load require an offset to
   be optionally scaled so the decode needs to pass that
   through to the execute routine.  */

typedef enum Scaling
{
  Unscaled = 0,
  Scaled = 1,
  NoScaling = -1
} Scaling;

/* 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.  */

typedef enum ScaleShift
{
  ScaleShift16 = 1,
  ScaleShift32 = 2,
  ScaleShift64 = 3,
  ScaleShift128 = 4
} ScaleShift;

/* 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.  */

typedef enum Extension
{
  UXTB = 0,
  UXTH = 1,
  UXTW = 2,
  UXTX = 3,
  SXTB = 4,
  SXTH = 5,
  SXTW = 6,
  SXTX = 7,
  NoExtension = -1
} Extension;

/* Arithmetic and logical register data processing operations
   optionally perform a shift on the second register value.  */

typedef enum Shift
{
  LSL = 0,
  LSR = 1,
  ASR = 2,
  ROR = 3
} Shift;

/* Bit twiddling helpers for instruction decode.  */

/* 32 bit mask with bits [hi,...,lo] set.  */
static inline uint32_t
mask32 (int hi, int lo)
{
  int nbits = (hi + 1) - lo;
  return ((1 << nbits) - 1) << lo;
}

/* 64 bit mask with bits [hi,...,lo] set.  */
static inline uint64_t
mask64 (int hi, int lo)
{
  int nbits = (hi + 1) - lo;
  return ((1L << nbits) - 1) << lo;
}

/* Pick bits [hi,...,lo] from val.  */
static inline uint32_t
pick32 (uint32_t val, int hi, int lo)
{
  return val & mask32 (hi, lo);
}

/* Pick bits [hi,...,lo] from val.  */
static inline uint64_t
pick64 (uint64_t val, int hi, int lo)
{
  return val & mask64 (hi, lo);
}

/* Pick bits [hi,...,lo] from val and shift to [(hi-(newlo - lo)),newlo].  */
static inline uint32_t
pickshift32 (uint32_t val, int hi, int lo, int newlo)
{
  uint32_t bits = pick32 (val, hi, lo);

  if (lo < newlo)
    return bits << (newlo - lo);

  return bits >> (lo - newlo);
}

/* Mask [hi,lo] and shift down to start at bit 0.  */
static inline uint32_t
pickbits32 (uint32_t val, int hi, int lo)
{
  return pick32 (val, hi, lo) >> lo;
}

/* Mask [hi,lo] and shift down to start at bit 0.  */
static inline uint64_t
pickbits64 (uint64_t val, int hi, int lo)
{
  return pick64 (val, hi, lo) >> lo;
}

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

static inline int32_t
simm32 (uint32_t val, int hi, int lo)
{
  union
  {
    uint32_t u;
    int32_t n;
  } x;

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

static inline int64_t
simm64 (uint64_t val, int hi, int lo)
{
  union
  {
    uint64_t u;
    int64_t n;
  } x;

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

/* Operation decode.
   Bits [28,24] are the primary dispatch vector.  */

static inline uint32_t
dispatchGroup (uint32_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 uint32_t
dispatchPseudo (uint32_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 uint32_t
dispatchDPImm (uint32_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 uint32_t
dispatchLS (uint32_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 uint32_t
dispatchDPReg (uint32_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 uint32_t
dispatchBrExSys (uint32_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 /* _DECODE_H */