| /* 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}, |
|