blob: e55bfe846cda4c32b29c373b3d77e432a3c259f1 [file] [log] [blame]
/* ppc-opc.c -- PowerPC opcode list
Copyright (C) 1994-2024 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Cygnus Support
This file is part of the GNU opcodes library.
This library 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, or (at your option)
any later version.
It 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 file; see the file COPYING. If not, write to the
Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
MA 02110-1301, USA. */
#include "sysdep.h"
#include <stdio.h>
#include "opcode/ppc.h"
#include "opintl.h"
#include "libiberty.h"
/* This file holds the PowerPC opcode table. The opcode table
includes almost all of the extended instruction mnemonics. This
permits the disassembler to use them, and simplifies the assembler
logic, at the cost of increasing the table size. The table is
strictly constant data, so the compiler should be able to put it in
the text segment.
This file also holds the operand table. All knowledge about
inserting operands into instructions and vice-versa is kept in this
file. */
/* The functions used to insert and extract complicated operands. */
/* The ARX, ARY, RX and RY operands are alternate encodings of GPRs. */
static uint64_t
insert_arx (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
value -= 8;
if (value < 0 || value >= 16)
{
*errmsg = _("invalid register");
value = 0xf;
}
return insn | value;
}
static int64_t
extract_arx (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return (insn & 0xf) + 8;
}
static uint64_t
insert_ary (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
value -= 8;
if (value < 0 || value >= 16)
{
*errmsg = _("invalid register");
value = 0xf;
}
return insn | (value << 4);
}
static int64_t
extract_ary (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn >> 4) & 0xf) + 8;
}
static uint64_t
insert_rx (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if (value >= 0 && value < 8)
;
else if (value >= 24 && value <= 31)
value -= 16;
else
{
*errmsg = _("invalid register");
value = 0xf;
}
return insn | value;
}
static int64_t
extract_rx (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
int64_t value = insn & 0xf;
if (value >= 0 && value < 8)
return value;
else
return value + 16;
}
static uint64_t
insert_ry (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if (value >= 0 && value < 8)
;
else if (value >= 24 && value <= 31)
value -= 16;
else
{
*errmsg = _("invalid register");
value = 0xf;
}
return insn | (value << 4);
}
static int64_t
extract_ry (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
int64_t value = (insn >> 4) & 0xf;
if (value >= 0 && value < 8)
return value;
else
return value + 16;
}
/* The BA and BB fields in an XL form instruction or the RA and RB fields or
VRA and VRB fields in a VX form instruction when they must be the same.
This is used for extended mnemonics like crclr. The extraction function
enforces that the fields are the same. */
static uint64_t
insert_bab (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
value &= 0x1f;
return insn | (value << 16) | (value << 11);
}
static int64_t
extract_bab (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t ba = (insn >> 16) & 0x1f;
int64_t bb = (insn >> 11) & 0x1f;
if (ba != bb)
*invalid = 1;
return ba;
}
/* The BT, BA and BB fields in an XL form instruction when they must all be
the same. This is used for extended mnemonics like crclr. The extraction
function enforces that the fields are the same. */
static uint64_t
insert_btab (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
value &= 0x1f;
return (value << 21) | insert_bab (insn, value, dialect, errmsg);
}
static int64_t
extract_btab (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
int64_t bt = (insn >> 21) & 0x1f;
int64_t bab = extract_bab (insn, dialect, invalid);
if (bt != bab)
*invalid = 1;
return bt;
}
/* The BD field in a B form instruction when the - modifier is used.
This modifier means that the branch is not expected to be taken.
For chips built to versions of the architecture prior to version 2
(ie. not Power4 compatible), we set the y bit of the BO field to 1
if the offset is negative. When extracting, we require that the y
bit be 1 and that the offset be positive, since if the y bit is 0
we just want to print the normal form of the instruction.
Power4 compatible targets use two bits, "a", and "t", instead of
the "y" bit. "at" == 00 => no hint, "at" == 01 => unpredictable,
"at" == 10 => not taken, "at" == 11 => taken. The "t" bit is 00001
in BO field, the "a" bit is 00010 for branch on CR(BI) and 01000
for branch on CTR. We only handle the taken/not-taken hint here.
Note that we don't relax the conditions tested here when
disassembling with -Many because insns using extract_bdm and
extract_bdp always occur in pairs. One or the other will always
be valid. */
#define ISA_V2 (PPC_OPCODE_POWER4 | PPC_OPCODE_E500MC | PPC_OPCODE_TITAN)
static uint64_t
insert_bdm (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg ATTRIBUTE_UNUSED)
{
if ((dialect & ISA_V2) == 0)
{
if ((value & 0x8000) != 0)
insn |= 1 << 21;
}
else
{
if ((insn & (0x14 << 21)) == (0x04 << 21))
insn |= 0x02 << 21;
else if ((insn & (0x14 << 21)) == (0x10 << 21))
insn |= 0x08 << 21;
}
return insn | (value & 0xfffc);
}
static int64_t
extract_bdm (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
if ((dialect & ISA_V2) == 0)
{
if (((insn & (1 << 21)) == 0) != ((insn & (1 << 15)) == 0))
*invalid = 1;
}
else
{
if ((insn & (0x17 << 21)) != (0x06 << 21)
&& (insn & (0x1d << 21)) != (0x18 << 21))
*invalid = 1;
}
return ((insn & 0xfffc) ^ 0x8000) - 0x8000;
}
/* The BD field in a B form instruction when the + modifier is used.
This is like BDM, above, except that the branch is expected to be
taken. */
static uint64_t
insert_bdp (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg ATTRIBUTE_UNUSED)
{
if ((dialect & ISA_V2) == 0)
{
if ((value & 0x8000) == 0)
insn |= 1 << 21;
}
else
{
if ((insn & (0x14 << 21)) == (0x04 << 21))
insn |= 0x03 << 21;
else if ((insn & (0x14 << 21)) == (0x10 << 21))
insn |= 0x09 << 21;
}
return insn | (value & 0xfffc);
}
static int64_t
extract_bdp (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
if ((dialect & ISA_V2) == 0)
{
if (((insn & (1 << 21)) == 0) == ((insn & (1 << 15)) == 0))
*invalid = 1;
}
else
{
if ((insn & (0x17 << 21)) != (0x07 << 21)
&& (insn & (0x1d << 21)) != (0x19 << 21))
*invalid = 1;
}
return ((insn & 0xfffc) ^ 0x8000) - 0x8000;
}
static inline int
valid_bo_pre_v2 (int64_t value)
{
/* Certain encodings have bits that are required to be zero.
These are (z must be zero, y may be anything):
0000y
0001y
001zy
0100y
0101y
011zy
1z00y
1z01y
1z1zz
*/
if ((value & 0x14) == 0)
/* BO: 0000y, 0001y, 0100y, 0101y. */
return 1;
else if ((value & 0x14) == 0x4)
/* BO: 001zy, 011zy. */
return (value & 0x2) == 0;
else if ((value & 0x14) == 0x10)
/* BO: 1z00y, 1z01y. */
return (value & 0x8) == 0;
else
/* BO: 1z1zz. */
return value == 0x14;
}
static inline int
valid_bo_post_v2 (int64_t value)
{
/* Certain encodings have bits that are required to be zero.
These are (z must be zero, a & t may be anything):
0000z
0001z
001at
0100z
0101z
011at
1a00t
1a01t
1z1zz
*/
if ((value & 0x14) == 0)
/* BO: 0000z, 0001z, 0100z, 0101z. */
return (value & 0x1) == 0;
else if ((value & 0x14) == 0x14)
/* BO: 1z1zz. */
return value == 0x14;
else if ((value & 0x14) == 0x4)
/* BO: 001at, 011at, with "at" == 0b01 being reserved. */
return (value & 0x3) != 1;
else if ((value & 0x14) == 0x10)
/* BO: 1a00t, 1a01t, with "at" == 0b01 being reserved. */
return (value & 0x9) != 1;
else
return 1;
}
/* Check for legal values of a BO field. */
static int
valid_bo (int64_t value, ppc_cpu_t dialect, int extract)
{
int valid_y = valid_bo_pre_v2 (value);
int valid_at = valid_bo_post_v2 (value);
/* When disassembling with -Many, accept either encoding on the
second pass through opcodes. */
if (extract && dialect == ~(ppc_cpu_t) PPC_OPCODE_ANY)
return valid_y || valid_at;
if ((dialect & ISA_V2) == 0)
return valid_y;
else
return valid_at;
}
/* The BO field in a B form instruction. Warn about attempts to set
the field to an illegal value. */
static uint64_t
insert_bo (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
if (!valid_bo (value, dialect, 0))
*errmsg = _("invalid conditional option");
else if (PPC_OP (insn) == 19
&& (((insn >> 1) & 0x3ff) == 528) && ! (value & 4))
*errmsg = _("invalid counter access");
return insn | ((value & 0x1f) << 21);
}
static int64_t
extract_bo (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
int64_t value = (insn >> 21) & 0x1f;
if (!valid_bo (value, dialect, 1))
*invalid = 1;
return value;
}
/* For the given BO value, return a bit mask detailing which bits
define the branch hints. */
static int64_t
get_bo_hint_mask (int64_t bo, ppc_cpu_t dialect)
{
if ((dialect & ISA_V2) == 0)
{
if ((bo & 0x14) != 0x14)
/* BO: 0000y, 0001y, 001zy, 0100y, 0101y, 011zy, 1z00y, 1z01y . */
return 1;
else
/* BO: 1z1zz. */
return 0;
}
else
{
if ((bo & 0x14) == 0x4)
/* BO: 001at, 011at. */
return 0x3;
else if ((bo & 0x14) == 0x10)
/* BO: 1a00t, 1a01t. */
return 0x9;
else
/* BO: 0000z, 0001z, 0100z, 0101z, 1z1zz. */
return 0;
}
}
/* The BO field in a B form instruction when the + or - modifier is used. */
static uint64_t
insert_boe (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg,
int branch_taken)
{
int64_t implied_hint;
int64_t hint_mask = get_bo_hint_mask (value, dialect);
if (branch_taken)
implied_hint = hint_mask;
else
implied_hint = hint_mask & ~1;
/* The branch hint bit(s) in the BO field must either be zero or exactly
match the branch hint bits implied by the '+' or '-' modifier. */
if (implied_hint == 0)
*errmsg = _("BO value implies no branch hint, when using + or - modifier");
else if ((value & hint_mask) != 0
&& (value & hint_mask) != implied_hint)
{
if ((dialect & ISA_V2) == 0)
*errmsg = _("attempt to set y bit when using + or - modifier");
else
*errmsg = _("attempt to set 'at' bits when using + or - modifier");
}
value |= implied_hint;
return insert_bo (insn, value, dialect, errmsg);
}
static int64_t
extract_boe (uint64_t insn,
ppc_cpu_t dialect,
int *invalid,
int branch_taken)
{
int64_t value = (insn >> 21) & 0x1f;
int64_t implied_hint;
int64_t hint_mask = get_bo_hint_mask (value, dialect);
if (branch_taken)
implied_hint = hint_mask;
else
implied_hint = hint_mask & ~1;
if (!valid_bo (value, dialect, 1)
|| implied_hint == 0
|| (value & hint_mask) != implied_hint)
*invalid = 1;
return value;
}
/* The BO field in a B form instruction when the - modifier is used. */
static uint64_t
insert_bom (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
return insert_boe (insn, value, dialect, errmsg, 0);
}
static int64_t
extract_bom (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
return extract_boe (insn, dialect, invalid, 0);
}
/* The BO field in a B form instruction when the + modifier is used. */
static uint64_t
insert_bop (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
return insert_boe (insn, value, dialect, errmsg, 1);
}
static int64_t
extract_bop (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
return extract_boe (insn, dialect, invalid, 1);
}
/* The DCMX field in a X form instruction when the field is split
into separate DC, DM and DX fields. */
static uint64_t
insert_dcmxs (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return (insn
| ((value & 0x1f) << 16)
| ((value & 0x20) >> 3)
| (value & 0x40));
}
static int64_t
extract_dcmxs (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return (insn & 0x40) | ((insn << 3) & 0x20) | ((insn >> 16) & 0x1f);
}
/* The DW field in a X form instruction when the field is split
into separate D and DX fields. */
static uint64_t
insert_dw (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
/* DW offsets must be in the range [-512, -8] and be a multiple of 8. */
if (value < -512
|| value > -8
|| (value & 0x7) != 0)
*errmsg = _("invalid offset: must be in the range [-512, -8] "
"and be a multiple of 8");
return insn | ((value & 0xf8) << 18) | ((value >> 8) & 1);
}
static int64_t
extract_dw (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
int64_t dw = ((insn & 1) << 8) | ((insn >> 18) & 0xf8);
return dw - 512;
}
/* The D field in a DX form instruction when the field is split
into separate D0, D1 and D2 fields. */
static uint64_t
insert_dxd (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | (value & 0xffc1) | ((value & 0x3e) << 15);
}
static int64_t
extract_dxd (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
uint64_t dxd = (insn & 0xffc1) | ((insn >> 15) & 0x3e);
return (dxd ^ 0x8000) - 0x8000;
}
static uint64_t
insert_dxdn (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insert_dxd (insn, -value, dialect, errmsg);
}
static int64_t
extract_dxdn (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
return -extract_dxd (insn, dialect, invalid);
}
/* The D field in a 64-bit D form prefix instruction when the field is split
into separate D0 and D1 fields. */
static uint64_t
insert_d34 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0x3ffff0000ULL) << 16) | (value & 0xffff);
}
static int64_t
extract_d34 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
int64_t mask = 1ULL << 33;
int64_t value = ((insn >> 16) & 0x3ffff0000ULL) | (insn & 0xffff);
value = (value ^ mask) - mask;
return value;
}
/* The NSI34 field in an 8-byte D form prefix instruction. This is the same
as the SI34 field, only negated. The extraction function always marks it
as invalid, since we never want to recognize an instruction which uses
a field of this type. */
static uint64_t
insert_nsi34 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
return insert_d34 (insn, -value, dialect, errmsg);
}
static int64_t
extract_nsi34 (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
int64_t value = extract_d34 (insn, dialect, invalid);
*invalid = 1;
return -value;
}
/* The split IMM32 field in a vector splat insn. */
static uint64_t
insert_imm32 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0xffff0000) << 16) | (value & 0xffff);
}
static int64_t
extract_imm32 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return (insn & 0xffff) | ((insn >> 16) & 0xffff0000);
}
/* The R field in an 8-byte prefix instruction when there are restrictions
between R's value and the RA value (ie, they cannot both be non zero). */
static uint64_t
insert_pcrel (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
value &= 0x1;
int64_t ra = (insn >> 16) & 0x1f;
if (ra != 0 && value != 0)
*errmsg = _("invalid R operand");
return insn | (value << 52);
}
static int64_t
extract_pcrel (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
/* If called with *invalid < 0 to return the value for missing
operands, *invalid will be the negative count of missing operands
including this one. Return a default value of 1 if the PRA0/PRAQ
operand was also omitted (ie. *invalid is -2). Return a default
value of 0 if the PRA0/PRAQ operand was not omitted
(ie. *invalid is -1). */
if (*invalid < 0)
return ~ *invalid & 1;
int64_t ra = (insn >> 16) & 0x1f;
int64_t pcrel = (insn >> 52) & 0x1;
if (ra != 0 && pcrel != 0)
*invalid = 1;
return pcrel;
}
/* Variant of extract_pcrel that sets invalid for R bit clear. Used
to disassemble "paddi rt,0,offset,1" as "pla rt,offset". */
static int64_t
extract_pcrel1 (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
int64_t pcrel = extract_pcrel (insn, dialect, invalid);
if (!pcrel)
*invalid = 1;
return pcrel;
}
/* FXM mask in mfcr and mtcrf instructions. */
static uint64_t
insert_fxm (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
/* If we're handling the mfocrf and mtocrf insns ensure that exactly
one bit of the mask field is set. */
if ((insn & (1 << 20)) != 0)
{
if (value == 0 || (value & -value) != value)
{
*errmsg = _("invalid mask field");
value = 0;
}
}
/* If only one bit of the FXM field is set, we can use the new form
of the instruction, which is faster. Unlike the Power4 branch hint
encoding, this is not backward compatible. Do not generate the
new form unless -mpower4 has been given, or -many and the two
operand form of mfcr was used. */
else if (value > 0
&& (value & -value) == value
&& ((dialect & PPC_OPCODE_POWER4) != 0
|| ((dialect & PPC_OPCODE_ANY) != 0
&& (insn & (0x3ff << 1)) == 19 << 1)))
insn |= 1 << 20;
/* Any other value on mfcr is an error. */
else if ((insn & (0x3ff << 1)) == 19 << 1)
{
/* A value of -1 means we used the one operand form of
mfcr which is valid. */
if (value != -1)
*errmsg = _("invalid mfcr mask");
value = 0;
}
return insn | ((value & 0xff) << 12);
}
static int64_t
extract_fxm (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
/* Return a value of -1 for a missing optional operand, which is
used as a flag by insert_fxm. */
if (*invalid < 0)
return -1;
int64_t mask = (insn >> 12) & 0xff;
/* Is this a Power4 insn? */
if ((insn & (1 << 20)) != 0)
{
/* Exactly one bit of MASK should be set. */
if (mask == 0 || (mask & -mask) != mask)
*invalid = 1;
}
/* Check that non-power4 form of mfcr has a zero MASK. */
else if ((insn & (0x3ff << 1)) == 19 << 1)
{
if (mask != 0)
*invalid = 1;
else
mask = -1;
}
return mask;
}
/* L field in the paste. instruction. */
static uint64_t
insert_l1opt (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 1) << 21);
}
static int64_t
extract_l1opt (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
/* Return a value of 1 for a missing optional operand. */
if (*invalid < 0)
return 1;
return (insn >> 21) & 1;
}
static uint64_t
insert_li20 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return (insn
| ((value & 0xf0000) >> 5)
| ((value & 0x0f800) << 5)
| (value & 0x7ff));
}
static int64_t
extract_li20 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((((insn << 5) & 0xf0000)
| ((insn >> 5) & 0xf800)
| (insn & 0x7ff)) ^ 0x80000) - 0x80000;
}
/* The 2-bit/3-bit L or 2-bit WC field in a SYNC, DCBF or WAIT instruction.
For SYNC, some L values are reserved:
* Values 6 and 7 are reserved on newer server cpus.
* Value 3 is reserved on all server cpus.
* Value 2 is reserved on all other cpus.
For DCBF, some L values are reserved:
* Values 2, 5 and 7 are reserved on all cpus.
For WAIT, some WC values are reserved:
* Value 3 is reserved on all server cpus.
* Values 1 and 2 are reserved on older server cpus. */
static uint64_t
insert_ls (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
int64_t mask;
if (((insn >> 1) & 0x3ff) == 598)
{
/* For SYNC, some L values are illegal. */
mask = (dialect & PPC_OPCODE_POWER10) ? 0x7 : 0x3;
/* If the value is within range, check for other illegal values. */
if ((value & mask) == value)
switch (value)
{
case 2:
if (dialect & PPC_OPCODE_POWER4)
break;
/* Fall through. */
case 3:
case 6:
case 7:
*errmsg = _("illegal L operand value");
break;
default:
break;
}
}
else if (((insn >> 1) & 0x3ff) == 86)
{
/* For DCBF, some L values are illegal. */
mask = (dialect & PPC_OPCODE_POWER10) ? 0x7 : 0x3;
/* If the value is within range, check for other illegal values. */
if ((value & mask) == value)
switch (value)
{
case 2:
case 5:
case 7:
*errmsg = _("illegal L operand value");
break;
default:
break;
}
}
else
{
/* For WAIT, some WC values are illegal. */
mask = 0x3;
/* If the value is within range, check for other illegal values. */
if ((dialect & PPC_OPCODE_A2) == 0
&& (dialect & PPC_OPCODE_E500MC) == 0
&& (value & mask) == value)
switch (value)
{
case 1:
case 2:
if (dialect & PPC_OPCODE_POWER10)
break;
/* Fall through. */
case 3:
*errmsg = _("illegal WC operand value");
break;
default:
break;
}
}
return insn | ((value & mask) << 21);
}
static int64_t
extract_ls (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
uint64_t value;
/* Missing optional operands have a value of zero. */
if (*invalid < 0)
return 0;
if (((insn >> 1) & 0x3ff) == 598)
{
/* For SYNC, some L values are illegal. */
int64_t mask = (dialect & PPC_OPCODE_POWER10) ? 0x7 : 0x3;
value = (insn >> 21) & mask;
switch (value)
{
case 2:
if (dialect & PPC_OPCODE_POWER4)
break;
/* Fall through. */
case 3:
case 6:
case 7:
*invalid = 1;
break;
default:
break;
}
}
else if (((insn >> 1) & 0x3ff) == 86)
{
/* For DCBF, some L values are illegal. */
int64_t mask = (dialect & PPC_OPCODE_POWER10) ? 0x7 : 0x3;
value = (insn >> 21) & mask;
switch (value)
{
case 2:
case 5:
case 7:
*invalid = 1;
break;
default:
break;
}
}
else
{
/* For WAIT, some WC values are illegal. */
value = (insn >> 21) & 0x3;
if ((dialect & PPC_OPCODE_A2) == 0
&& (dialect & PPC_OPCODE_E500MC) == 0)
switch (value)
{
case 1:
case 2:
if (dialect & PPC_OPCODE_POWER10)
break;
/* Fall through. */
case 3:
*invalid = 1;
break;
default:
break;
}
}
return value;
}
/* The 4-bit E field in a sync instruction that accepts 2 operands.
If ESYNC is non-zero, then the L field must be either 0 or 1 and
the complement of ESYNC-bit2. */
static uint64_t
insert_esync (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
uint64_t ls = (insn >> 21) & 0x03;
if (value != 0
&& ((~value >> 1) & 0x1) != ls)
*errmsg = _("incompatible L operand value");
return insn | ((value & 0xf) << 16);
}
static int64_t
extract_esync (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
/* Missing optional operands have a value of zero. */
if (*invalid < 0)
return 0;
uint64_t ls = (insn >> 21) & 0x3;
uint64_t value = (insn >> 16) & 0xf;
if (value != 0
&& ((~value >> 1) & 0x1) != ls)
*invalid = 1;
return value;
}
/* The n operand of clrrwi, which sets the ME field to 31 - n. */
static uint64_t
insert_crwn (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((~value & 0x1f) << 1);
}
static int64_t
extract_crwn (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ~(insn >> 1) & 0x1f;
}
/* The n operand of extlwi, which sets the ME field to n - 1. */
static uint64_t
insert_elwn (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | (((value - 1) & 0x1f) << 1);
}
static int64_t
extract_elwn (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn >> 1) & 0x1f) + 1;
}
/* The n operand of extrwi, sets MB = 32 - n. */
static uint64_t
insert_erwn (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((-value & 0x1f) << 6);
}
static int64_t
extract_erwn (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return (~(insn >> 6) & 0x1f) + 1;
}
/* The b operand of extrwi, sets SH = b + n. */
static uint64_t
insert_erwb (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg ATTRIBUTE_UNUSED)
{
int64_t n = extract_erwn (insn, dialect, NULL);
return insn | (((n + value) & 0x1f) << 11);
}
static int64_t
extract_erwb (uint64_t insn,
ppc_cpu_t dialect,
int *invalid ATTRIBUTE_UNUSED)
{
int64_t n = extract_erwn (insn, dialect, NULL);
return ((insn >> 11) - n) & 0x1f;
}
/* The n and b operands of clrlslwi. */
static uint64_t
insert_cslwn (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
uint64_t mb = 0x1f << 6;
int64_t b = (insn >> 6) & 0x1f;
return ((insn & ~mb) | ((value & 0x1f) << 11) | (((b - value) & 0x1f) << 6)
| ((~value & 0x1f) << 1));
}
static int64_t
extract_cslwb (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t sh = (insn >> 11) & 0x1f;
int64_t mb = (insn >> 6) & 0x1f;
int64_t me = (insn >> 1) & 0x1f;
if (sh != 31 - me)
*invalid = 1;
return (mb + sh) & 0x1f;
}
/* The n and b operands of inslwi. */
static uint64_t
insert_ilwb (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
uint64_t me = 0x1f << 1;
int64_t n = (insn >> 1) & 0x1f;
return ((insn & ~me) | ((-value & 0x1f) << 11) | ((value & 0x1f) << 6)
| (((value + n - 1) & 0x1f) << 1));
}
static int64_t
extract_ilwn (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t sh = (insn >> 11) & 0x1f;
int64_t mb = (insn >> 6) & 0x1f;
int64_t me = (insn >> 1) & 0x1f;
if (((sh + mb) & 0x1f) != 0)
*invalid = 1;
return ((me - mb) & 0x1f) + 1;
}
/* The n and b operands of insrwi. */
static uint64_t
insert_irwb (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
uint64_t me = 0x1f << 1;
int64_t n = (insn >> 1) & 0x1f;
return ((insn & ~me) | ((-(value + n) & 0x1f) << 11) | ((value & 0x1f) << 6)
| (((value + n - 1) & 0x1f) << 1));
}
static int64_t
extract_irwn (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t sh = (insn >> 11) & 0x1f;
int64_t mb = (insn >> 6) & 0x1f;
int64_t me = (insn >> 1) & 0x1f;
if (((sh + me + 1) & 0x1f) != 0)
*invalid = 1;
return ((me - mb) & 0x1f) + 1;
}
/* The MB and ME fields in an M form instruction expressed as a single
operand which is itself a bitmask. The extraction function always
marks it as invalid, since we never want to recognize an
instruction which uses a field of this type. */
static uint64_t
insert_mbe (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
uint64_t uval, mask;
long mb, me, mx, count, last;
uval = value;
if (uval == 0)
{
*errmsg = _("illegal bitmask");
return insn;
}
mb = 0;
me = 32;
if ((uval & 1) != 0)
last = 1;
else
last = 0;
count = 0;
/* mb: location of last 0->1 transition */
/* me: location of last 1->0 transition */
/* count: # transitions */
for (mx = 0, mask = (uint64_t) 1 << 31; mx < 32; ++mx, mask >>= 1)
{
if ((uval & mask) && !last)
{
++count;
mb = mx;
last = 1;
}
else if (!(uval & mask) && last)
{
++count;
me = mx;
last = 0;
}
}
if (me == 0)
me = 32;
if (count != 2 && (count != 0 || ! last))
*errmsg = _("illegal bitmask");
return insn | (mb << 6) | ((me - 1) << 1);
}
static int64_t
extract_mbe (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t ret;
long mb, me;
long i;
*invalid = 1;
mb = (insn >> 6) & 0x1f;
me = (insn >> 1) & 0x1f;
if (mb < me + 1)
{
ret = 0;
for (i = mb; i <= me; i++)
ret |= (uint64_t) 1 << (31 - i);
}
else if (mb == me + 1)
ret = ~0;
else /* (mb > me + 1) */
{
ret = ~0;
for (i = me + 1; i < mb; i++)
ret &= ~((uint64_t) 1 << (31 - i));
}
return ret;
}
/* The MB or ME field in an MD or MDS form instruction. The high bit
is wrapped to the low end. */
static uint64_t
insert_mb6 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0x1f) << 6) | (value & 0x20);
}
static int64_t
extract_mb6 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn >> 6) & 0x1f) | (insn & 0x20);
}
/* The n operand of extrdi, which sets MB field. */
static uint64_t
insert_erdn (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
return insert_mb6 (insn, -value, dialect, errmsg);
}
static int64_t
extract_erdn (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
return (~extract_mb6 (insn, dialect, invalid) & 63) + 1;
}
/* The n operand of extldi, which sets ME field. */
static uint64_t
insert_eldn (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
return insert_mb6 (insn, value - 1, dialect, errmsg);
}
static int64_t
extract_eldn (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
return extract_mb6 (insn, dialect, invalid) + 1;
}
/* The n operand of clrrdi, which set ME field. */
static uint64_t
insert_crdn (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
return insert_mb6 (insn, 63 - value, dialect, errmsg);
}
static int64_t
extract_crdn (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
return 63 - extract_mb6 (insn, dialect, invalid);
}
/* The NB field in an X form instruction. The value 32 is stored as
0. */
static int64_t
extract_nb (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
int64_t ret;
ret = (insn >> 11) & 0x1f;
if (ret == 0)
ret = 32;
return ret;
}
/* The NB field in an lswi instruction, which has special value
restrictions. The value 32 is stored as 0. */
static uint64_t
insert_nbi (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
int64_t rtvalue = (insn >> 21) & 0x1f;
int64_t ravalue = (insn >> 16) & 0x1f;
if (value == 0)
value = 32;
if (rtvalue + (value + 3) / 4 > (rtvalue > ravalue ? ravalue + 32
: ravalue))
*errmsg = _("address register in load range");
return insn | ((value & 0x1f) << 11);
}
/* The NSI field in a D form instruction. This is the same as the SI
field, only negated. The extraction function always marks it as
invalid, since we never want to recognize an instruction which uses
a field of this type. */
static uint64_t
insert_nsi (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | (-value & 0xffff);
}
static int64_t
extract_nsi (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
*invalid = 1;
return -(((insn & 0xffff) ^ 0x8000) - 0x8000);
}
/* The 2-bit SC field in a SYNC or PL field in a WAIT instruction.
For WAIT, some PL values are reserved:
* Values 1, 2 and 3 are reserved. */
static uint64_t
insert_pl (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
/* For WAIT, some PL values are illegal. */
if (((insn >> 1) & 0x3ff) == 30
&& value != 0)
*errmsg = _("illegal PL operand value");
return insn | ((value & 0x3) << 16);
}
static int64_t
extract_pl (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
/* Missing optional operands have a value of zero. */
if (*invalid < 0)
return 0;
uint64_t value = (insn >> 16) & 0x3;
/* For WAIT, some PL values are illegal. */
if (((insn >> 1) & 0x3ff) == 30
&& value != 0)
*invalid = 1;
return value;
}
/* The 2-bit P field in a MMA XX2-form instruction. This is split. */
static uint64_t
insert_p2 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0x2) << 15) | ((value & 0x1) << 11);
}
static int64_t
extract_p2 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
uint64_t value = ((insn >> 15) & 0x2) | ((insn >> 11) & 0x1);
return value;
}
/* The RA field in a D or X form instruction which is an updating
load, which means that the RA field may not be zero and may not
equal the RT field. */
static uint64_t
insert_ral (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if (value == 0
|| (uint64_t) value == ((insn >> 21) & 0x1f))
*errmsg = "invalid register operand when updating";
return insn | ((value & 0x1f) << 16);
}
static int64_t
extract_ral (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t rtvalue = (insn >> 21) & 0x1f;
int64_t ravalue = (insn >> 16) & 0x1f;
if (rtvalue == ravalue || ravalue == 0)
*invalid = 1;
return ravalue;
}
/* The RA field in an lmw instruction, which has special value
restrictions. */
static uint64_t
insert_ram (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if ((uint64_t) value >= ((insn >> 21) & 0x1f))
*errmsg = _("index register in load range");
return insn | ((value & 0x1f) << 16);
}
static int64_t
extract_ram (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
uint64_t rtvalue = (insn >> 21) & 0x1f;
uint64_t ravalue = (insn >> 16) & 0x1f;
if (ravalue >= rtvalue)
*invalid = 1;
return ravalue;
}
/* The RA field in the DQ form lq or an lswx instruction, which have special
value restrictions. */
static uint64_t
insert_raq (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
int64_t rtvalue = (insn >> 21) & 0x1f;
if (value == rtvalue)
*errmsg = _("source and target register operands must be different");
return insn | ((value & 0x1f) << 16);
}
static int64_t
extract_raq (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
/* Missing optional operands have a value of zero. */
if (*invalid < 0)
return 0;
uint64_t rtvalue = (insn >> 21) & 0x1f;
uint64_t ravalue = (insn >> 16) & 0x1f;
if (ravalue == rtvalue)
*invalid = 1;
return ravalue;
}
/* The RA field in a D or X form instruction which is an updating
store or an updating floating point load, which means that the RA
field may not be zero. */
static uint64_t
insert_ras (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if (value == 0)
*errmsg = _("invalid register operand when updating");
return insn | ((value & 0x1f) << 16);
}
static int64_t
extract_ras (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
uint64_t ravalue = (insn >> 16) & 0x1f;
if (ravalue == 0)
*invalid = 1;
return ravalue;
}
/* The RS and RB fields in an X form instruction when they must be the same.
This is used for extended mnemonics like mr. The extraction function
enforces that the fields are the same. */
static uint64_t
insert_rsb (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
value &= 0x1f;
return insn | (value << 21) | (value << 11);
}
static int64_t
extract_rsb (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t rs = (insn >> 21) & 0x1f;
int64_t rb = (insn >> 11) & 0x1f;
if (rs != rb)
*invalid = 1;
return rs;
}
/* The RB field in an lswx instruction, which has special value
restrictions. */
static uint64_t
insert_rbx (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
int64_t rtvalue = (insn >> 21) & 0x1f;
if (value == rtvalue)
*errmsg = _("source and target register operands must be different");
return insn | ((value & 0x1f) << 11);
}
static int64_t
extract_rbx (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
uint64_t rtvalue = (insn >> 21) & 0x1f;
uint64_t rbvalue = (insn >> 11) & 0x1f;
if (rbvalue == rtvalue)
*invalid = 1;
return rbvalue;
}
/* The SCI8 field is made up of SCL and {U,N}I8 fields. */
static uint64_t
insert_sci8 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
uint64_t fill_scale = 0;
uint64_t ui8 = value;
if ((ui8 & 0xffffff00) == 0)
;
else if ((ui8 & 0xffffff00) == 0xffffff00)
fill_scale = 0x400;
else if ((ui8 & 0xffff00ff) == 0)
{
fill_scale = 1 << 8;
ui8 >>= 8;
}
else if ((ui8 & 0xffff00ff) == 0xffff00ff)
{
fill_scale = 0x400 | (1 << 8);
ui8 >>= 8;
}
else if ((ui8 & 0xff00ffff) == 0)
{
fill_scale = 2 << 8;
ui8 >>= 16;
}
else if ((ui8 & 0xff00ffff) == 0xff00ffff)
{
fill_scale = 0x400 | (2 << 8);
ui8 >>= 16;
}
else if ((ui8 & 0x00ffffff) == 0)
{
fill_scale = 3 << 8;
ui8 >>= 24;
}
else if ((ui8 & 0x00ffffff) == 0x00ffffff)
{
fill_scale = 0x400 | (3 << 8);
ui8 >>= 24;
}
else
{
*errmsg = _("illegal immediate value");
ui8 = 0;
}
return insn | fill_scale | (ui8 & 0xff);
}
static int64_t
extract_sci8 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
int64_t fill = insn & 0x400;
int64_t scale_factor = (insn & 0x300) >> 5;
int64_t value = (insn & 0xff) << scale_factor;
if (fill != 0)
value |= ~((int64_t) 0xff << scale_factor);
return value;
}
static uint64_t
insert_sci8n (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
return insert_sci8 (insn, -value, dialect, errmsg);
}
static int64_t
extract_sci8n (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
return -extract_sci8 (insn, dialect, invalid);
}
static uint64_t
insert_oimm (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | (((value - 1) & 0x1f) << 4);
}
static int64_t
extract_oimm (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn >> 4) & 0x1f) + 1;
}
/* The n operand of rotrwi, sets SH = 32 - n. */
static uint64_t
insert_rrwn (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((-value & 0x1f) << 11);
}
static int64_t
extract_rrwn (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return 31 & -(insn >> 11);
}
/* The n operand of slwi, sets SH = n and ME = 31 - n. */
static uint64_t
insert_slwn (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0x1f) << 11) | ((~value & 0x1f) << 1);
}
static int64_t
extract_slwn (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t sh = (insn >> 11) & 0x1f;
int64_t nme = ~(insn >> 1) & 0x1f;
if (sh != nme)
*invalid = 1;
return sh;
}
/* The n operand of srwi, sets SH = 32 - n and MB = n. */
static uint64_t
insert_srwn (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((-value & 0x1f) << 11) | ((value & 0x1f) << 6);
}
static int64_t
extract_srwn (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t nsh = -(insn >> 11) & 0x1f;
int64_t mb = (insn >> 6) & 0x1f;
if (nsh != mb)
*invalid = 1;
return nsh;
}
/* The SH field in an MD form instruction. This is split. */
static uint64_t
insert_sh6 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0x1f) << 11) | ((value & 0x20) >> 4);
}
static int64_t
extract_sh6 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn >> 11) & 0x1f) | ((insn << 4) & 0x20);
}
/* The n operand of rotrdi, which writes to SH field. */
static uint64_t
insert_rrdn (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
return insert_sh6 (insn, -value, dialect, errmsg);
}
static int64_t
extract_rrdn (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
return -extract_sh6 (insn, dialect, invalid) & 63;
}
/* The n operand of sldi, which writes to SH and ME fields. */
static uint64_t
insert_sldn (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
insn = insert_sh6 (insn, value, dialect, errmsg);
return insert_crdn (insn, value, dialect, errmsg);
}
static int64_t
extract_sldn (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
int64_t sh = extract_sh6 (insn, dialect, invalid);
int64_t me = extract_crdn (insn, dialect, invalid);
if (me != sh)
*invalid = 1;
return sh;
}
/* The n operand of srdi, which writes to SH and MB fields. */
static uint64_t
insert_srdn (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
insn = insert_rrdn (insn, value, dialect, errmsg);
return insert_mb6 (insn, value, dialect, errmsg);
}
static int64_t
extract_srdn (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
int64_t sh = extract_rrdn (insn, dialect, invalid);
int64_t mb = extract_mb6 (insn, dialect, invalid);
if (mb != sh)
*invalid = 1;
return sh;
}
/* The b operand of extrdi, which sets SH field. */
static uint64_t
insert_erdb (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
int64_t n = extract_erdn (insn, dialect, NULL);
return insert_sh6 (insn, value + n, dialect, errmsg);
}
static int64_t
extract_erdb (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
int64_t sh = extract_sh6 (insn, dialect, invalid);
int64_t n = extract_erdn (insn, dialect, invalid);
return (sh - n) & 63;
}
/* The b and n operands of clrlsldi. */
static uint64_t
insert_csldn (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
uint64_t mb6 = 0x3f << 5;
int64_t b = extract_mb6 (insn, dialect, NULL);
insn = insert_mb6 (insn & ~mb6, b - value, dialect, errmsg);
return insert_sh6 (insn, value, dialect, errmsg);
}
static int64_t
extract_csldb (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
int64_t sh = extract_sh6 (insn, dialect, invalid);
int64_t mb = extract_mb6 (insn, dialect, invalid);
return (mb + sh) & 63;
}
/* The b and n operands of insrdi. */
static uint64_t
insert_irdb (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
uint64_t sh6 = (0x1f << 11) | 2;
int64_t n = extract_sh6 (insn, dialect, NULL);
insn = insert_sh6 (insn & ~sh6, -(value + n), dialect, errmsg);
return insert_mb6 (insn, value, dialect, errmsg);
}
static int64_t
extract_irdn (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
int64_t sh = extract_sh6 (insn, dialect, invalid);
int64_t mb = extract_mb6 (insn, dialect, invalid);
return (~(mb + sh) & 63) + 1;
}
/* The SPR field in an XFX form instruction. This is flipped--the
lower 5 bits are stored in the upper 5 and vice- versa. */
static uint64_t
insert_spr (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6);
}
static int64_t
extract_spr (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0);
}
/* Some dialects have 8 [DI]BAT registers instead of the standard 4. */
#define ALLOW8_BAT (PPC_OPCODE_750)
static uint64_t
insert_sprbat (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
if ((uint64_t) value > 7
|| ((uint64_t) value > 3 && (dialect & ALLOW8_BAT) == 0))
*errmsg = _("invalid bat number");
/* If this is [di]bat4..7 then use spr 560..575, otherwise 528..543. */
if ((uint64_t) value > 3)
value = ((value & 3) << 6) | 1;
else
value = value << 6;
return insn | (value << 11);
}
static int64_t
extract_sprbat (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
uint64_t val = (insn >> 17) & 0x3;
val = val + ((insn >> 9) & 0x4);
if (val > 3 && (dialect & ALLOW8_BAT) == 0)
*invalid = 1;
return val;
}
/* Some dialects have 8 SPRG registers instead of the standard 4. */
#define ALLOW8_SPRG (PPC_OPCODE_BOOKE | PPC_OPCODE_405)
static uint64_t
insert_sprg (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
if ((uint64_t) value > 7
|| ((uint64_t) value > 3 && (dialect & ALLOW8_SPRG) == 0))
*errmsg = _("invalid sprg number");
/* If this is mfsprg4..7 then use spr 260..263 which can be read in
user mode. Anything else must use spr 272..279. */
if ((uint64_t) value <= 3 || (insn & 0x100) != 0)
value |= 0x10;
return insn | ((value & 0x17) << 16);
}
static int64_t
extract_sprg (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
uint64_t val = (insn >> 16) & 0x1f;
/* mfsprg can use 260..263 and 272..279. mtsprg only uses spr 272..279
If not BOOKE, 405 or VLE, then both use only 272..275. */
if ((val - 0x10 > 3 && (dialect & ALLOW8_SPRG) == 0)
|| (val - 0x10 > 7 && (insn & 0x100) != 0)
|| val <= 3
|| (val & 8) != 0)
*invalid = 1;
return val & 7;
}
/* The TBR field in an XFX instruction. This is just like SPR, but it
is optional. */
static uint64_t
insert_tbr (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if (value != 268 && value != 269)
*errmsg = _("invalid tbr number");
return insn | ((value & 0x1f) << 16) | ((value & 0x3e0) << 6);
}
static int64_t
extract_tbr (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
/* Missing optional operands have a value of 268. */
if (*invalid < 0)
return 268;
int64_t ret = ((insn >> 16) & 0x1f) | ((insn >> 6) & 0x3e0);
if (ret != 268 && ret != 269)
*invalid = 1;
return ret;
}
/* The XT and XS fields in an XX1 or XX3 form instruction. This is split. */
static uint64_t
insert_xt6 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0x1f) << 21) | ((value & 0x20) >> 5);
}
static int64_t
extract_xt6 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn << 5) & 0x20) | ((insn >> 21) & 0x1f);
}
/* The XT and XS fields in an DQ form VSX instruction. This is split. */
static uint64_t
insert_xtq6 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0x1f) << 21) | ((value & 0x20) >> 2);
}
static int64_t
extract_xtq6 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn << 2) & 0x20) | ((insn >> 21) & 0x1f);
}
/* The 5-bit XAp field in an XX3 form instruction. This is split. */
static uint64_t
insert_xa5 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0x1e) << 16) | ((value & 0x20) >> 3);
}
static int64_t
extract_xa5 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn << 3) & 0x20) | ((insn >> 16) & 0x1e);
}
/* The XA field in an XX3 form instruction. This is split. */
static uint64_t
insert_xa6 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0x1f) << 16) | ((value & 0x20) >> 3);
}
static int64_t
extract_xa6 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn << 3) & 0x20) | ((insn >> 16) & 0x1f);
}
/* The XA field in an MMA XX3 form instruction. This is split
and must not overlap with the ACC operand. */
static uint64_t
insert_xa6a (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
int64_t acc = (insn >> 23) & 0x7;
/* Power10 doesn't allow VSRs to overlap ACCs in MMA instructions. */
if ((dialect & PPC_OPCODE_FUTURE) == 0
&& (value >> 2) == acc)
*errmsg = _("VSR overlaps ACC operand");
return insert_xa6 (insn, value, dialect, errmsg);
}
static int64_t
extract_xa6a (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
int64_t acc = (insn >> 23) & 0x7;
int64_t value = extract_xa6 (insn, dialect, invalid);
/* Power10 doesn't allow VSRs to overlap ACCs in MMA instructions. */
if ((dialect & PPC_OPCODE_FUTURE) == 0
&& (value >> 2) == acc)
*invalid = 1;
return value;
}
/* The 5-bit XB field in an XX3 form instruction. This is split. */
static uint64_t
insert_xb5 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0x1e) << 11) | ((value & 0x20) >> 4);
}
static int64_t
extract_xb5 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn << 4) & 0x20) | ((insn >> 11) & 0x1e);
}
/* The XB field in an XX3 form instruction. This is split. */
static uint64_t
insert_xb6 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0x1f) << 11) | ((value & 0x20) >> 4);
}
static int64_t
extract_xb6 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn << 4) & 0x20) | ((insn >> 11) & 0x1f);
}
/* The XB field in an MMA XX3 form instruction. This is split
and must not overlap with the ACC operand. */
static uint64_t
insert_xb6a (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
int64_t acc = (insn >> 23) & 0x7;
/* Power10 doesn't allow VSRs to overlap ACCs in MMA instructions. */
if ((dialect & PPC_OPCODE_FUTURE) == 0
&& (value >> 2) == acc)
*errmsg = _("VSR overlaps ACC operand");
return insert_xb6 (insn, value, dialect, errmsg);
}
static int64_t
extract_xb6a (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
int64_t acc = (insn >> 23) & 0x7;
int64_t value = extract_xb6 (insn, dialect, invalid);
/* Power10 doesn't allow VSRs to overlap ACCs in MMA instructions. */
if ((dialect & PPC_OPCODE_FUTURE) == 0
&& (value >> 2) == acc)
*invalid = 1;
return value;
}
/* The XA and XB fields in an XX3 form instruction when they must be the same.
This is used for extended mnemonics like xvmovdp. The extraction function
enforces that the fields are the same. */
static uint64_t
insert_xab6 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect,
const char **errmsg)
{
return insert_xa6 (insn, value, dialect, errmsg)
| insert_xb6 (insn, value, dialect, errmsg);
}
static int64_t
extract_xab6 (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
int64_t xa6 = extract_xa6 (insn, dialect, invalid);
int64_t xb6 = extract_xb6 (insn, dialect, invalid);
if (xa6 != xb6)
*invalid = 1;
return xa6;
}
/* The XC field in an XX4 form instruction. This is split. */
static uint64_t
insert_xc6 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0x1f) << 6) | ((value & 0x20) >> 2);
}
static int64_t
extract_xc6 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn << 2) & 0x20) | ((insn >> 6) & 0x1f);
}
/* The split XTp and XSp field in a vector paired insn. */
static uint64_t
insert_xtp (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0x1e) << 21) | ((value & 0x20) << (21 - 5));
}
static int64_t
extract_xtp (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn >> (21 - 5)) & 0x20) | ((insn >> 21) & 0x1e);
}
/* The split XT field in a vector splat insn. */
static uint64_t
insert_xts (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0x1f) << 21) | ((value & 0x20) << (16 - 5));
}
static int64_t
extract_xts (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn >> (16 - 5)) & 0x20) | ((insn >> 21) & 0x1f);
}
static uint64_t
insert_dm (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if (value != 0 && value != 1)
*errmsg = _("invalid constant");
return insn | (((value) ? 3 : 0) << 8);
}
static int64_t
extract_dm (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t value = (insn >> 8) & 3;
if (value != 0 && value != 3)
*invalid = 1;
return (value) ? 1 : 0;
}
/* The VLESIMM field in an I16A form instruction. This is split. */
static uint64_t
insert_vlesi (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0xf800) << 10) | (value & 0x7ff);
}
static int64_t
extract_vlesi (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
int64_t value = ((insn >> 10) & 0xf800) | (insn & 0x7ff);
value = (value ^ 0x8000) - 0x8000;
return value;
}
static uint64_t
insert_vlensi (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
value = -value;
return insn | ((value & 0xf800) << 10) | (value & 0x7ff);
}
static int64_t
extract_vlensi (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t value = ((insn >> 10) & 0xf800) | (insn & 0x7ff);
value = (value ^ 0x8000) - 0x8000;
/* Don't use for disassembly. */
*invalid = 1;
return -value;
}
/* The VLEUIMM field in an I16A form instruction. This is split. */
static uint64_t
insert_vleui (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0xf800) << 10) | (value & 0x7ff);
}
static int64_t
extract_vleui (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn >> 10) & 0xf800) | (insn & 0x7ff);
}
/* The VLEUIMML field in an I16L form instruction. This is split. */
static uint64_t
insert_vleil (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0xf800) << 5) | (value & 0x7ff);
}
static int64_t
extract_vleil (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn >> 5) & 0xf800) | (insn & 0x7ff);
}
static uint64_t
insert_evuimm1_ex0 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if (value <= 0 || value > 0x1f)
*errmsg = _("UIMM = 00000 is illegal");
return insn | ((value & 0x1f) << 11);
}
static int64_t
extract_evuimm1_ex0 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t value = ((insn >> 11) & 0x1f);
if (value == 0)
*invalid = 1;
return value;
}
static uint64_t
insert_evuimm2_ex0 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if (value <= 0 || value > 0x3e)
*errmsg = _("UIMM = 00000 is illegal");
return insn | ((value & 0x3e) << 10);
}
static int64_t
extract_evuimm2_ex0 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t value = ((insn >> 10) & 0x3e);
if (value == 0)
*invalid = 1;
return value;
}
static uint64_t
insert_evuimm4_ex0 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if (value <= 0 || value > 0x7c)
*errmsg = _("UIMM = 00000 is illegal");
return insn | ((value & 0x7c) << 9);
}
static int64_t
extract_evuimm4_ex0 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t value = ((insn >> 9) & 0x7c);
if (value == 0)
*invalid = 1;
return value;
}
static uint64_t
insert_evuimm8_ex0 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if (value <= 0 || value > 0xf8)
*errmsg = _("UIMM = 00000 is illegal");
return insn | ((value & 0xf8) << 8);
}
static int64_t
extract_evuimm8_ex0 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t value = ((insn >> 8) & 0xf8);
if (value == 0)
*invalid = 1;
return value;
}
static uint64_t
insert_evuimm_lt8 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if (value < 0 || value > 7)
*errmsg = _("UIMM values >7 are illegal");
return insn | ((value & 0x7) << 11);
}
static int64_t
extract_evuimm_lt8 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t value = ((insn >> 11) & 0x1f);
if (value > 7)
*invalid = 1;
return value;
}
static uint64_t
insert_evuimm_lt16 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if (value < 0 || value > 15)
*errmsg = _("UIMM values >15 are illegal");
return insn | ((value & 0xf) << 11);
}
static int64_t
extract_evuimm_lt16 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t value = ((insn >> 11) & 0x1f);
if (value > 15)
*invalid = 1;
return value;
}
static uint64_t
insert_rD_rS_even (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if ((value & 0x1) != 0)
*errmsg = _("GPR odd is illegal");
return insn | ((value & 0x1e) << 21);
}
static int64_t
extract_rD_rS_even (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t value = ((insn >> 21) & 0x1f);
if ((value & 0x1) != 0)
*invalid = 1;
return value;
}
static uint64_t
insert_off_lsp (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if (value <= 0 || value > 0x3)
*errmsg = _("invalid offset");
return insn | (value & 0x3);
}
static int64_t
extract_off_lsp (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t value = (insn & 0x3);
if (value == 0)
*invalid = 1;
return value;
}
static uint64_t
insert_off_spe2 (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if (value <= 0 || value > 0x7)
*errmsg = _("invalid offset");
return insn | (value & 0x7);
}
static int64_t
extract_off_spe2 (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
int64_t value = (insn & 0x7);
if (value == 0)
*invalid = 1;
return value;
}
static uint64_t
insert_Ddd (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if (value < 0 || value > 0x7)
*errmsg = _("invalid Ddd value");
return insn | ((value & 0x3) << 11) | ((value & 0x4) >> 2);
}
static int64_t
extract_Ddd (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid ATTRIBUTE_UNUSED)
{
return ((insn >> 11) & 0x3) | ((insn << 2) & 0x4);
}
static uint64_t
insert_sxl (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg ATTRIBUTE_UNUSED)
{
return insn | ((value & 0x1) << 11);
}
static int64_t
extract_sxl (uint64_t insn,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
int *invalid)
{
/* Missing optional operands have a value of one. */
if (*invalid < 0)
return 1;
return (insn >> 11) & 0x1;
}
/* The list of embedded processors that use the embedded operand ordering
for the 3 operand dcbt and dcbtst instructions. */
#define DCBT_EO (PPC_OPCODE_E500 | PPC_OPCODE_E500MC | PPC_OPCODE_476 \
| PPC_OPCODE_A2)
/* ISA 2.03 and later specify extended mnemonics dcbtct, dcbtds, and
dcbtstct, dcbtstds with a note saying these should be used in new
programs rather than the base mnemonics "so that it can be coded
with TH as the last operand for all categories". For that reason
the extended mnemonics are enabled in the assembler for the
embedded processors, but not for the disassembler so as to display
the embedded dcbt or dcbtst expected form with TH first for
embedded programmers. */
static uint64_t
insert_thct (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if ((uint64_t) value > 7)
*errmsg = _("invalid TH value");
return insn | ((value & 7) << 21);
}
static int64_t
extract_thct (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
/* Missing optional operands have a value of 0. */
if (*invalid < 0)
return 0;
int64_t value = (insn >> 21) & 0x1f;
if (value > 7 || (dialect & DCBT_EO) != 0)
*invalid = 1;
return value;
}
static uint64_t
insert_thds (uint64_t insn,
int64_t value,
ppc_cpu_t dialect ATTRIBUTE_UNUSED,
const char **errmsg)
{
if (value < 8 || value > 15)
*errmsg = _("invalid TH value");
return insn | ((value & 0x1f) << 21);
}
static int64_t
extract_thds (uint64_t insn,
ppc_cpu_t dialect,
int *invalid)
{
/* Missing optional operands have a value of 8. */
if (*invalid < 0)
return 8;
int64_t value = (insn >> 21) & 0x1f;
if (value < 8 || value > 15 || (dialect & DCBT_EO) != 0)
*invalid = 1;
return value;
}
/* The operands table.
The fields are bitm, shift, insert, extract, flags.
We used to put parens around the various additions, like the one
for BA just below. However, that caused trouble with feeble
compilers with a limit on depth of a parenthesized expression, like
(reportedly) the compiler in Microsoft Developer Studio 5. So we
omit the parens, since the macros are never used in a context where
the addition will be ambiguous. */
const struct powerpc_operand powerpc_operands[] =
{
/* The zero index is used to indicate the end of the list of
operands. */
#define UNUSED 0
{ 0, 0, NULL, NULL, 0 },
/* The BA field in an XL form instruction. */
#define BA UNUSED + 1
/* The BI field in a B form or XL form instruction. */
#define BI BA
#define BI_MASK (0x1f << 16)
{ 0x1f, 16, NULL, NULL, PPC_OPERAND_CR_BIT },
/* The BT, BA and BB fields in a XL form instruction when they must all
be the same. */
#define BTAB BA + 1
{ 0x1f, 21, insert_btab, extract_btab, PPC_OPERAND_CR_BIT },
/* The BB field in an XL form instruction. */
#define BB BTAB + 1
#define BB_MASK (0x1f << 11)
{ 0x1f, 11, NULL, NULL, PPC_OPERAND_CR_BIT },
/* The BA and BB fields in a XL form instruction when they must be
the same. */
#define BAB BB + 1
{ 0x1f, 16, insert_bab, extract_bab, PPC_OPERAND_CR_BIT },
/* The VRA and VRB fields in a VX form instruction when they must be the same.
This is used for extended mnemonics like vmr. */
#define VAB BAB + 1
{ 0x1f, 16, insert_bab, extract_bab, PPC_OPERAND_VR },
/* The RA and RB fields in a VX form instruction when they must be the same.
This is used for extended mnemonics like evmr. */
#define RAB VAB + 1
{ 0x1f, 16, insert_bab, extract_bab, PPC_OPERAND_GPR },
#define BC RAB + 1
{ 0x1f, 6, NULL, NULL, PPC_OPERAND_CR_BIT },
/* The BD field in a B form instruction. The lower two bits are
forced to zero. */
#define BD BC + 1
{ 0xfffc, 0, NULL, NULL, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
/* The BD field in a B form instruction when absolute addressing is
used. */
#define BDA BD + 1
{ 0xfffc, 0, NULL, NULL, PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
/* The BD field in a B form instruction when the - modifier is used.
This sets the y bit of the BO field appropriately. */
#define BDM BDA + 1
{ 0xfffc, 0, insert_bdm, extract_bdm,
PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
/* The BD field in a B form instruction when the - modifier is used
and absolute address is used. */
#define BDMA BDM + 1
{ 0xfffc, 0, insert_bdm, extract_bdm,
PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
/* The BD field in a B form instruction when the + modifier is used.
This sets the y bit of the BO field appropriately. */
#define BDP BDMA + 1
{ 0xfffc, 0, insert_bdp, extract_bdp,
PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
/* The BD field in a B form instruction when the + modifier is used
and absolute addressing is used. */
#define BDPA BDP + 1
{ 0xfffc, 0, insert_bdp, extract_bdp,
PPC_OPERAND_ABSOLUTE | PPC_OPERAND_SIGNED },
/* The BF field in an X or XL form instruction. */
#define BF BDPA + 1
/* The CRFD field in an X form instruction. */
#define CRFD BF
/* The CRD field in an XL form instruction. */
#define CRD BF
{ 0x7, 23, NULL, NULL, PPC_OPERAND_CR_REG },
/* The BF field in an X or XL form instruction. */
#define BFF BF + 1
{ 0x7, 23, NULL, NULL, 0 },
/* The ACC field in a VSX ACC 8LS:D-form instruction. */
#define ACC BFF + 1
{ 0x7, 23, NULL, NULL, PPC_OPERAND_ACC },
/* The DMR field in a MMA instruction. */
#define DMR ACC + 1
{ 0x7, 23, NULL, NULL, PPC_OPERAND_DMR },
/* The second DMR field in a two DMR operand MMA instruction. */
#define DMRAB DMR + 1
{ 0x7, 13, NULL, NULL, PPC_OPERAND_DMR },
/* An optional BF field. This is used for comparison instructions,
in which an omitted BF field is taken as zero. */
#define OBF DMRAB + 1
{ 0x7, 23, NULL, NULL, PPC_OPERAND_CR_REG | PPC_OPERAND_OPTIONAL },
/* The BFA field in an X or XL form instruction. */
#define BFA OBF + 1
{ 0x7, 18, NULL, NULL, PPC_OPERAND_CR_REG },
/* The BO field in a B form instruction. Certain values are
illegal. */
#define BO BFA + 1
#define BO_MASK (0x1f << 21)
{ 0x1f, 21, insert_bo, extract_bo, 0 },
/* The BO field in a B form instruction when the - modifier is used. */
#define BOM BO + 1
{ 0x1f, 21, insert_bom, extract_bom, 0 },
/* The BO field in a B form instruction when the + modifier is used. */
#define BOP BOM + 1
{ 0x1f, 21, insert_bop, extract_bop, 0 },
/* The RM field in an X form instruction. */
#define RM BOP + 1
#define DD RM
#define mo1 RM
{ 0x3, 11, NULL, NULL, 0 },
#define BH RM + 1
{ 0x3, 11, NULL, NULL, PPC_OPERAND_OPTIONAL },
/* The BT field in an X or XL form instruction. */
#define BT BH + 1
{ 0x1f, 21, NULL, NULL, PPC_OPERAND_CR_BIT },
/* The BT field in a mtfsb0 or mtfsb1 instruction. */
#define BTF BT + 1
{ 0x1f, 21, NULL, NULL, PPC_OPERAND_CR_BIT | PPC_OPERAND_CR_REG },
/* The BI16 field in a BD8 form instruction. */
#define BI16 BTF + 1
{ 0x3, 8, NULL, NULL, PPC_OPERAND_CR_BIT },
/* The BI32 field in a BD15 form instruction. */
#define BI32 BI16 + 1
{ 0xf, 16, NULL, NULL, PPC_OPERAND_CR_BIT },
/* The BO32 field in a BD15 form instruction. */
#define BO32 BI32 + 1
{ 0x3, 20, NULL, NULL, 0 },
/* The B8 field in a BD8 form instruction. */
#define B8 BO32 + 1
{ 0x1fe, -1, NULL, NULL, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
/* The B15 field in a BD15 form instruction. The lowest bit is
forced to zero. */
#define B15 B8 + 1
{ 0xfffe, 0, NULL, NULL, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
/* The B24 field in a BD24 form instruction. The lowest bit is
forced to zero. */
#define B24 B15 + 1
{ 0x1fffffe, 0, NULL, NULL, PPC_OPERAND_RELATIVE | PPC_OPERAND_SIGNED },
/* The condition register number portion of the BI field in a B form
or XL form instruction. This is used for the extended
conditional branch mnemonics, which set the lower two bits of the
BI field. This field is optional. */
#define CR B24 + 1
{ 0x7, 18, NULL, NULL, PPC_OPERAND_CR_REG | PPC_OPERAND_OPTIONAL },
/* The CRB field in an X form instruction. */
#define CRB CR + 1
/* The MB field in an M form instruction. */
#define MB CRB
#define MB_MASK (0x1f << 6)
{ 0x1f, 6, NULL, NULL, 0 },
/* The CRD32 field in an XL form instruction. */
#define CRD32 CRB + 1
{ 0x3, 21, NULL, NULL, PPC_OPERAND_CR_REG },
/* The CRFS field in an X form instruction. */
#define CRFS CRD32 + 1
{ 0x7, 0, NULL, NULL, PPC_OPERAND_CR_REG },
#define CRS CRFS + 1
{ 0x3, 18, NULL, NULL, PPC_OPERAND_CR_REG | PPC_OPERAND_OPTIONAL },
/* The CT field in an X form instruction. */
#define CT CRS + 1
/* The MO field in an mbar instruction. */
#define MO CT
{ 0x1f, 21, NULL, NULL, PPC_OPERAND_OPTIONAL },
/* The TH field in dcbtct. */
#define THCT CT + 1
{ 0x1f, 21, insert_thct, extract_thct, PPC_OPERAND_OPTIONAL },
/* The TH field in dcbtds. */
#define THDS THCT + 1
{ 0x1f, 21, insert_thds, extract_thds, PPC_OPERAND_OPTIONAL },
/* The D field in a D form instruction. This is a displacement off
a register, and implies that the next operand is a register in
parentheses. */
#define D THDS + 1
{ 0xffff, 0, NULL, NULL, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED },
/* The D8 field in a D form instruction. This is a displacement off
a register, and implies that the next operand is a register in
parentheses. */
#define D8 D + 1
{ 0xff, 0, NULL, NULL, PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED },
/* The DCMX field in an X form instruction. */
#define DCMX D8 + 1
{ 0x7f, 16, NULL, NULL, 0 },
/* The split DCMX field in an X form instruction. */
#define DCMXS DCMX + 1
{ 0x7f, PPC_OPSHIFT_INV, insert_dcmxs, extract_dcmxs, 0 },
/* The DQ field in a DQ form instruction. This is like D, but the
lower four bits are forced to zero. */
#define DQ DCMXS + 1
{ 0xfff0, 0, NULL, NULL,
PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED | PPC_OPERAND_DQ },
/* The DS field in a DS form instruction. This is like D, but the
lower two bits are forced to zero. */
#define DS DQ + 1
{ 0xfffc, 0, NULL, NULL,
PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED | PPC_OPERAND_DS },
/* The D field in an 8-byte D form prefix instruction. This is a displacement
off a register, and implies that the next operand is a register in
parentheses. */
#define D34 DS + 1
{ UINT64_C(0x3ffffffff), PPC_OPSHIFT_INV, insert_d34, extract_d34,
PPC_OPERAND_PARENS | PPC_OPERAND_SIGNED },
/* The SI field in an 8-byte D form prefix instruction. */
#define SI34 D34 + 1
{ UINT64_C(0x3ffffffff), PPC_OPSHIFT_INV, insert_d34, extract_d34, PPC_OPERAND_SIGNED },
/* The NSI field in an 8-byte D form prefix instruction. This is the
same as the SI34 field, only negated. */
#define NSI34 SI34 + 1
{ UINT64_C(0x3ffffffff), PPC_OPSHIFT_INV, insert_nsi34, extract_nsi34,
PPC_OPERAND_NEGATIVE | PPC_OPERAND_SIGNED },
/* The IMM32 field in a vector splat immediate prefix instruction. */
#define IMM32 NSI34 + 1
{ 0xffffffff, PPC_OPSHIFT_INV, insert_imm32, extract_imm32, 0},
/* The UIM field in a vector permute extended prefix instruction. */
#define UIM3 IMM32 + 1
{ 0x7, 32, NULL, NULL, 0},