diff options
36 files changed, 9842 insertions, 77 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index ad1000415d..60b0b0922c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -132,6 +132,12 @@ F: include/hw/cris/ F: tests/tcg/cris/ F: disas/cris.c +HPPA (PA-RISC) +M: Richard Henderson <rth@twiddle.net> +S: Maintained +F: target/hppa/ +F: disas/hppa.c + LM32 M: Michael Walle <michael@walle.cc> S: Maintained @@ -5843,7 +5843,7 @@ target_name=$(echo $target | cut -d '-' -f 1) target_bigendian="no" case "$target_name" in - armeb|lm32|m68k|microblaze|mips|mipsn32|mips64|moxie|or32|ppc|ppcemb|ppc64|ppc64abi32|s390x|sh4eb|sparc|sparc64|sparc32plus|xtensaeb) + armeb|hppa|lm32|m68k|microblaze|mips|mipsn32|mips64|moxie|or32|ppc|ppcemb|ppc64|ppc64abi32|s390x|sh4eb|sparc|sparc64|sparc32plus|xtensaeb) target_bigendian=yes ;; esac @@ -5906,6 +5906,8 @@ case "$target_name" in ;; cris) ;; + hppa) + ;; lm32) ;; m68k) @@ -6114,6 +6116,9 @@ for i in $ARCH $TARGET_BASE_ARCH ; do cris) disas_config "CRIS" ;; + hppa) + disas_config "HPPA" + ;; i386|x86_64|x32) disas_config "I386" ;; diff --git a/default-configs/hppa-linux-user.mak b/default-configs/hppa-linux-user.mak new file mode 100644 index 0000000000..796393940b --- /dev/null +++ b/default-configs/hppa-linux-user.mak @@ -0,0 +1 @@ +# Default configuration for hppa-linux-user @@ -310,6 +310,8 @@ void disas(FILE *out, void *code, unsigned long size) print_insn = print_insn_m68k; #elif defined(__s390__) print_insn = print_insn_s390; +#elif defined(__hppa__) + print_insn = print_insn_hppa; #elif defined(__ia64__) print_insn = print_insn_ia64; #endif diff --git a/disas/Makefile.objs b/disas/Makefile.objs index 09bc992883..abeba84661 100644 --- a/disas/Makefile.objs +++ b/disas/Makefile.objs @@ -9,6 +9,7 @@ libvixldir = $(SRC_PATH)/disas/libvixl # versions do not. arm-a64.o-cflags := -I$(libvixldir) -Wno-sign-compare common-obj-$(CONFIG_CRIS_DIS) += cris.o +common-obj-$(CONFIG_HPPA_DIS) += hppa.o common-obj-$(CONFIG_I386_DIS) += i386.o common-obj-$(CONFIG_IA64_DIS) += ia64.o common-obj-$(CONFIG_M68K_DIS) += m68k.o diff --git a/disas/hppa.c b/disas/hppa.c new file mode 100644 index 0000000000..43facdc47b --- /dev/null +++ b/disas/hppa.c @@ -0,0 +1,2832 @@ +/* Disassembler for the PA-RISC. Somewhat derived from sparc-pinsn.c. + Copyright 1989, 1990, 1992, 1993, 1994, 1995, 1998, 1999, 2000, 2001, 2003, + 2005 Free Software Foundation, Inc. + + Contributed by the Center for Software Science at the + University of Utah (pa-gdb-bugs@cs.utah.edu). + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. */ + +#include "qemu/osdep.h" +#include "disas/bfd.h" + +/* HP PA-RISC SOM object file format: definitions internal to BFD. + Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, + 2003 Free Software Foundation, Inc. + + Contributed by the Center for Software Science at the + University of Utah (pa-gdb-bugs@cs.utah.edu). + + This file is part of BFD, the Binary File Descriptor library. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. */ + +#ifndef _LIBHPPA_H +#define _LIBHPPA_H + +#define BYTES_IN_WORD 4 +#define PA_PAGESIZE 0x1000 + +/* The PA instruction set variants. */ +enum pa_arch {pa10 = 10, pa11 = 11, pa20 = 20, pa20w = 25}; + +/* HP PA-RISC relocation types */ + +enum hppa_reloc_field_selector_type + { + R_HPPA_FSEL = 0x0, + R_HPPA_LSSEL = 0x1, + R_HPPA_RSSEL = 0x2, + R_HPPA_LSEL = 0x3, + R_HPPA_RSEL = 0x4, + R_HPPA_LDSEL = 0x5, + R_HPPA_RDSEL = 0x6, + R_HPPA_LRSEL = 0x7, + R_HPPA_RRSEL = 0x8, + R_HPPA_NSEL = 0x9, + R_HPPA_NLSEL = 0xa, + R_HPPA_NLRSEL = 0xb, + R_HPPA_PSEL = 0xc, + R_HPPA_LPSEL = 0xd, + R_HPPA_RPSEL = 0xe, + R_HPPA_TSEL = 0xf, + R_HPPA_LTSEL = 0x10, + R_HPPA_RTSEL = 0x11, + R_HPPA_LTPSEL = 0x12, + R_HPPA_RTPSEL = 0x13 + }; + +/* /usr/include/reloc.h defines these to constants. We want to use + them in enums, so #undef them before we start using them. We might + be able to fix this another way by simply managing not to include + /usr/include/reloc.h, but currently GDB picks up these defines + somewhere. */ +#undef e_fsel +#undef e_lssel +#undef e_rssel +#undef e_lsel +#undef e_rsel +#undef e_ldsel +#undef e_rdsel +#undef e_lrsel +#undef e_rrsel +#undef e_nsel +#undef e_nlsel +#undef e_nlrsel +#undef e_psel +#undef e_lpsel +#undef e_rpsel +#undef e_tsel +#undef e_ltsel +#undef e_rtsel +#undef e_one +#undef e_two +#undef e_pcrel +#undef e_con +#undef e_plabel +#undef e_abs + +/* for compatibility */ +enum hppa_reloc_field_selector_type_alt + { + e_fsel = R_HPPA_FSEL, + e_lssel = R_HPPA_LSSEL, + e_rssel = R_HPPA_RSSEL, + e_lsel = R_HPPA_LSEL, + e_rsel = R_HPPA_RSEL, + e_ldsel = R_HPPA_LDSEL, + e_rdsel = R_HPPA_RDSEL, + e_lrsel = R_HPPA_LRSEL, + e_rrsel = R_HPPA_RRSEL, + e_nsel = R_HPPA_NSEL, + e_nlsel = R_HPPA_NLSEL, + e_nlrsel = R_HPPA_NLRSEL, + e_psel = R_HPPA_PSEL, + e_lpsel = R_HPPA_LPSEL, + e_rpsel = R_HPPA_RPSEL, + e_tsel = R_HPPA_TSEL, + e_ltsel = R_HPPA_LTSEL, + e_rtsel = R_HPPA_RTSEL, + e_ltpsel = R_HPPA_LTPSEL, + e_rtpsel = R_HPPA_RTPSEL + }; + +enum hppa_reloc_expr_type + { + R_HPPA_E_ONE = 0, + R_HPPA_E_TWO = 1, + R_HPPA_E_PCREL = 2, + R_HPPA_E_CON = 3, + R_HPPA_E_PLABEL = 7, + R_HPPA_E_ABS = 18 + }; + +/* for compatibility */ +enum hppa_reloc_expr_type_alt + { + e_one = R_HPPA_E_ONE, + e_two = R_HPPA_E_TWO, + e_pcrel = R_HPPA_E_PCREL, + e_con = R_HPPA_E_CON, + e_plabel = R_HPPA_E_PLABEL, + e_abs = R_HPPA_E_ABS + }; + + +/* Relocations for function calls must be accompanied by parameter + relocation bits. These bits describe exactly where the caller has + placed the function's arguments and where it expects to find a return + value. + + Both ELF and SOM encode this information within the addend field + of the call relocation. (Note this could break very badly if one + was to make a call like bl foo + 0x12345678). + + The high order 10 bits contain parameter relocation information, + the low order 22 bits contain the constant offset. */ + +#define HPPA_R_ARG_RELOC(a) \ + (((a) >> 22) & 0x3ff) +#define HPPA_R_CONSTANT(a) \ + ((((bfd_signed_vma)(a)) << (BFD_ARCH_SIZE-22)) >> (BFD_ARCH_SIZE-22)) +#define HPPA_R_ADDEND(r, c) \ + (((r) << 22) + ((c) & 0x3fffff)) + + +/* Some functions to manipulate PA instructions. */ + +/* Declare the functions with the unused attribute to avoid warnings. */ +static inline int sign_extend (int, int) ATTRIBUTE_UNUSED; +static inline int low_sign_extend (int, int) ATTRIBUTE_UNUSED; +static inline int sign_unext (int, int) ATTRIBUTE_UNUSED; +static inline int low_sign_unext (int, int) ATTRIBUTE_UNUSED; +static inline int re_assemble_3 (int) ATTRIBUTE_UNUSED; +static inline int re_assemble_12 (int) ATTRIBUTE_UNUSED; +static inline int re_assemble_14 (int) ATTRIBUTE_UNUSED; +static inline int re_assemble_16 (int) ATTRIBUTE_UNUSED; +static inline int re_assemble_17 (int) ATTRIBUTE_UNUSED; +static inline int re_assemble_21 (int) ATTRIBUTE_UNUSED; +static inline int re_assemble_22 (int) ATTRIBUTE_UNUSED; +static inline bfd_signed_vma hppa_field_adjust + (bfd_vma, bfd_signed_vma, enum hppa_reloc_field_selector_type_alt) + ATTRIBUTE_UNUSED; +static inline int hppa_rebuild_insn (int, int, int) ATTRIBUTE_UNUSED; + + +/* The *sign_extend functions are used to assemble various bitfields + taken from an instruction and return the resulting immediate + value. */ + +static inline int +sign_extend (int x, int len) +{ + int signbit = (1 << (len - 1)); + int mask = (signbit << 1) - 1; + return ((x & mask) ^ signbit) - signbit; +} + +static inline int +low_sign_extend (int x, int len) +{ + return (x >> 1) - ((x & 1) << (len - 1)); +} + + +/* The re_assemble_* functions prepare an immediate value for + insertion into an opcode. pa-risc uses all sorts of weird bitfields + in the instruction to hold the value. */ + +static inline int +sign_unext (int x, int len) +{ + int len_ones; + + len_ones = (1 << len) - 1; + + return x & len_ones; +} + +static inline int +low_sign_unext (int x, int len) +{ + int temp; + int sign; + + sign = (x >> (len-1)) & 1; + + temp = sign_unext (x, len-1); + + return (temp << 1) | sign; +} + +static inline int +re_assemble_3 (int as3) +{ + return (( (as3 & 4) << (13-2)) + | ((as3 & 3) << (13+1))); +} + +static inline int +re_assemble_12 (int as12) +{ + return (( (as12 & 0x800) >> 11) + | ((as12 & 0x400) >> (10 - 2)) + | ((as12 & 0x3ff) << (1 + 2))); +} + +static inline int +re_assemble_14 (int as14) +{ + return (( (as14 & 0x1fff) << 1) + | ((as14 & 0x2000) >> 13)); +} + +static inline int +re_assemble_16 (int as16) +{ + int s, t; + + /* Unusual 16-bit encoding, for wide mode only. */ + t = (as16 << 1) & 0xffff; + s = (as16 & 0x8000); + return (t ^ s ^ (s >> 1)) | (s >> 15); +} + +static inline int +re_assemble_17 (int as17) +{ + return (( (as17 & 0x10000) >> 16) + | ((as17 & 0x0f800) << (16 - 11)) + | ((as17 & 0x00400) >> (10 - 2)) + | ((as17 & 0x003ff) << (1 + 2))); +} + +static inline int +re_assemble_21 (int as21) +{ + return (( (as21 & 0x100000) >> 20) + | ((as21 & 0x0ffe00) >> 8) + | ((as21 & 0x000180) << 7) + | ((as21 & 0x00007c) << 14) + | ((as21 & 0x000003) << 12)); +} + +static inline int +re_assemble_22 (int as22) +{ + return (( (as22 & 0x200000) >> 21) + | ((as22 & 0x1f0000) << (21 - 16)) + | ((as22 & 0x00f800) << (16 - 11)) + | ((as22 & 0x000400) >> (10 - 2)) + | ((as22 & 0x0003ff) << (1 + 2))); +} + + +/* Handle field selectors for PA instructions. + The L and R (and LS, RS etc.) selectors are used in pairs to form a + full 32 bit address. eg. + + LDIL L'start,%r1 ; put left part into r1 + LDW R'start(%r1),%r2 ; add r1 and right part to form address + + This function returns sign extended values in all cases. +*/ + +static inline bfd_signed_vma +hppa_field_adjust (bfd_vma sym_val, + bfd_signed_vma addend, + enum hppa_reloc_field_selector_type_alt r_field) +{ + bfd_signed_vma value; + + value = sym_val + addend; + switch (r_field) + { + case e_fsel: + /* F: No change. */ + break; + + case e_nsel: + /* N: null selector. I don't really understand what this is all + about, but HP's documentation says "this indicates that zero + bits are to be used for the displacement on the instruction. + This fixup is used to identify three-instruction sequences to + access data (for importing shared library data)." */ + value = 0; + break; + + case e_lsel: + case e_nlsel: + /* L: Select top 21 bits. */ + value = value >> 11; + break; + + case e_rsel: + /* R: Select bottom 11 bits. */ + value = value & 0x7ff; + break; + + case e_lssel: + /* LS: Round to nearest multiple of 2048 then select top 21 bits. */ + value = value + 0x400; + value = value >> 11; + break; + + case e_rssel: + /* RS: Select bottom 11 bits for LS. + We need to return a value such that 2048 * LS'x + RS'x == x. + ie. RS'x = x - ((x + 0x400) & -0x800) + this is just a sign extension from bit 21. */ + value = ((value & 0x7ff) ^ 0x400) - 0x400; + break; + + case e_ldsel: + /* LD: Round to next multiple of 2048 then select top 21 bits. + Yes, if we are already on a multiple of 2048, we go up to the + next one. RD in this case will be -2048. */ + value = value + 0x800; + value = value >> 11; + break; + + case e_rdsel: + /* RD: Set bits 0-20 to one. */ + value = value | -0x800; + break; + + case e_lrsel: + case e_nlrsel: + /* LR: L with rounding of the addend to nearest 8k. */ + value = sym_val + ((addend + 0x1000) & -0x2000); + value = value >> 11; + break; + + case e_rrsel: + /* RR: R with rounding of the addend to nearest 8k. + We need to return a value such that 2048 * LR'x + RR'x == x + ie. RR'x = s+a - (s + (((a + 0x1000) & -0x2000) & -0x800)) + . = s+a - ((s & -0x800) + ((a + 0x1000) & -0x2000)) + . = (s & 0x7ff) + a - ((a + 0x1000) & -0x2000) */ + value = (sym_val & 0x7ff) + (((addend & 0x1fff) ^ 0x1000) - 0x1000); + break; + + default: + abort (); + } + return value; +} + +/* PA-RISC OPCODES */ +#define get_opcode(insn) (((insn) >> 26) & 0x3f) + +enum hppa_opcode_type +{ + /* None of the opcodes in the first group generate relocs, so we + aren't too concerned about them. */ + OP_SYSOP = 0x00, + OP_MEMMNG = 0x01, + OP_ALU = 0x02, + OP_NDXMEM = 0x03, + OP_SPOP = 0x04, + OP_DIAG = 0x05, + OP_FMPYADD = 0x06, + OP_UNDEF07 = 0x07, + OP_COPRW = 0x09, + OP_COPRDW = 0x0b, + OP_COPR = 0x0c, + OP_FLOAT = 0x0e, + OP_PRDSPEC = 0x0f, + OP_UNDEF15 = 0x15, + OP_UNDEF1d = 0x1d, + OP_FMPYSUB = 0x26, + OP_FPFUSED = 0x2e, + OP_SHEXDP0 = 0x34, + OP_SHEXDP1 = 0x35, + OP_SHEXDP2 = 0x36, + OP_UNDEF37 = 0x37, + OP_SHEXDP3 = 0x3c, + OP_SHEXDP4 = 0x3d, + OP_MULTMED = 0x3e, + OP_UNDEF3f = 0x3f, + + OP_LDIL = 0x08, + OP_ADDIL = 0x0a, + + OP_LDO = 0x0d, + OP_LDB = 0x10, + OP_LDH = 0x11, + OP_LDW = 0x12, + OP_LDWM = 0x13, + OP_STB = 0x18, + OP_STH = 0x19, + OP_STW = 0x1a, + OP_STWM = 0x1b, + + OP_LDD = 0x14, + OP_STD = 0x1c, + + OP_FLDW = 0x16, + OP_LDWL = 0x17, + OP_FSTW = 0x1e, + OP_STWL = 0x1f, + + OP_COMBT = 0x20, + OP_COMIBT = 0x21, + OP_COMBF = 0x22, + OP_COMIBF = 0x23, + OP_CMPBDT = 0x27, + OP_ADDBT = 0x28, + OP_ADDIBT = 0x29, + OP_ADDBF = 0x2a, + OP_ADDIBF = 0x2b, + OP_CMPBDF = 0x2f, + OP_BVB = 0x30, + OP_BB = 0x31, + OP_MOVB = 0x32, + OP_MOVIB = 0x33, + OP_CMPIBD = 0x3b, + + OP_COMICLR = 0x24, + OP_SUBI = 0x25, + OP_ADDIT = 0x2c, + OP_ADDI = 0x2d, + + OP_BE = 0x38, + OP_BLE = 0x39, + OP_BL = 0x3a +}; + + +/* Insert VALUE into INSN using R_FORMAT to determine exactly what + bits to change. */ + +static inline int +hppa_rebuild_insn (int insn, int value, int r_format) +{ + switch (r_format) + { + case 11: + return (insn & ~ 0x7ff) | low_sign_unext (value, 11); + + case 12: + return (insn & ~ 0x1ffd) | re_assemble_12 (value); + + + case 10: + return (insn & ~ 0x3ff1) | re_assemble_14 (value & -8); + + case -11: + return (insn & ~ 0x3ff9) | re_assemble_14 (value & -4); + + case 14: + return (insn & ~ 0x3fff) | re_assemble_14 (value); + + + case -10: + return (insn & ~ 0xfff1) | re_assemble_16 (value & -8); + + case -16: + return (insn & ~ 0xfff9) | re_assemble_16 (value & -4); + + case 16: + return (insn & ~ 0xffff) | re_assemble_16 (value); + + + case 17: + return (insn & ~ 0x1f1ffd) | re_assemble_17 (value); + + case 21: + return (insn & ~ 0x1fffff) | re_assemble_21 (value); + + case 22: + return (insn & ~ 0x3ff1ffd) | re_assemble_22 (value); + + case 32: + return value; + + default: + abort (); + } + return insn; +} + +#endif /* _LIBHPPA_H */ +/* Table of opcodes for the PA-RISC. + Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, + 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + + Contributed by the Center for Software Science at the + University of Utah (pa-gdb-bugs@cs.utah.edu). + +This file is part of GAS, the GNU Assembler, and GDB, the GNU disassembler. + +GAS/GDB 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 1, or (at your option) +any later version. + +GAS/GDB 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 GAS or GDB; see the file COPYING. +If not, see <http://www.gnu.org/licenses/>. */ + +#if !defined(__STDC__) && !defined(const) +#define const +#endif + +/* + * Structure of an opcode table entry. + */ + +/* There are two kinds of delay slot nullification: normal which is + * controlled by the nullification bit, and conditional, which depends + * on the direction of the branch and its success or failure. + * + * NONE is unfortunately #defined in the hiux system include files. + * #undef it away. + */ +#undef NONE +struct pa_opcode +{ + const char *name; + unsigned long int match; /* Bits that must be set... */ + unsigned long int mask; /* ... in these bits. */ + const char *args; + enum pa_arch arch; + char flags; +}; + +/* Enables strict matching. Opcodes with match errors are skipped + when this bit is set. */ +#define FLAG_STRICT 0x1 + +/* + All hppa opcodes are 32 bits. + + The match component is a mask saying which bits must match a + particular opcode in order for an instruction to be an instance + of that opcode. + + The args component is a string containing one character for each operand of + the instruction. Characters used as a prefix allow any second character to + be used without conflicting with the main operand characters. + + Bit positions in this description follow HP usage of lsb = 31, + "at" is lsb of field. + + In the args field, the following characters must match exactly: + + '+,() ' + + In the args field, the following characters are unused: + + ' " - / 34 6789:; ' + '@ C M [\] ' + '` e g } ' + + Here are all the characters: + + ' !"#$%&'()*+-,./0123456789:;<=>?' + '@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_' + '`abcdefghijklmnopqrstuvwxyz{|}~ ' + +Kinds of operands: + x integer register field at 15. + b integer register field at 10. + t integer register field at 31. + a integer register field at 10 and 15 (for PERMH) + 5 5 bit immediate at 15. + s 2 bit space specifier at 17. + S 3 bit space specifier at 18. + V 5 bit immediate value at 31 + i 11 bit immediate value at 31 + j 14 bit immediate value at 31 + k 21 bit immediate value at 31 + l 16 bit immediate value at 31 (wide mode only, unusual encoding). + n nullification for branch instructions + N nullification for spop and copr instructions + w 12 bit branch displacement + W 17 bit branch displacement (PC relative) + X 22 bit branch displacement (PC relative) + z 17 bit branch displacement (just a number, not an address) + +Also these: + + . 2 bit shift amount at 25 + * 4 bit shift amount at 25 + p 5 bit shift count at 26 (to support the SHD instruction) encoded as + 31-p + ~ 6 bit shift count at 20,22:26 encoded as 63-~. + P 5 bit bit position at 26 + q 6 bit bit position at 20,22:26 + T 5 bit field length at 31 (encoded as 32-T) + % 6 bit field length at 23,27:31 (variable extract/deposit) + | 6 bit field length at 19,27:31 (fixed extract/deposit) + A 13 bit immediate at 18 (to support the BREAK instruction) + ^ like b, but describes a control register + ! sar (cr11) register + D 26 bit immediate at 31 (to support the DIAG instruction) + $ 9 bit immediate at 28 (to support POPBTS) + + v 3 bit Special Function Unit identifier at 25 + O 20 bit Special Function Unit operation split between 15 bits at 20 + and 5 bits at 31 + o 15 bit Special Function Unit operation at 20 + 2 22 bit Special Function Unit operation split between 17 bits at 20 + and 5 bits at 31 + 1 15 bit Special Function Unit operation split between 10 bits at 20 + and 5 bits at 31 + 0 10 bit Special Function Unit operation split between 5 bits at 20 + and 5 bits at 31 + u 3 bit coprocessor unit identifier at 25 + F Source Floating Point Operand Format Completer encoded 2 bits at 20 + I Source Floating Point Operand Format Completer encoded 1 bits at 20 + (for 0xe format FP instructions) + G Destination Floating Point Operand Format Completer encoded 2 bits at 18 + H Floating Point Operand Format at 26 for 'fmpyadd' and 'fmpysub' + (very similar to 'F') + + r 5 bit immediate value at 31 (for the break instruction) + (very similar to V above, except the value is unsigned instead of + low_sign_ext) + R 5 bit immediate value at 15 (for the ssm, rsm, probei instructions) + (same as r above, except the value is in a different location) + U 10 bit immediate value at 15 (for SSM, RSM on pa2.0) + Q 5 bit immediate value at 10 (a bit position specified in + the bb instruction. It's the same as r above, except the + value is in a different location) + B 5 bit immediate value at 10 (a bit position specified in + the bb instruction. Similar to Q, but 64 bit handling is + different. + Z %r1 -- implicit target of addil instruction. + L ,%r2 completer for new syntax branch + { Source format completer for fcnv + _ Destination format completer for fcnv + h cbit for fcmp + = gfx tests for ftest + d 14 bit offset for single precision FP long load/store. + # 14 bit offset for double precision FP load long/store. + J Yet another 14 bit offset for load/store with ma,mb completers. + K Yet another 14 bit offset for load/store with ma,mb completers. + y 16 bit offset for word aligned load/store (PA2.0 wide). + & 16 bit offset for dword aligned load/store (PA2.0 wide). + < 16 bit offset for load/store with ma,mb completers (PA2.0 wide). + > 16 bit offset for load/store with ma,mb completers (PA2.0 wide). + Y %sr0,%r31 -- implicit target of be,l instruction. + @ implicit immediate value of 0 + +Completer operands all have 'c' as the prefix: + + cx indexed load and store completer. + cX indexed load and store completer. Like cx, but emits a space + after in disassembler. + cm short load and store completer. + cM short load and store completer. Like cm, but emits a space + after in disassembler. + cq long load and store completer (like cm, but inserted into a + different location in the target instruction). + cs store bytes short completer. + cA store bytes short completer. Like cs, but emits a space + after in disassembler. + ce long load/store completer for LDW/STW with a different encoding + than the others + cc load cache control hint + cd load and clear cache control hint + cC store cache control hint + co ordered access + + cp branch link and push completer + cP branch pop completer + cl branch link completer + cg branch gate completer + + cw read/write completer for PROBE + cW wide completer for MFCTL + cL local processor completer for cache control + cZ System Control Completer (to support LPA, LHA, etc.) + + ci correction completer for DCOR + ca add completer + cy 32 bit add carry completer + cY 64 bit add carry completer + cv signed overflow trap completer + ct trap on condition completer for ADDI, SUB + cT trap on condition completer for UADDCM + cb 32 bit borrow completer for SUB + cB 64 bit borrow completer for SUB + + ch left/right half completer + cH signed/unsigned saturation completer + cS signed/unsigned completer at 21 + cz zero/sign extension completer. + c* permutation completer + +Condition operands all have '?' as the prefix: + + ?f Floating point compare conditions (encoded as 5 bits at 31) + + ?a add conditions + ?A 64 bit add conditions + ?@ add branch conditions followed by nullify + ?d non-negated add branch conditions + ?D negated add branch conditions + ?w wide mode non-negated add branch conditions + ?W wide mode negated add branch conditions + + ?s compare/subtract conditions + ?S 64 bit compare/subtract conditions + ?t non-negated compare and branch conditions + ?n 32 bit compare and branch conditions followed by nullify + ?N 64 bit compare and branch conditions followed by nullify + ?Q 64 bit compare and branch conditions for CMPIB instruction + + ?l logical conditions + ?L 64 bit logical conditions + + ?b branch on bit conditions + ?B 64 bit branch on bit conditions + + ?x shift/extract/deposit conditions + ?X 64 bit shift/extract/deposit conditions + ?y shift/extract/deposit conditions followed by nullify for conditional + branches + + ?u unit conditions + ?U 64 bit unit conditions + +Floating point registers all have 'f' as a prefix: + + ft target register at 31 + fT target register with L/R halves at 31 + fa operand 1 register at 10 + fA operand 1 register with L/R halves at 10 + fX Same as fA, except prints a space before register during disasm + fb operand 2 register at 15 + fB operand 2 register with L/R halves at 15 + fC operand 3 register with L/R halves at 16:18,21:23 + fe Like fT, but encoding is different. + fE Same as fe, except prints a space before register during disasm. + fx target register at 15 (only for PA 2.0 long format FLDD/FSTD). + +Float registers for fmpyadd and fmpysub: + + fi mult operand 1 register at 10 + fj mult operand 2 register at 15 + fk mult target register at 20 + fl add/sub operand register at 25 + fm add/sub target register at 31 + +*/ + + +#if 0 +/* List of characters not to put a space after. Note that + "," is included, as the "spopN" operations use literal + commas in their completer sections. */ +static const char *const completer_chars = ",CcY<>?!@+&U~FfGHINnOoZMadu|/=0123%e$m}"; +#endif + +/* The order of the opcodes in this table is significant: + + * The assembler requires that all instances of the same mnemonic be + consecutive. If they aren't, the assembler will bomb at runtime. + + * Immediate fields use pa_get_absolute_expression to parse the + string. It will generate a "bad expression" error if passed + a register name. Thus, register index variants of an opcode + need to precede immediate variants. + + * The disassembler does not care about the order of the opcodes + except in cases where implicit addressing is used. + + Here are the rules for ordering the opcodes of a mnemonic: + + 1) Opcodes with FLAG_STRICT should precede opcodes without + FLAG_STRICT. + + 2) Opcodes with FLAG_STRICT should be ordered as follows: + register index opcodes, short immediate opcodes, and finally + long immediate opcodes. When both pa10 and pa11 variants + of the same opcode are available, the pa10 opcode should + come first for correct architectural promotion. + + 3) When implicit addressing is available for an opcode, the + implicit opcode should precede the explicit opcode. + + 4) Opcodes without FLAG_STRICT should be ordered as follows: + register index opcodes, long immediate opcodes, and finally + short immediate opcodes. */ + +static const struct pa_opcode pa_opcodes[] = +{ + +/* Pseudo-instructions. */ + +{ "ldi", 0x34000000, 0xffe00000, "l,x", pa20w, 0},/* ldo val(r0),r */ +{ "ldi", 0x34000000, 0xffe0c000, "j,x", pa10, 0},/* ldo val(r0),r */ + +{ "cmpib", 0xec000000, 0xfc000000, "?Qn5,b,w", pa20, FLAG_STRICT}, +{ "cmpib", 0x84000000, 0xf4000000, "?nn5,b,w", pa10, FLAG_STRICT}, +{ "comib", 0x84000000, 0xfc000000, "?nn5,b,w", pa10, 0}, /* comib{tf}*/ +/* This entry is for the disassembler only. It will never be used by + assembler. */ +{ "comib", 0x8c000000, 0xfc000000, "?nn5,b,w", pa10, 0}, /* comib{tf}*/ +{ "cmpb", 0x9c000000, 0xdc000000, "?Nnx,b,w", pa20, FLAG_STRICT}, +{ "cmpb", 0x80000000, 0xf4000000, "?nnx,b,w", pa10, FLAG_STRICT}, +{ "comb", 0x80000000, 0xfc000000, "?nnx,b,w", pa10, 0}, /* comb{tf} */ +/* This entry is for the disassembler only. It will never be used by + assembler. */ +{ "comb", 0x88000000, 0xfc000000, "?nnx,b,w", pa10, 0}, /* comb{tf} */ +{ "addb", 0xa0000000, 0xf4000000, "?Wnx,b,w", pa20w, FLAG_STRICT}, +{ "addb", 0xa0000000, 0xfc000000, "?@nx,b,w", pa10, 0}, /* addb{tf} */ +/* This entry is for the disassembler only. It will never be used by + assembler. */ +{ "addb", 0xa8000000, 0xfc000000, "?@nx,b,w", pa10, 0}, +{ "addib", 0xa4000000, 0xf4000000, "?Wn5,b,w", pa20w, FLAG_STRICT}, +{ "addib", 0xa4000000, 0xfc000000, "?@n5,b,w", pa10, 0}, /* addib{tf}*/ +/* This entry is for the disassembler only. It will never be used by + assembler. */ +{ "addib", 0xac000000, 0xfc000000, "?@n5,b,w", pa10, 0}, /* addib{tf}*/ +{ "nop", 0x08000240, 0xffffffff, "", pa10, 0}, /* or 0,0,0 */ +{ "copy", 0x08000240, 0xffe0ffe0, "x,t", pa10, 0}, /* or r,0,t */ +{ "mtsar", 0x01601840, 0xffe0ffff, "x", pa10, 0}, /* mtctl r,cr11 */ + +/* Loads and Stores for integer registers. */ + +{ "ldd", 0x0c0000c0, 0xfc00d3c0, "cxccx(b),t", pa20, FLAG_STRICT}, +{ "ldd", 0x0c0000c0, 0xfc0013c0, "cxccx(s,b),t", pa20, FLAG_STRICT}, +{ "ldd", 0x0c0010e0, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT}, +{ "ldd", 0x0c0010e0, 0xfc1f33e0, "cocc@(s,b),t", pa20, FLAG_STRICT}, +{ "ldd", 0x0c0010c0, 0xfc00d3c0, "cmcc5(b),t", pa20, FLAG_STRICT}, +{ "ldd", 0x0c0010c0, 0xfc0013c0, "cmcc5(s,b),t", pa20, FLAG_STRICT}, +{ "ldd", 0x50000000, 0xfc000002, "cq&(b),x", pa20w, FLAG_STRICT}, +{ "ldd", 0x50000000, 0xfc00c002, "cq#(b),x", pa20, FLAG_STRICT}, +{ "ldd", 0x50000000, 0xfc000002, "cq#(s,b),x", pa20, FLAG_STRICT}, +{ "ldw", 0x0c000080, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldw", 0x0c000080, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT}, +{ "ldw", 0x0c000080, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT}, +{ "ldw", 0x0c000080, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT}, +{ "ldw", 0x0c0010a0, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT}, +{ "ldw", 0x0c0010a0, 0xfc1f33e0, "cocc@(s,b),t", pa20, FLAG_STRICT}, +{ "ldw", 0x0c001080, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldw", 0x0c001080, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT}, +{ "ldw", 0x0c001080, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT}, +{ "ldw", 0x0c001080, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "ldw", 0x4c000000, 0xfc000000, "ce<(b),x", pa20w, FLAG_STRICT}, +{ "ldw", 0x5c000004, 0xfc000006, "ce>(b),x", pa20w, FLAG_STRICT}, +{ "ldw", 0x48000000, 0xfc000000, "l(b),x", pa20w, FLAG_STRICT}, +{ "ldw", 0x5c000004, 0xfc00c006, "ceK(b),x", pa20, FLAG_STRICT}, +{ "ldw", 0x5c000004, 0xfc000006, "ceK(s,b),x", pa20, FLAG_STRICT}, +{ "ldw", 0x4c000000, 0xfc00c000, "ceJ(b),x", pa10, FLAG_STRICT}, +{ "ldw", 0x4c000000, 0xfc000000, "ceJ(s,b),x", pa10, FLAG_STRICT}, +{ "ldw", 0x48000000, 0xfc00c000, "j(b),x", pa10, 0}, +{ "ldw", 0x48000000, 0xfc000000, "j(s,b),x", pa10, 0}, +{ "ldh", 0x0c000040, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldh", 0x0c000040, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT}, +{ "ldh", 0x0c000040, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT}, +{ "ldh", 0x0c000040, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT}, +{ "ldh", 0x0c001060, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT}, +{ "ldh", 0x0c001060, 0xfc1f33e0, "cocc@(s,b),t", pa20, FLAG_STRICT}, +{ "ldh", 0x0c001040, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldh", 0x0c001040, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT}, +{ "ldh", 0x0c001040, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT}, +{ "ldh", 0x0c001040, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "ldh", 0x44000000, 0xfc000000, "l(b),x", pa20w, FLAG_STRICT}, +{ "ldh", 0x44000000, 0xfc00c000, "j(b),x", pa10, 0}, +{ "ldh", 0x44000000, 0xfc000000, "j(s,b),x", pa10, 0}, +{ "ldb", 0x0c000000, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldb", 0x0c000000, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT}, +{ "ldb", 0x0c000000, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT}, +{ "ldb", 0x0c000000, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT}, +{ "ldb", 0x0c001020, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT}, +{ "ldb", 0x0c001020, 0xfc1f33e0, "cocc@(s,b),t", pa20, FLAG_STRICT}, +{ "ldb", 0x0c001000, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldb", 0x0c001000, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT}, +{ "ldb", 0x0c001000, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT}, +{ "ldb", 0x0c001000, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "ldb", 0x40000000, 0xfc000000, "l(b),x", pa20w, FLAG_STRICT}, +{ "ldb", 0x40000000, 0xfc00c000, "j(b),x", pa10, 0}, +{ "ldb", 0x40000000, 0xfc000000, "j(s,b),x", pa10, 0}, +{ "std", 0x0c0012e0, 0xfc00f3ff, "cocCx,@(b)", pa20, FLAG_STRICT}, +{ "std", 0x0c0012e0, 0xfc0033ff, "cocCx,@(s,b)", pa20, FLAG_STRICT}, +{ "std", 0x0c0012c0, 0xfc00d3c0, "cmcCx,V(b)", pa20, FLAG_STRICT}, +{ "std", 0x0c0012c0, 0xfc0013c0, "cmcCx,V(s,b)", pa20, FLAG_STRICT}, +{ "std", 0x70000000, 0xfc000002, "cqx,&(b)", pa20w, FLAG_STRICT}, +{ "std", 0x70000000, 0xfc00c002, "cqx,#(b)", pa20, FLAG_STRICT}, +{ "std", 0x70000000, 0xfc000002, "cqx,#(s,b)", pa20, FLAG_STRICT}, +{ "stw", 0x0c0012a0, 0xfc00f3ff, "cocCx,@(b)", pa20, FLAG_STRICT}, +{ "stw", 0x0c0012a0, 0xfc0033ff, "cocCx,@(s,b)", pa20, FLAG_STRICT}, +{ "stw", 0x0c001280, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT}, +{ "stw", 0x0c001280, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT}, +{ "stw", 0x0c001280, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT}, +{ "stw", 0x0c001280, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT}, +{ "stw", 0x6c000000, 0xfc000000, "cex,<(b)", pa20w, FLAG_STRICT}, +{ "stw", 0x7c000004, 0xfc000006, "cex,>(b)", pa20w, FLAG_STRICT}, +{ "stw", 0x68000000, 0xfc000000, "x,l(b)", pa20w, FLAG_STRICT}, +{ "stw", 0x7c000004, 0xfc00c006, "cex,K(b)", pa20, FLAG_STRICT}, +{ "stw", 0x7c000004, 0xfc000006, "cex,K(s,b)", pa20, FLAG_STRICT}, +{ "stw", 0x6c000000, 0xfc00c000, "cex,J(b)", pa10, FLAG_STRICT}, +{ "stw", 0x6c000000, 0xfc000000, "cex,J(s,b)", pa10, FLAG_STRICT}, +{ "stw", 0x68000000, 0xfc00c000, "x,j(b)", pa10, 0}, +{ "stw", 0x68000000, 0xfc000000, "x,j(s,b)", pa10, 0}, +{ "sth", 0x0c001260, 0xfc00f3ff, "cocCx,@(b)", pa20, FLAG_STRICT}, +{ "sth", 0x0c001260, 0xfc0033ff, "cocCx,@(s,b)", pa20, FLAG_STRICT}, +{ "sth", 0x0c001240, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT}, +{ "sth", 0x0c001240, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT}, +{ "sth", 0x0c001240, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT}, +{ "sth", 0x0c001240, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT}, +{ "sth", 0x64000000, 0xfc000000, "x,l(b)", pa20w, FLAG_STRICT}, +{ "sth", 0x64000000, 0xfc00c000, "x,j(b)", pa10, 0}, +{ "sth", 0x64000000, 0xfc000000, "x,j(s,b)", pa10, 0}, +{ "stb", 0x0c001220, 0xfc00f3ff, "cocCx,@(b)", pa20, FLAG_STRICT}, +{ "stb", 0x0c001220, 0xfc0033ff, "cocCx,@(s,b)", pa20, FLAG_STRICT}, +{ "stb", 0x0c001200, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT}, +{ "stb", 0x0c001200, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT}, +{ "stb", 0x0c001200, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT}, +{ "stb", 0x0c001200, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT}, +{ "stb", 0x60000000, 0xfc000000, "x,l(b)", pa20w, FLAG_STRICT}, +{ "stb", 0x60000000, 0xfc00c000, "x,j(b)", pa10, 0}, +{ "stb", 0x60000000, 0xfc000000, "x,j(s,b)", pa10, 0}, +{ "ldwm", 0x4c000000, 0xfc00c000, "j(b),x", pa10, 0}, +{ "ldwm", 0x4c000000, 0xfc000000, "j(s,b),x", pa10, 0}, +{ "stwm", 0x6c000000, 0xfc00c000, "x,j(b)", pa10, 0}, +{ "stwm", 0x6c000000, 0xfc000000, "x,j(s,b)", pa10, 0}, +{ "ldwx", 0x0c000080, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldwx", 0x0c000080, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT}, +{ "ldwx", 0x0c000080, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT}, +{ "ldwx", 0x0c000080, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT}, +{ "ldwx", 0x0c000080, 0xfc00dfc0, "cXx(b),t", pa10, 0}, +{ "ldwx", 0x0c000080, 0xfc001fc0, "cXx(s,b),t", pa10, 0}, +{ "ldhx", 0x0c000040, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldhx", 0x0c000040, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT}, +{ "ldhx", 0x0c000040, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT}, +{ "ldhx", 0x0c000040, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT}, +{ "ldhx", 0x0c000040, 0xfc00dfc0, "cXx(b),t", pa10, 0}, +{ "ldhx", 0x0c000040, 0xfc001fc0, "cXx(s,b),t", pa10, 0}, +{ "ldbx", 0x0c000000, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldbx", 0x0c000000, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT}, +{ "ldbx", 0x0c000000, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT}, +{ "ldbx", 0x0c000000, 0xfc0013c0, "cxccx(s,b),t", pa11, FLAG_STRICT}, +{ "ldbx", 0x0c000000, 0xfc00dfc0, "cXx(b),t", pa10, 0}, +{ "ldbx", 0x0c000000, 0xfc001fc0, "cXx(s,b),t", pa10, 0}, +{ "ldwa", 0x0c000180, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldwa", 0x0c000180, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT}, +{ "ldwa", 0x0c0011a0, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT}, +{ "ldwa", 0x0c001180, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldwa", 0x0c001180, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT}, +{ "ldcw", 0x0c0001c0, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldcw", 0x0c0001c0, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT}, +{ "ldcw", 0x0c0001c0, 0xfc00d3c0, "cxcdx(b),t", pa11, FLAG_STRICT}, +{ "ldcw", 0x0c0001c0, 0xfc0013c0, "cxcdx(s,b),t", pa11, FLAG_STRICT}, +{ "ldcw", 0x0c0011c0, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldcw", 0x0c0011c0, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT}, +{ "ldcw", 0x0c0011c0, 0xfc00d3c0, "cmcd5(b),t", pa11, FLAG_STRICT}, +{ "ldcw", 0x0c0011c0, 0xfc0013c0, "cmcd5(s,b),t", pa11, FLAG_STRICT}, +{ "stwa", 0x0c0013a0, 0xfc00d3ff, "cocCx,@(b)", pa20, FLAG_STRICT}, +{ "stwa", 0x0c001380, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT}, +{ "stwa", 0x0c001380, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT}, +{ "stby", 0x0c001300, 0xfc00dfc0, "cAx,V(b)", pa10, FLAG_STRICT}, +{ "stby", 0x0c001300, 0xfc001fc0, "cAx,V(s,b)", pa10, FLAG_STRICT}, +{ "stby", 0x0c001300, 0xfc00d3c0, "cscCx,V(b)", pa11, FLAG_STRICT}, +{ "stby", 0x0c001300, 0xfc0013c0, "cscCx,V(s,b)", pa11, FLAG_STRICT}, +{ "ldda", 0x0c000100, 0xfc00d3c0, "cxccx(b),t", pa20, FLAG_STRICT}, +{ "ldda", 0x0c001120, 0xfc1ff3e0, "cocc@(b),t", pa20, FLAG_STRICT}, +{ "ldda", 0x0c001100, 0xfc00d3c0, "cmcc5(b),t", pa20, FLAG_STRICT}, +{ "ldcd", 0x0c000140, 0xfc00d3c0, "cxcdx(b),t", pa20, FLAG_STRICT}, +{ "ldcd", 0x0c000140, 0xfc0013c0, "cxcdx(s,b),t", pa20, FLAG_STRICT}, +{ "ldcd", 0x0c001140, 0xfc00d3c0, "cmcd5(b),t", pa20, FLAG_STRICT}, +{ "ldcd", 0x0c001140, 0xfc0013c0, "cmcd5(s,b),t", pa20, FLAG_STRICT}, +{ "stda", 0x0c0013e0, 0xfc00f3ff, "cocCx,@(b)", pa20, FLAG_STRICT}, +{ "stda", 0x0c0013c0, 0xfc00d3c0, "cmcCx,V(b)", pa20, FLAG_STRICT}, +{ "ldwax", 0x0c000180, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldwax", 0x0c000180, 0xfc00d3c0, "cxccx(b),t", pa11, FLAG_STRICT}, +{ "ldwax", 0x0c000180, 0xfc00dfc0, "cXx(b),t", pa10, 0}, +{ "ldcwx", 0x0c0001c0, 0xfc00dfc0, "cXx(b),t", pa10, FLAG_STRICT}, +{ "ldcwx", 0x0c0001c0, 0xfc001fc0, "cXx(s,b),t", pa10, FLAG_STRICT}, +{ "ldcwx", 0x0c0001c0, 0xfc00d3c0, "cxcdx(b),t", pa11, FLAG_STRICT}, +{ "ldcwx", 0x0c0001c0, 0xfc0013c0, "cxcdx(s,b),t", pa11, FLAG_STRICT}, +{ "ldcwx", 0x0c0001c0, 0xfc00dfc0, "cXx(b),t", pa10, 0}, +{ "ldcwx", 0x0c0001c0, 0xfc001fc0, "cXx(s,b),t", pa10, 0}, +{ "ldws", 0x0c001080, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldws", 0x0c001080, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT}, +{ "ldws", 0x0c001080, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT}, +{ "ldws", 0x0c001080, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "ldws", 0x0c001080, 0xfc00dfc0, "cM5(b),t", pa10, 0}, +{ "ldws", 0x0c001080, 0xfc001fc0, "cM5(s,b),t", pa10, 0}, +{ "ldhs", 0x0c001040, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldhs", 0x0c001040, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT}, +{ "ldhs", 0x0c001040, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT}, +{ "ldhs", 0x0c001040, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "ldhs", 0x0c001040, 0xfc00dfc0, "cM5(b),t", pa10, 0}, +{ "ldhs", 0x0c001040, 0xfc001fc0, "cM5(s,b),t", pa10, 0}, +{ "ldbs", 0x0c001000, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldbs", 0x0c001000, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT}, +{ "ldbs", 0x0c001000, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT}, +{ "ldbs", 0x0c001000, 0xfc0013c0, "cmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "ldbs", 0x0c001000, 0xfc00dfc0, "cM5(b),t", pa10, 0}, +{ "ldbs", 0x0c001000, 0xfc001fc0, "cM5(s,b),t", pa10, 0}, +{ "ldwas", 0x0c001180, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldwas", 0x0c001180, 0xfc00d3c0, "cmcc5(b),t", pa11, FLAG_STRICT}, +{ "ldwas", 0x0c001180, 0xfc00dfc0, "cM5(b),t", pa10, 0}, +{ "ldcws", 0x0c0011c0, 0xfc00dfc0, "cM5(b),t", pa10, FLAG_STRICT}, +{ "ldcws", 0x0c0011c0, 0xfc001fc0, "cM5(s,b),t", pa10, FLAG_STRICT}, +{ "ldcws", 0x0c0011c0, 0xfc00d3c0, "cmcd5(b),t", pa11, FLAG_STRICT}, +{ "ldcws", 0x0c0011c0, 0xfc0013c0, "cmcd5(s,b),t", pa11, FLAG_STRICT}, +{ "ldcws", 0x0c0011c0, 0xfc00dfc0, "cM5(b),t", pa10, 0}, +{ "ldcws", 0x0c0011c0, 0xfc001fc0, "cM5(s,b),t", pa10, 0}, +{ "stws", 0x0c001280, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT}, +{ "stws", 0x0c001280, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT}, +{ "stws", 0x0c001280, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT}, +{ "stws", 0x0c001280, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT}, +{ "stws", 0x0c001280, 0xfc00dfc0, "cMx,V(b)", pa10, 0}, +{ "stws", 0x0c001280, 0xfc001fc0, "cMx,V(s,b)", pa10, 0}, +{ "sths", 0x0c001240, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT}, +{ "sths", 0x0c001240, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT}, +{ "sths", 0x0c001240, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT}, +{ "sths", 0x0c001240, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT}, +{ "sths", 0x0c001240, 0xfc00dfc0, "cMx,V(b)", pa10, 0}, +{ "sths", 0x0c001240, 0xfc001fc0, "cMx,V(s,b)", pa10, 0}, +{ "stbs", 0x0c001200, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT}, +{ "stbs", 0x0c001200, 0xfc001fc0, "cMx,V(s,b)", pa10, FLAG_STRICT}, +{ "stbs", 0x0c001200, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT}, +{ "stbs", 0x0c001200, 0xfc0013c0, "cmcCx,V(s,b)", pa11, FLAG_STRICT}, +{ "stbs", 0x0c001200, 0xfc00dfc0, "cMx,V(b)", pa10, 0}, +{ "stbs", 0x0c001200, 0xfc001fc0, "cMx,V(s,b)", pa10, 0}, +{ "stwas", 0x0c001380, 0xfc00dfc0, "cMx,V(b)", pa10, FLAG_STRICT}, +{ "stwas", 0x0c001380, 0xfc00d3c0, "cmcCx,V(b)", pa11, FLAG_STRICT}, +{ "stwas", 0x0c001380, 0xfc00dfc0, "cMx,V(b)", pa10, 0}, +{ "stdby", 0x0c001340, 0xfc00d3c0, "cscCx,V(b)", pa20, FLAG_STRICT}, +{ "stdby", 0x0c001340, 0xfc0013c0, "cscCx,V(s,b)", pa20, FLAG_STRICT}, +{ "stbys", 0x0c001300, 0xfc00dfc0, "cAx,V(b)", pa10, FLAG_STRICT}, +{ "stbys", 0x0c001300, 0xfc001fc0, "cAx,V(s,b)", pa10, FLAG_STRICT}, +{ "stbys", 0x0c001300, 0xfc00d3c0, "cscCx,V(b)", pa11, FLAG_STRICT}, +{ "stbys", 0x0c001300, 0xfc0013c0, "cscCx,V(s,b)", pa11, FLAG_STRICT}, +{ "stbys", 0x0c001300, 0xfc00dfc0, "cAx,V(b)", pa10, 0}, +{ "stbys", 0x0c001300, 0xfc001fc0, "cAx,V(s,b)", pa10, 0}, + +/* Immediate instructions. */ +{ "ldo", 0x34000000, 0xfc000000, "l(b),x", pa20w, 0}, +{ "ldo", 0x34000000, 0xfc00c000, "j(b),x", pa10, 0}, +{ "ldil", 0x20000000, 0xfc000000, "k,b", pa10, 0}, +{ "addil", 0x28000000, 0xfc000000, "k,b,Z", pa10, 0}, +{ "addil", 0x28000000, 0xfc000000, "k,b", pa10, 0}, + +/* Branching instructions. */ +{ "b", 0xe8008000, 0xfc00e000, "cpnXL", pa20, FLAG_STRICT}, +{ "b", 0xe800a000, 0xfc00e000, "clnXL", pa20, FLAG_STRICT}, +{ "b", 0xe8000000, 0xfc00e000, "clnW,b", pa10, FLAG_STRICT}, +{ "b", 0xe8002000, 0xfc00e000, "cgnW,b", pa10, FLAG_STRICT}, +{ "b", 0xe8000000, 0xffe0e000, "nW", pa10, 0}, /* b,l foo,r0 */ +{ "bl", 0xe8000000, 0xfc00e000, "nW,b", pa10, 0}, +{ "gate", 0xe8002000, 0xfc00e000, "nW,b", pa10, 0}, +{ "blr", 0xe8004000, 0xfc00e001, "nx,b", pa10, 0}, +{ "bv", 0xe800c000, 0xfc00fffd, "nx(b)", pa10, 0}, +{ "bv", 0xe800c000, 0xfc00fffd, "n(b)", pa10, 0}, +{ "bve", 0xe800f001, 0xfc1ffffd, "cpn(b)L", pa20, FLAG_STRICT}, +{ "bve", 0xe800f000, 0xfc1ffffd, "cln(b)L", pa20, FLAG_STRICT}, +{ "bve", 0xe800d001, 0xfc1ffffd, "cPn(b)", pa20, FLAG_STRICT}, +{ "bve", 0xe800d000, 0xfc1ffffd, "n(b)", pa20, FLAG_STRICT}, +{ "be", 0xe4000000, 0xfc000000, "clnz(S,b),Y", pa10, FLAG_STRICT}, +{ "be", 0xe4000000, 0xfc000000, "clnz(b),Y", pa10, FLAG_STRICT}, +{ "be", 0xe0000000, 0xfc000000, "nz(S,b)", pa10, 0}, +{ "be", 0xe0000000, 0xfc000000, "nz(b)", pa10, 0}, +{ "ble", 0xe4000000, 0xfc000000, "nz(S,b)", pa10, 0}, +{ "movb", 0xc8000000, 0xfc000000, "?ynx,b,w", pa10, 0}, +{ "movib", 0xcc000000, 0xfc000000, "?yn5,b,w", pa10, 0}, +{ "combt", 0x80000000, 0xfc000000, "?tnx,b,w", pa10, 0}, +{ "combf", 0x88000000, 0xfc000000, "?tnx,b,w", pa10, 0}, +{ "comibt", 0x84000000, 0xfc000000, "?tn5,b,w", pa10, 0}, +{ "comibf", 0x8c000000, 0xfc000000, "?tn5,b,w", pa10, 0}, +{ "addbt", 0xa0000000, 0xfc000000, "?dnx,b,w", pa10, 0}, +{ "addbf", 0xa8000000, 0xfc000000, "?dnx,b,w", pa10, 0}, +{ "addibt", 0xa4000000, 0xfc000000, "?dn5,b,w", pa10, 0}, +{ "addibf", 0xac000000, 0xfc000000, "?dn5,b,w", pa10, 0}, +{ "bb", 0xc0004000, 0xffe06000, "?bnx,!,w", pa10, FLAG_STRICT}, +{ "bb", 0xc0006000, 0xffe06000, "?Bnx,!,w", pa20, FLAG_STRICT}, +{ "bb", 0xc4004000, 0xfc006000, "?bnx,Q,w", pa10, FLAG_STRICT}, +{ "bb", 0xc4004000, 0xfc004000, "?Bnx,B,w", pa20, FLAG_STRICT}, +{ "bvb", 0xc0004000, 0xffe04000, "?bnx,w", pa10, 0}, +{ "clrbts", 0xe8004005, 0xffffffff, "", pa20, FLAG_STRICT}, +{ "popbts", 0xe8004005, 0xfffff007, "$", pa20, FLAG_STRICT}, +{ "pushnom", 0xe8004001, 0xffffffff, "", pa20, FLAG_STRICT}, +{ "pushbts", 0xe8004001, 0xffe0ffff, "x", pa20, FLAG_STRICT}, + +/* Computation Instructions. */ + +{ "cmpclr", 0x080008a0, 0xfc000fe0, "?Sx,b,t", pa20, FLAG_STRICT}, +{ "cmpclr", 0x08000880, 0xfc000fe0, "?sx,b,t", pa10, FLAG_STRICT}, +{ "comclr", 0x08000880, 0xfc000fe0, "?sx,b,t", pa10, 0}, +{ "or", 0x08000260, 0xfc000fe0, "?Lx,b,t", pa20, FLAG_STRICT}, +{ "or", 0x08000240, 0xfc000fe0, "?lx,b,t", pa10, 0}, +{ "xor", 0x080002a0, 0xfc000fe0, "?Lx,b,t", pa20, FLAG_STRICT}, +{ "xor", 0x08000280, 0xfc000fe0, "?lx,b,t", pa10, 0}, +{ "and", 0x08000220, 0xfc000fe0, "?Lx,b,t", pa20, FLAG_STRICT}, +{ "and", 0x08000200, 0xfc000fe0, "?lx,b,t", pa10, 0}, +{ "andcm", 0x08000020, 0xfc000fe0, "?Lx,b,t", pa20, FLAG_STRICT}, +{ "andcm", 0x08000000, 0xfc000fe0, "?lx,b,t", pa10, 0}, +{ "uxor", 0x080003a0, 0xfc000fe0, "?Ux,b,t", pa20, FLAG_STRICT}, +{ "uxor", 0x08000380, 0xfc000fe0, "?ux,b,t", pa10, 0}, +{ "uaddcm", 0x080009a0, 0xfc000fa0, "cT?Ux,b,t", pa20, FLAG_STRICT}, +{ "uaddcm", 0x08000980, 0xfc000fa0, "cT?ux,b,t", pa10, FLAG_STRICT}, +{ "uaddcm", 0x08000980, 0xfc000fe0, "?ux,b,t", pa10, 0}, +{ "uaddcmt", 0x080009c0, 0xfc000fe0, "?ux,b,t", pa10, 0}, +{ "dcor", 0x08000ba0, 0xfc1f0fa0, "ci?Ub,t", pa20, FLAG_STRICT}, +{ "dcor", 0x08000b80, 0xfc1f0fa0, "ci?ub,t", pa10, FLAG_STRICT}, +{ "dcor", 0x08000b80, 0xfc1f0fe0, "?ub,t", pa10, 0}, +{ "idcor", 0x08000bc0, 0xfc1f0fe0, "?ub,t", pa10, 0}, +{ "addi", 0xb0000000, 0xfc000000, "ct?ai,b,x", pa10, FLAG_STRICT}, +{ "addi", 0xb4000000, 0xfc000000, "cv?ai,b,x", pa10, FLAG_STRICT}, +{ "addi", 0xb4000000, 0xfc000800, "?ai,b,x", pa10, 0}, +{ "addio", 0xb4000800, 0xfc000800, "?ai,b,x", pa10, 0}, +{ "addit", 0xb0000000, 0xfc000800, "?ai,b,x", pa10, 0}, +{ "addito", 0xb0000800, 0xfc000800, "?ai,b,x", pa10, 0}, +{ "add", 0x08000720, 0xfc0007e0, "cY?Ax,b,t", pa20, FLAG_STRICT}, +{ "add", 0x08000700, 0xfc0007e0, "cy?ax,b,t", pa10, FLAG_STRICT}, +{ "add", 0x08000220, 0xfc0003e0, "ca?Ax,b,t", pa20, FLAG_STRICT}, +{ "add", 0x08000200, 0xfc0003e0, "ca?ax,b,t", pa10, FLAG_STRICT}, +{ "add", 0x08000600, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "addl", 0x08000a00, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "addo", 0x08000e00, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "addc", 0x08000700, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "addco", 0x08000f00, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sub", 0x080004e0, 0xfc0007e0, "ct?Sx,b,t", pa20, FLAG_STRICT}, +{ "sub", 0x080004c0, 0xfc0007e0, "ct?sx,b,t", pa10, FLAG_STRICT}, +{ "sub", 0x08000520, 0xfc0007e0, "cB?Sx,b,t", pa20, FLAG_STRICT}, +{ "sub", 0x08000500, 0xfc0007e0, "cb?sx,b,t", pa10, FLAG_STRICT}, +{ "sub", 0x08000420, 0xfc0007e0, "cv?Sx,b,t", pa20, FLAG_STRICT}, +{ "sub", 0x08000400, 0xfc0007e0, "cv?sx,b,t", pa10, FLAG_STRICT}, +{ "sub", 0x08000400, 0xfc000fe0, "?sx,b,t", pa10, 0}, +{ "subo", 0x08000c00, 0xfc000fe0, "?sx,b,t", pa10, 0}, +{ "subb", 0x08000500, 0xfc000fe0, "?sx,b,t", pa10, 0}, +{ "subbo", 0x08000d00, 0xfc000fe0, "?sx,b,t", pa10, 0}, +{ "subt", 0x080004c0, 0xfc000fe0, "?sx,b,t", pa10, 0}, +{ "subto", 0x08000cc0, 0xfc000fe0, "?sx,b,t", pa10, 0}, +{ "ds", 0x08000440, 0xfc000fe0, "?sx,b,t", pa10, 0}, +{ "subi", 0x94000000, 0xfc000000, "cv?si,b,x", pa10, FLAG_STRICT}, +{ "subi", 0x94000000, 0xfc000800, "?si,b,x", pa10, 0}, +{ "subio", 0x94000800, 0xfc000800, "?si,b,x", pa10, 0}, +{ "cmpiclr", 0x90000800, 0xfc000800, "?Si,b,x", pa20, FLAG_STRICT}, +{ "cmpiclr", 0x90000000, 0xfc000800, "?si,b,x", pa10, FLAG_STRICT}, +{ "comiclr", 0x90000000, 0xfc000800, "?si,b,x", pa10, 0}, +{ "shladd", 0x08000220, 0xfc000320, "ca?Ax,.,b,t", pa20, FLAG_STRICT}, +{ "shladd", 0x08000200, 0xfc000320, "ca?ax,.,b,t", pa10, FLAG_STRICT}, +{ "sh1add", 0x08000640, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sh1addl", 0x08000a40, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sh1addo", 0x08000e40, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sh2add", 0x08000680, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sh2addl", 0x08000a80, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sh2addo", 0x08000e80, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sh3add", 0x080006c0, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sh3addl", 0x08000ac0, 0xfc000fe0, "?ax,b,t", pa10, 0}, +{ "sh3addo", 0x08000ec0, 0xfc000fe0, "?ax,b,t", pa10, 0}, + +/* Subword Operation Instructions. */ + +{ "hadd", 0x08000300, 0xfc00ff20, "cHx,b,t", pa20, FLAG_STRICT}, +{ "havg", 0x080002c0, 0xfc00ffe0, "x,b,t", pa20, FLAG_STRICT}, +{ "hshl", 0xf8008800, 0xffe0fc20, "x,*,t", pa20, FLAG_STRICT}, +{ "hshladd", 0x08000700, 0xfc00ff20, "x,.,b,t", pa20, FLAG_STRICT}, +{ "hshr", 0xf800c800, 0xfc1ff820, "cSb,*,t", pa20, FLAG_STRICT}, +{ "hshradd", 0x08000500, 0xfc00ff20, "x,.,b,t", pa20, FLAG_STRICT}, +{ "hsub", 0x08000100, 0xfc00ff20, "cHx,b,t", pa20, FLAG_STRICT}, +{ "mixh", 0xf8008400, 0xfc009fe0, "chx,b,t", pa20, FLAG_STRICT}, +{ "mixw", 0xf8008000, 0xfc009fe0, "chx,b,t", pa20, FLAG_STRICT}, +{ "permh", 0xf8000000, 0xfc009020, "c*a,t", pa20, FLAG_STRICT}, + + +/* Extract and Deposit Instructions. */ + +{ "shrpd", 0xd0000200, 0xfc001fe0, "?Xx,b,!,t", pa20, FLAG_STRICT}, +{ "shrpd", 0xd0000400, 0xfc001400, "?Xx,b,~,t", pa20, FLAG_STRICT}, +{ "shrpw", 0xd0000000, 0xfc001fe0, "?xx,b,!,t", pa10, FLAG_STRICT}, +{ "shrpw", 0xd0000800, 0xfc001c00, "?xx,b,p,t", pa10, FLAG_STRICT}, +{ "vshd", 0xd0000000, 0xfc001fe0, "?xx,b,t", pa10, 0}, +{ "shd", 0xd0000800, 0xfc001c00, "?xx,b,p,t", pa10, 0}, +{ "extrd", 0xd0001200, 0xfc001ae0, "cS?Xb,!,%,x", pa20, FLAG_STRICT}, +{ "extrd", 0xd8000000, 0xfc000000, "cS?Xb,q,|,x", pa20, FLAG_STRICT}, +{ "extrw", 0xd0001000, 0xfc001be0, "cS?xb,!,T,x", pa10, FLAG_STRICT}, +{ "extrw", 0xd0001800, 0xfc001800, "cS?xb,P,T,x", pa10, FLAG_STRICT}, +{ "vextru", 0xd0001000, 0xfc001fe0, "?xb,T,x", pa10, 0}, +{ "vextrs", 0xd0001400, 0xfc001fe0, "?xb,T,x", pa10, 0}, +{ "extru", 0xd0001800, 0xfc001c00, "?xb,P,T,x", pa10, 0}, +{ "extrs", 0xd0001c00, 0xfc001c00, "?xb,P,T,x", pa10, 0}, +{ "depd", 0xd4000200, 0xfc001ae0, "cz?Xx,!,%,b", pa20, FLAG_STRICT}, +{ "depd", 0xf0000000, 0xfc000000, "cz?Xx,~,|,b", pa20, FLAG_STRICT}, +{ "depdi", 0xd4001200, 0xfc001ae0, "cz?X5,!,%,b", pa20, FLAG_STRICT}, +{ "depdi", 0xf4000000, 0xfc000000, "cz?X5,~,|,b", pa20, FLAG_STRICT}, +{ "depw", 0xd4000000, 0xfc001be0, "cz?xx,!,T,b", pa10, FLAG_STRICT}, +{ "depw", 0xd4000800, 0xfc001800, "cz?xx,p,T,b", pa10, FLAG_STRICT}, +{ "depwi", 0xd4001000, 0xfc001be0, "cz?x5,!,T,b", pa10, FLAG_STRICT}, +{ "depwi", 0xd4001800, 0xfc001800, "cz?x5,p,T,b", pa10, FLAG_STRICT}, +{ "zvdep", 0xd4000000, 0xfc001fe0, "?xx,T,b", pa10, 0}, +{ "vdep", 0xd4000400, 0xfc001fe0, "?xx,T,b", pa10, 0}, +{ "zdep", 0xd4000800, 0xfc001c00, "?xx,p,T,b", pa10, 0}, +{ "dep", 0xd4000c00, 0xfc001c00, "?xx,p,T,b", pa10, 0}, +{ "zvdepi", 0xd4001000, 0xfc001fe0, "?x5,T,b", pa10, 0}, +{ "vdepi", 0xd4001400, 0xfc001fe0, "?x5,T,b", pa10, 0}, +{ "zdepi", 0xd4001800, 0xfc001c00, "?x5,p,T,b", pa10, 0}, +{ "depi", 0xd4001c00, 0xfc001c00, "?x5,p,T,b", pa10, 0}, + +/* System Control Instructions. */ + +{ "break", 0x00000000, 0xfc001fe0, "r,A", pa10, 0}, +{ "rfi", 0x00000c00, 0xffffff1f, "cr", pa10, FLAG_STRICT}, +{ "rfi", 0x00000c00, 0xffffffff, "", pa10, 0}, +{ "rfir", 0x00000ca0, 0xffffffff, "", pa11, 0}, +{ "ssm", 0x00000d60, 0xfc00ffe0, "U,t", pa20, FLAG_STRICT}, +{ "ssm", 0x00000d60, 0xffe0ffe0, "R,t", pa10, 0}, +{ "rsm", 0x00000e60, 0xfc00ffe0, "U,t", pa20, FLAG_STRICT}, +{ "rsm", 0x00000e60, 0xffe0ffe0, "R,t", pa10, 0}, +{ "mtsm", 0x00001860, 0xffe0ffff, "x", pa10, 0}, +{ "ldsid", 0x000010a0, 0xfc1fffe0, "(b),t", pa10, 0}, +{ "ldsid", 0x000010a0, 0xfc1f3fe0, "(s,b),t", pa10, 0}, +{ "mtsp", 0x00001820, 0xffe01fff, "x,S", pa10, 0}, +{ "mtctl", 0x00001840, 0xfc00ffff, "x,^", pa10, 0}, +{ "mtsarcm", 0x016018C0, 0xffe0ffff, "x", pa20, FLAG_STRICT}, +{ "mfia", 0x000014A0, 0xffffffe0, "t", pa20, FLAG_STRICT}, +{ "mfsp", 0x000004a0, 0xffff1fe0, "S,t", pa10, 0}, +{ "mfctl", 0x016048a0, 0xffffffe0, "cW!,t", pa20, FLAG_STRICT}, +{ "mfctl", 0x000008a0, 0xfc1fffe0, "^,t", pa10, 0}, +{ "sync", 0x00000400, 0xffffffff, "", pa10, 0}, +{ "syncdma", 0x00100400, 0xffffffff, "", pa10, 0}, +{ "probe", 0x04001180, 0xfc00ffa0, "cw(b),x,t", pa10, FLAG_STRICT}, +{ "probe", 0x04001180, 0xfc003fa0, "cw(s,b),x,t", pa10, FLAG_STRICT}, +{ "probei", 0x04003180, 0xfc00ffa0, "cw(b),R,t", pa10, FLAG_STRICT}, +{ "probei", 0x04003180, 0xfc003fa0, "cw(s,b),R,t", pa10, FLAG_STRICT}, +{ "prober", 0x04001180, 0xfc00ffe0, "(b),x,t", pa10, 0}, +{ "prober", 0x04001180, 0xfc003fe0, "(s,b),x,t", pa10, 0}, +{ "proberi", 0x04003180, 0xfc00ffe0, "(b),R,t", pa10, 0}, +{ "proberi", 0x04003180, 0xfc003fe0, "(s,b),R,t", pa10, 0}, +{ "probew", 0x040011c0, 0xfc00ffe0, "(b),x,t", pa10, 0}, +{ "probew", 0x040011c0, 0xfc003fe0, "(s,b),x,t", pa10, 0}, +{ "probewi", 0x040031c0, 0xfc00ffe0, "(b),R,t", pa10, 0}, +{ "probewi", 0x040031c0, 0xfc003fe0, "(s,b),R,t", pa10, 0}, +{ "lpa", 0x04001340, 0xfc00ffc0, "cZx(b),t", pa10, 0}, +{ "lpa", 0x04001340, 0xfc003fc0, "cZx(s,b),t", pa10, 0}, +{ "lci", 0x04001300, 0xfc00ffe0, "x(b),t", pa11, 0}, +{ "lci", 0x04001300, 0xfc003fe0, "x(s,b),t", pa11, 0}, +{ "pdtlb", 0x04001600, 0xfc00ffdf, "cLcZx(b)", pa20, FLAG_STRICT}, +{ "pdtlb", 0x04001600, 0xfc003fdf, "cLcZx(s,b)", pa20, FLAG_STRICT}, +{ "pdtlb", 0x04001600, 0xfc1fffdf, "cLcZ@(b)", pa20, FLAG_STRICT}, +{ "pdtlb", 0x04001600, 0xfc1f3fdf, "cLcZ@(s,b)", pa20, FLAG_STRICT}, +{ "pdtlb", 0x04001200, 0xfc00ffdf, "cZx(b)", pa10, 0}, +{ "pdtlb", 0x04001200, 0xfc003fdf, "cZx(s,b)", pa10, 0}, +{ "pitlb", 0x04000600, 0xfc001fdf, "cLcZx(S,b)", pa20, FLAG_STRICT}, +{ "pitlb", 0x04000600, 0xfc1f1fdf, "cLcZ@(S,b)", pa20, FLAG_STRICT}, +{ "pitlb", 0x04000200, 0xfc001fdf, "cZx(S,b)", pa10, 0}, +{ "pdtlbe", 0x04001240, 0xfc00ffdf, "cZx(b)", pa10, 0}, +{ "pdtlbe", 0x04001240, 0xfc003fdf, "cZx(s,b)", pa10, 0}, +{ "pitlbe", 0x04000240, 0xfc001fdf, "cZx(S,b)", pa10, 0}, +{ "idtlba", 0x04001040, 0xfc00ffff, "x,(b)", pa10, 0}, +{ "idtlba", 0x04001040, 0xfc003fff, "x,(s,b)", pa10, 0}, +{ "iitlba", 0x04000040, 0xfc001fff, "x,(S,b)", pa10, 0}, +{ "idtlbp", 0x04001000, 0xfc00ffff, "x,(b)", pa10, 0}, +{ "idtlbp", 0x04001000, 0xfc003fff, "x,(s,b)", pa10, 0}, +{ "iitlbp", 0x04000000, 0xfc001fff, "x,(S,b)", pa10, 0}, +{ "pdc", 0x04001380, 0xfc00ffdf, "cZx(b)", pa10, 0}, +{ "pdc", 0x04001380, 0xfc003fdf, "cZx(s,b)", pa10, 0}, +{ "fdc", 0x04001280, 0xfc00ffdf, "cZx(b)", pa10, FLAG_STRICT}, +{ "fdc", 0x04001280, 0xfc003fdf, "cZx(s,b)", pa10, FLAG_STRICT}, +{ "fdc", 0x04003280, 0xfc00ffff, "5(b)", pa20, FLAG_STRICT}, +{ "fdc", 0x04003280, 0xfc003fff, "5(s,b)", pa20, FLAG_STRICT}, +{ "fdc", 0x04001280, 0xfc00ffdf, "cZx(b)", pa10, 0}, +{ "fdc", 0x04001280, 0xfc003fdf, "cZx(s,b)", pa10, 0}, +{ "fic", 0x040013c0, 0xfc00dfdf, "cZx(b)", pa20, FLAG_STRICT}, +{ "fic", 0x04000280, 0xfc001fdf, "cZx(S,b)", pa10, 0}, +{ "fdce", 0x040012c0, 0xfc00ffdf, "cZx(b)", pa10, 0}, +{ "fdce", 0x040012c0, 0xfc003fdf, "cZx(s,b)", pa10, 0}, +{ "fice", 0x040002c0, 0xfc001fdf, "cZx(S,b)", pa10, 0}, +{ "diag", 0x14000000, 0xfc000000, "D", pa10, 0}, +{ "idtlbt", 0x04001800, 0xfc00ffff, "x,b", pa20, FLAG_STRICT}, +{ "iitlbt", 0x04000800, 0xfc00ffff, "x,b", pa20, FLAG_STRICT}, + +/* These may be specific to certain versions of the PA. Joel claimed + they were 72000 (7200?) specific. However, I'm almost certain the + mtcpu/mfcpu were undocumented, but available in the older 700 machines. */ +{ "mtcpu", 0x14001600, 0xfc00ffff, "x,^", pa10, 0}, +{ "mfcpu", 0x14001A00, 0xfc00ffff, "^,x", pa10, 0}, +{ "tocen", 0x14403600, 0xffffffff, "", pa10, 0}, +{ "tocdis", 0x14401620, 0xffffffff, "", pa10, 0}, +{ "shdwgr", 0x14402600, 0xffffffff, "", pa10, 0}, +{ "grshdw", 0x14400620, 0xffffffff, "", pa10, 0}, + +/* gfw and gfr are not in the HP PA 1.1 manual, but they are in either + the Timex FPU or the Mustang ERS (not sure which) manual. */ +{ "gfw", 0x04001680, 0xfc00ffdf, "cZx(b)", pa11, 0}, +{ "gfw", 0x04001680, 0xfc003fdf, "cZx(s,b)", pa11, 0}, +{ "gfr", 0x04001a80, 0xfc00ffdf, "cZx(b)", pa11, 0}, +{ "gfr", 0x04001a80, 0xfc003fdf, "cZx(s,b)", pa11, 0}, + +/* Floating Point Coprocessor Instructions. */ + +{ "fldw", 0x24000000, 0xfc00df80, "cXx(b),fT", pa10, FLAG_STRICT}, +{ "fldw", 0x24000000, 0xfc001f80, "cXx(s,b),fT", pa10, FLAG_STRICT}, +{ "fldw", 0x24000000, 0xfc00d380, "cxccx(b),fT", pa11, FLAG_STRICT}, +{ "fldw", 0x24000000, 0xfc001380, "cxccx(s,b),fT", pa11, FLAG_STRICT}, +{ "fldw", 0x24001020, 0xfc1ff3a0, "cocc@(b),fT", pa20, FLAG_STRICT}, +{ "fldw", 0x24001020, 0xfc1f33a0, "cocc@(s,b),fT", pa20, FLAG_STRICT}, +{ "fldw", 0x24001000, 0xfc00df80, "cM5(b),fT", pa10, FLAG_STRICT}, +{ "fldw", 0x24001000, 0xfc001f80, "cM5(s,b),fT", pa10, FLAG_STRICT}, +{ "fldw", 0x24001000, 0xfc00d380, "cmcc5(b),fT", pa11, FLAG_STRICT}, +{ "fldw", 0x24001000, 0xfc001380, "cmcc5(s,b),fT", pa11, FLAG_STRICT}, +{ "fldw", 0x5c000000, 0xfc000004, "y(b),fe", pa20w, FLAG_STRICT}, +{ "fldw", 0x58000000, 0xfc000000, "cJy(b),fe", pa20w, FLAG_STRICT}, +{ "fldw", 0x5c000000, 0xfc00c004, "d(b),fe", pa20, FLAG_STRICT}, +{ "fldw", 0x5c000000, 0xfc000004, "d(s,b),fe", pa20, FLAG_STRICT}, +{ "fldw", 0x58000000, 0xfc00c000, "cJd(b),fe", pa20, FLAG_STRICT}, +{ "fldw", 0x58000000, 0xfc000000, "cJd(s,b),fe", pa20, FLAG_STRICT}, +{ "fldd", 0x2c000000, 0xfc00dfc0, "cXx(b),ft", pa10, FLAG_STRICT}, +{ "fldd", 0x2c000000, 0xfc001fc0, "cXx(s,b),ft", pa10, FLAG_STRICT}, +{ "fldd", 0x2c000000, 0xfc00d3c0, "cxccx(b),ft", pa11, FLAG_STRICT}, +{ "fldd", 0x2c000000, 0xfc0013c0, "cxccx(s,b),ft", pa11, FLAG_STRICT}, +{ "fldd", 0x2c001020, 0xfc1ff3e0, "cocc@(b),ft", pa20, FLAG_STRICT}, +{ "fldd", 0x2c001020, 0xfc1f33e0, "cocc@(s,b),ft", pa20, FLAG_STRICT}, +{ "fldd", 0x2c001000, 0xfc00dfc0, "cM5(b),ft", pa10, FLAG_STRICT}, +{ "fldd", 0x2c001000, 0xfc001fc0, "cM5(s,b),ft", pa10, FLAG_STRICT}, +{ "fldd", 0x2c001000, 0xfc00d3c0, "cmcc5(b),ft", pa11, FLAG_STRICT}, +{ "fldd", 0x2c001000, 0xfc0013c0, "cmcc5(s,b),ft", pa11, FLAG_STRICT}, +{ "fldd", 0x50000002, 0xfc000002, "cq&(b),fx", pa20w, FLAG_STRICT}, +{ "fldd", 0x50000002, 0xfc00c002, "cq#(b),fx", pa20, FLAG_STRICT}, +{ "fldd", 0x50000002, 0xfc000002, "cq#(s,b),fx", pa20, FLAG_STRICT}, +{ "fstw", 0x24000200, 0xfc00df80, "cXfT,x(b)", pa10, FLAG_STRICT}, +{ "fstw", 0x24000200, 0xfc001f80, "cXfT,x(s,b)", pa10, FLAG_STRICT}, +{ "fstw", 0x24000200, 0xfc00d380, "cxcCfT,x(b)", pa11, FLAG_STRICT}, +{ "fstw", 0x24000200, 0xfc001380, "cxcCfT,x(s,b)", pa11, FLAG_STRICT}, +{ "fstw", 0x24001220, 0xfc1ff3a0, "cocCfT,@(b)", pa20, FLAG_STRICT}, +{ "fstw", 0x24001220, 0xfc1f33a0, "cocCfT,@(s,b)", pa20, FLAG_STRICT}, +{ "fstw", 0x24001200, 0xfc00df80, "cMfT,5(b)", pa10, FLAG_STRICT}, +{ "fstw", 0x24001200, 0xfc001f80, "cMfT,5(s,b)", pa10, FLAG_STRICT}, +{ "fstw", 0x24001200, 0xfc00df80, "cMfT,5(b)", pa10, FLAG_STRICT}, +{ "fstw", 0x24001200, 0xfc001f80, "cMfT,5(s,b)", pa10, FLAG_STRICT}, +{ "fstw", 0x7c000000, 0xfc000004, "fE,y(b)", pa20w, FLAG_STRICT}, +{ "fstw", 0x78000000, 0xfc000000, "cJfE,y(b)", pa20w, FLAG_STRICT}, +{ "fstw", 0x7c000000, 0xfc00c004, "fE,d(b)", pa20, FLAG_STRICT}, +{ "fstw", 0x7c000000, 0xfc000004, "fE,d(s,b)", pa20, FLAG_STRICT}, +{ "fstw", 0x78000000, 0xfc00c000, "cJfE,d(b)", pa20, FLAG_STRICT}, +{ "fstw", 0x78000000, 0xfc000000, "cJfE,d(s,b)", pa20, FLAG_STRICT}, +{ "fstd", 0x2c000200, 0xfc00dfc0, "cXft,x(b)", pa10, FLAG_STRICT}, +{ "fstd", 0x2c000200, 0xfc001fc0, "cXft,x(s,b)", pa10, FLAG_STRICT}, +{ "fstd", 0x2c000200, 0xfc00d3c0, "cxcCft,x(b)", pa11, FLAG_STRICT}, +{ "fstd", 0x2c000200, 0xfc0013c0, "cxcCft,x(s,b)", pa11, FLAG_STRICT}, +{ "fstd", 0x2c001220, 0xfc1ff3e0, "cocCft,@(b)", pa20, FLAG_STRICT}, +{ "fstd", 0x2c001220, 0xfc1f33e0, "cocCft,@(s,b)", pa20, FLAG_STRICT}, +{ "fstd", 0x2c001200, 0xfc00dfc0, "cMft,5(b)", pa10, FLAG_STRICT}, +{ "fstd", 0x2c001200, 0xfc001fc0, "cMft,5(s,b)", pa10, FLAG_STRICT}, +{ "fstd", 0x2c001200, 0xfc00d3c0, "cmcCft,5(b)", pa11, FLAG_STRICT}, +{ "fstd", 0x2c001200, 0xfc0013c0, "cmcCft,5(s,b)", pa11, FLAG_STRICT}, +{ "fstd", 0x70000002, 0xfc000002, "cqfx,&(b)", pa20w, FLAG_STRICT}, +{ "fstd", 0x70000002, 0xfc00c002, "cqfx,#(b)", pa20, FLAG_STRICT}, +{ "fstd", 0x70000002, 0xfc000002, "cqfx,#(s,b)", pa20, FLAG_STRICT}, +{ "fldwx", 0x24000000, 0xfc00df80, "cXx(b),fT", pa10, FLAG_STRICT}, +{ "fldwx", 0x24000000, 0xfc001f80, "cXx(s,b),fT", pa10, FLAG_STRICT}, +{ "fldwx", 0x24000000, 0xfc00d380, "cxccx(b),fT", pa11, FLAG_STRICT}, +{ "fldwx", 0x24000000, 0xfc001380, "cxccx(s,b),fT", pa11, FLAG_STRICT}, +{ "fldwx", 0x24000000, 0xfc00df80, "cXx(b),fT", pa10, 0}, +{ "fldwx", 0x24000000, 0xfc001f80, "cXx(s,b),fT", pa10, 0}, +{ "flddx", 0x2c000000, 0xfc00dfc0, "cXx(b),ft", pa10, FLAG_STRICT}, +{ "flddx", 0x2c000000, 0xfc001fc0, "cXx(s,b),ft", pa10, FLAG_STRICT}, +{ "flddx", 0x2c000000, 0xfc00d3c0, "cxccx(b),ft", pa11, FLAG_STRICT}, +{ "flddx", 0x2c000000, 0xfc0013c0, "cxccx(s,b),ft", pa11, FLAG_STRICT}, +{ "flddx", 0x2c000000, 0xfc00dfc0, "cXx(b),ft", pa10, 0}, +{ "flddx", 0x2c000000, 0xfc001fc0, "cXx(s,b),ft", pa10, 0}, +{ "fstwx", 0x24000200, 0xfc00df80, "cxfT,x(b)", pa10, FLAG_STRICT}, +{ "fstwx", 0x24000200, 0xfc001f80, "cxfT,x(s,b)", pa10, FLAG_STRICT}, +{ "fstwx", 0x24000200, 0xfc00d380, "cxcCfT,x(b)", pa11, FLAG_STRICT}, +{ "fstwx", 0x24000200, 0xfc001380, "cxcCfT,x(s,b)", pa11, FLAG_STRICT}, +{ "fstwx", 0x24000200, 0xfc00df80, "cxfT,x(b)", pa10, 0}, +{ "fstwx", 0x24000200, 0xfc001f80, "cxfT,x(s,b)", pa10, 0}, +{ "fstdx", 0x2c000200, 0xfc00dfc0, "cxft,x(b)", pa10, FLAG_STRICT}, +{ "fstdx", 0x2c000200, 0xfc001fc0, "cxft,x(s,b)", pa10, FLAG_STRICT}, +{ "fstdx", 0x2c000200, 0xfc00d3c0, "cxcCft,x(b)", pa11, FLAG_STRICT}, +{ "fstdx", 0x2c000200, 0xfc0013c0, "cxcCft,x(s,b)", pa11, FLAG_STRICT}, +{ "fstdx", 0x2c000200, 0xfc00dfc0, "cxft,x(b)", pa10, 0}, +{ "fstdx", 0x2c000200, 0xfc001fc0, "cxft,x(s,b)", pa10, 0}, +{ "fstqx", 0x3c000200, 0xfc00dfc0, "cxft,x(b)", pa10, 0}, +{ "fstqx", 0x3c000200, 0xfc001fc0, "cxft,x(s,b)", pa10, 0}, +{ "fldws", 0x24001000, 0xfc00df80, "cm5(b),fT", pa10, FLAG_STRICT}, +{ "fldws", 0x24001000, 0xfc001f80, "cm5(s,b),fT", pa10, FLAG_STRICT}, +{ "fldws", 0x24001000, 0xfc00d380, "cmcc5(b),fT", pa11, FLAG_STRICT}, +{ "fldws", 0x24001000, 0xfc001380, "cmcc5(s,b),fT", pa11, FLAG_STRICT}, +{ "fldws", 0x24001000, 0xfc00df80, "cm5(b),fT", pa10, 0}, +{ "fldws", 0x24001000, 0xfc001f80, "cm5(s,b),fT", pa10, 0}, +{ "fldds", 0x2c001000, 0xfc00dfc0, "cm5(b),ft", pa10, FLAG_STRICT}, +{ "fldds", 0x2c001000, 0xfc001fc0, "cm5(s,b),ft", pa10, FLAG_STRICT}, +{ "fldds", 0x2c001000, 0xfc00d3c0, "cmcc5(b),ft", pa11, FLAG_STRICT}, +{ "fldds", 0x2c001000, 0xfc0013c0, "cmcc5(s,b),ft", pa11, FLAG_STRICT}, +{ "fldds", 0x2c001000, 0xfc00dfc0, "cm5(b),ft", pa10, 0}, +{ "fldds", 0x2c001000, 0xfc001fc0, "cm5(s,b),ft", pa10, 0}, +{ "fstws", 0x24001200, 0xfc00df80, "cmfT,5(b)", pa10, FLAG_STRICT}, +{ "fstws", 0x24001200, 0xfc001f80, "cmfT,5(s,b)", pa10, FLAG_STRICT}, +{ "fstws", 0x24001200, 0xfc00d380, "cmcCfT,5(b)", pa11, FLAG_STRICT}, +{ "fstws", 0x24001200, 0xfc001380, "cmcCfT,5(s,b)", pa11, FLAG_STRICT}, +{ "fstws", 0x24001200, 0xfc00df80, "cmfT,5(b)", pa10, 0}, +{ "fstws", 0x24001200, 0xfc001f80, "cmfT,5(s,b)", pa10, 0}, +{ "fstds", 0x2c001200, 0xfc00dfc0, "cmft,5(b)", pa10, FLAG_STRICT}, +{ "fstds", 0x2c001200, 0xfc001fc0, "cmft,5(s,b)", pa10, FLAG_STRICT}, +{ "fstds", 0x2c001200, 0xfc00d3c0, "cmcCft,5(b)", pa11, FLAG_STRICT}, +{ "fstds", 0x2c001200, 0xfc0013c0, "cmcCft,5(s,b)", pa11, FLAG_STRICT}, +{ "fstds", 0x2c001200, 0xfc00dfc0, "cmft,5(b)", pa10, 0}, +{ "fstds", 0x2c001200, 0xfc001fc0, "cmft,5(s,b)", pa10, 0}, +{ "fstqs", 0x3c001200, 0xfc00dfc0, "cmft,5(b)", pa10, 0}, +{ "fstqs", 0x3c001200, 0xfc001fc0, "cmft,5(s,b)", pa10, 0}, +{ "fadd", 0x30000600, 0xfc00e7e0, "Ffa,fb,fT", pa10, 0}, +{ "fadd", 0x38000600, 0xfc00e720, "IfA,fB,fT", pa10, 0}, +{ "fsub", 0x30002600, 0xfc00e7e0, "Ffa,fb,fT", pa10, 0}, +{ "fsub", 0x38002600, 0xfc00e720, "IfA,fB,fT", pa10, 0}, +{ "fmpy", 0x30004600, 0xfc00e7e0, "Ffa,fb,fT", pa10, 0}, +{ "fmpy", 0x38004600, 0xfc00e720, "IfA,fB,fT", pa10, 0}, +{ "fdiv", 0x30006600, 0xfc00e7e0, "Ffa,fb,fT", pa10, 0}, +{ "fdiv", 0x38006600, 0xfc00e720, "IfA,fB,fT", pa10, 0}, +{ "fsqrt", 0x30008000, 0xfc1fe7e0, "Ffa,fT", pa10, 0}, +{ "fsqrt", 0x38008000, 0xfc1fe720, "FfA,fT", pa10, 0}, +{ "fabs", 0x30006000, 0xfc1fe7e0, "Ffa,fT", pa10, 0}, +{ "fabs", 0x38006000, 0xfc1fe720, "FfA,fT", pa10, 0}, +{ "frem", 0x30008600, 0xfc00e7e0, "Ffa,fb,fT", pa10, 0}, +{ "frem", 0x38008600, 0xfc00e720, "FfA,fB,fT", pa10, 0}, +{ "frnd", 0x3000a000, 0xfc1fe7e0, "Ffa,fT", pa10, 0}, +{ "frnd", 0x3800a000, 0xfc1fe720, "FfA,fT", pa10, 0}, +{ "fcpy", 0x30004000, 0xfc1fe7e0, "Ffa,fT", pa10, 0}, +{ "fcpy", 0x38004000, 0xfc1fe720, "FfA,fT", pa10, 0}, +{ "fcnvff", 0x30000200, 0xfc1f87e0, "FGfa,fT", pa10, 0}, +{ "fcnvff", 0x38000200, 0xfc1f8720, "FGfA,fT", pa10, 0}, +{ "fcnvxf", 0x30008200, 0xfc1f87e0, "FGfa,fT", pa10, 0}, +{ "fcnvxf", 0x38008200, 0xfc1f8720, "FGfA,fT", pa10, 0}, +{ "fcnvfx", 0x30010200, 0xfc1f87e0, "FGfa,fT", pa10, 0}, +{ "fcnvfx", 0x38010200, 0xfc1f8720, "FGfA,fT", pa10, 0}, +{ "fcnvfxt", 0x30018200, 0xfc1f87e0, "FGfa,fT", pa10, 0}, +{ "fcnvfxt", 0x38018200, 0xfc1f8720, "FGfA,fT", pa10, 0}, +{ "fmpyfadd", 0xb8000000, 0xfc000020, "IfA,fB,fC,fT", pa20, FLAG_STRICT}, +{ "fmpynfadd", 0xb8000020, 0xfc000020, "IfA,fB,fC,fT", pa20, FLAG_STRICT}, +{ "fneg", 0x3000c000, 0xfc1fe7e0, "Ffa,fT", pa20, FLAG_STRICT}, +{ "fneg", 0x3800c000, 0xfc1fe720, "IfA,fT", pa20, FLAG_STRICT}, +{ "fnegabs", 0x3000e000, 0xfc1fe7e0, "Ffa,fT", pa20, FLAG_STRICT}, +{ "fnegabs", 0x3800e000, 0xfc1fe720, "IfA,fT", pa20, FLAG_STRICT}, +{ "fcnv", 0x30000200, 0xfc1c0720, "{_fa,fT", pa20, FLAG_STRICT}, +{ "fcnv", 0x38000200, 0xfc1c0720, "FGfA,fT", pa20, FLAG_STRICT}, +{ "fcmp", 0x30000400, 0xfc00e7e0, "F?ffa,fb", pa10, FLAG_STRICT}, +{ "fcmp", 0x38000400, 0xfc00e720, "I?ffA,fB", pa10, FLAG_STRICT}, +{ "fcmp", 0x30000400, 0xfc0007e0, "F?ffa,fb,h", pa20, FLAG_STRICT}, +{ "fcmp", 0x38000400, 0xfc000720, "I?ffA,fB,h", pa20, FLAG_STRICT}, +{ "fcmp", 0x30000400, 0xfc00e7e0, "F?ffa,fb", pa10, 0}, +{ "fcmp", 0x38000400, 0xfc00e720, "I?ffA,fB", pa10, 0}, +{ "xmpyu", 0x38004700, 0xfc00e720, "fX,fB,fT", pa11, 0}, +{ "fmpyadd", 0x18000000, 0xfc000000, "Hfi,fj,fk,fl,fm", pa11, 0}, +{ "fmpysub", 0x98000000, 0xfc000000, "Hfi,fj,fk,fl,fm", pa11, 0}, +{ "ftest", 0x30002420, 0xffffffff, "", pa10, FLAG_STRICT}, +{ "ftest", 0x30002420, 0xffffffe0, ",=", pa20, FLAG_STRICT}, +{ "ftest", 0x30000420, 0xffff1fff, "m", pa20, FLAG_STRICT}, +{ "fid", 0x30000000, 0xffffffff, "", pa11, 0}, + +/* Performance Monitor Instructions. */ + +{ "pmdis", 0x30000280, 0xffffffdf, "N", pa20, FLAG_STRICT}, +{ "pmenb", 0x30000680, 0xffffffff, "", pa20, FLAG_STRICT}, + +/* Assist Instructions. */ + +{ "spop0", 0x10000000, 0xfc000600, "v,ON", pa10, 0}, +{ "spop1", 0x10000200, 0xfc000600, "v,oNt", pa10, 0}, +{ "spop2", 0x10000400, 0xfc000600, "v,1Nb", pa10, 0}, +{ "spop3", 0x10000600, 0xfc000600, "v,0Nx,b", pa10, 0}, +{ "copr", 0x30000000, 0xfc000000, "u,2N", pa10, 0}, +{ "cldw", 0x24000000, 0xfc00de00, "ucXx(b),t", pa10, FLAG_STRICT}, +{ "cldw", 0x24000000, 0xfc001e00, "ucXx(s,b),t", pa10, FLAG_STRICT}, +{ "cldw", 0x24000000, 0xfc00d200, "ucxccx(b),t", pa11, FLAG_STRICT}, +{ "cldw", 0x24000000, 0xfc001200, "ucxccx(s,b),t", pa11, FLAG_STRICT}, +{ "cldw", 0x24001000, 0xfc00d200, "ucocc@(b),t", pa20, FLAG_STRICT}, +{ "cldw", 0x24001000, 0xfc001200, "ucocc@(s,b),t", pa20, FLAG_STRICT}, +{ "cldw", 0x24001000, 0xfc00de00, "ucM5(b),t", pa10, FLAG_STRICT}, +{ "cldw", 0x24001000, 0xfc001e00, "ucM5(s,b),t", pa10, FLAG_STRICT}, +{ "cldw", 0x24001000, 0xfc00d200, "ucmcc5(b),t", pa11, FLAG_STRICT}, +{ "cldw", 0x24001000, 0xfc001200, "ucmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "cldd", 0x2c000000, 0xfc00de00, "ucXx(b),t", pa10, FLAG_STRICT}, +{ "cldd", 0x2c000000, 0xfc001e00, "ucXx(s,b),t", pa10, FLAG_STRICT}, +{ "cldd", 0x2c000000, 0xfc00d200, "ucxccx(b),t", pa11, FLAG_STRICT}, +{ "cldd", 0x2c000000, 0xfc001200, "ucxccx(s,b),t", pa11, FLAG_STRICT}, +{ "cldd", 0x2c001000, 0xfc00d200, "ucocc@(b),t", pa20, FLAG_STRICT}, +{ "cldd", 0x2c001000, 0xfc001200, "ucocc@(s,b),t", pa20, FLAG_STRICT}, +{ "cldd", 0x2c001000, 0xfc00de00, "ucM5(b),t", pa10, FLAG_STRICT}, +{ "cldd", 0x2c001000, 0xfc001e00, "ucM5(s,b),t", pa10, FLAG_STRICT}, +{ "cldd", 0x2c001000, 0xfc00d200, "ucmcc5(b),t", pa11, FLAG_STRICT}, +{ "cldd", 0x2c001000, 0xfc001200, "ucmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "cstw", 0x24000200, 0xfc00de00, "ucXt,x(b)", pa10, FLAG_STRICT}, +{ "cstw", 0x24000200, 0xfc001e00, "ucXt,x(s,b)", pa10, FLAG_STRICT}, +{ "cstw", 0x24000200, 0xfc00d200, "ucxcCt,x(b)", pa11, FLAG_STRICT}, +{ "cstw", 0x24000200, 0xfc001200, "ucxcCt,x(s,b)", pa11, FLAG_STRICT}, +{ "cstw", 0x24001200, 0xfc00d200, "ucocCt,@(b)", pa20, FLAG_STRICT}, +{ "cstw", 0x24001200, 0xfc001200, "ucocCt,@(s,b)", pa20, FLAG_STRICT}, +{ "cstw", 0x24001200, 0xfc00de00, "ucMt,5(b)", pa10, FLAG_STRICT}, +{ "cstw", 0x24001200, 0xfc001e00, "ucMt,5(s,b)", pa10, FLAG_STRICT}, +{ "cstw", 0x24001200, 0xfc00d200, "ucmcCt,5(b)", pa11, FLAG_STRICT}, +{ "cstw", 0x24001200, 0xfc001200, "ucmcCt,5(s,b)", pa11, FLAG_STRICT}, +{ "cstd", 0x2c000200, 0xfc00de00, "ucXt,x(b)", pa10, FLAG_STRICT}, +{ "cstd", 0x2c000200, 0xfc001e00, "ucXt,x(s,b)", pa10, FLAG_STRICT}, +{ "cstd", 0x2c000200, 0xfc00d200, "ucxcCt,x(b)", pa11, FLAG_STRICT}, +{ "cstd", 0x2c000200, 0xfc001200, "ucxcCt,x(s,b)", pa11, FLAG_STRICT}, +{ "cstd", 0x2c001200, 0xfc00d200, "ucocCt,@(b)", pa20, FLAG_STRICT}, +{ "cstd", 0x2c001200, 0xfc001200, "ucocCt,@(s,b)", pa20, FLAG_STRICT}, +{ "cstd", 0x2c001200, 0xfc00de00, "ucMt,5(b)", pa10, FLAG_STRICT}, +{ "cstd", 0x2c001200, 0xfc001e00, "ucMt,5(s,b)", pa10, FLAG_STRICT}, +{ "cstd", 0x2c001200, 0xfc00d200, "ucmcCt,5(b)", pa11, FLAG_STRICT}, +{ "cstd", 0x2c001200, 0xfc001200, "ucmcCt,5(s,b)", pa11, FLAG_STRICT}, +{ "cldwx", 0x24000000, 0xfc00de00, "ucXx(b),t", pa10, FLAG_STRICT}, +{ "cldwx", 0x24000000, 0xfc001e00, "ucXx(s,b),t", pa10, FLAG_STRICT}, +{ "cldwx", 0x24000000, 0xfc00d200, "ucxccx(b),t", pa11, FLAG_STRICT}, +{ "cldwx", 0x24000000, 0xfc001200, "ucxccx(s,b),t", pa11, FLAG_STRICT}, +{ "cldwx", 0x24000000, 0xfc00de00, "ucXx(b),t", pa10, 0}, +{ "cldwx", 0x24000000, 0xfc001e00, "ucXx(s,b),t", pa10, 0}, +{ "clddx", 0x2c000000, 0xfc00de00, "ucXx(b),t", pa10, FLAG_STRICT}, +{ "clddx", 0x2c000000, 0xfc001e00, "ucXx(s,b),t", pa10, FLAG_STRICT}, +{ "clddx", 0x2c000000, 0xfc00d200, "ucxccx(b),t", pa11, FLAG_STRICT}, +{ "clddx", 0x2c000000, 0xfc001200, "ucxccx(s,b),t", pa11, FLAG_STRICT}, +{ "clddx", 0x2c000000, 0xfc00de00, "ucXx(b),t", pa10, 0}, +{ "clddx", 0x2c000000, 0xfc001e00, "ucXx(s,b),t", pa10, 0}, +{ "cstwx", 0x24000200, 0xfc00de00, "ucXt,x(b)", pa10, FLAG_STRICT}, +{ "cstwx", 0x24000200, 0xfc001e00, "ucXt,x(s,b)", pa10, FLAG_STRICT}, +{ "cstwx", 0x24000200, 0xfc00d200, "ucxcCt,x(b)", pa11, FLAG_STRICT}, +{ "cstwx", 0x24000200, 0xfc001200, "ucxcCt,x(s,b)", pa11, FLAG_STRICT}, +{ "cstwx", 0x24000200, 0xfc00de00, "ucXt,x(b)", pa10, 0}, +{ "cstwx", 0x24000200, 0xfc001e00, "ucXt,x(s,b)", pa10, 0}, +{ "cstdx", 0x2c000200, 0xfc00de00, "ucXt,x(b)", pa10, FLAG_STRICT}, +{ "cstdx", 0x2c000200, 0xfc001e00, "ucXt,x(s,b)", pa10, FLAG_STRICT}, +{ "cstdx", 0x2c000200, 0xfc00d200, "ucxcCt,x(b)", pa11, FLAG_STRICT}, +{ "cstdx", 0x2c000200, 0xfc001200, "ucxcCt,x(s,b)", pa11, FLAG_STRICT}, +{ "cstdx", 0x2c000200, 0xfc00de00, "ucXt,x(b)", pa10, 0}, +{ "cstdx", 0x2c000200, 0xfc001e00, "ucXt,x(s,b)", pa10, 0}, +{ "cldws", 0x24001000, 0xfc00de00, "ucM5(b),t", pa10, FLAG_STRICT}, +{ "cldws", 0x24001000, 0xfc001e00, "ucM5(s,b),t", pa10, FLAG_STRICT}, +{ "cldws", 0x24001000, 0xfc00d200, "ucmcc5(b),t", pa11, FLAG_STRICT}, +{ "cldws", 0x24001000, 0xfc001200, "ucmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "cldws", 0x24001000, 0xfc00de00, "ucM5(b),t", pa10, 0}, +{ "cldws", 0x24001000, 0xfc001e00, "ucM5(s,b),t", pa10, 0}, +{ "cldds", 0x2c001000, 0xfc00de00, "ucM5(b),t", pa10, FLAG_STRICT}, +{ "cldds", 0x2c001000, 0xfc001e00, "ucM5(s,b),t", pa10, FLAG_STRICT}, +{ "cldds", 0x2c001000, 0xfc00d200, "ucmcc5(b),t", pa11, FLAG_STRICT}, +{ "cldds", 0x2c001000, 0xfc001200, "ucmcc5(s,b),t", pa11, FLAG_STRICT}, +{ "cldds", 0x2c001000, 0xfc00de00, "ucM5(b),t", pa10, 0}, +{ "cldds", 0x2c001000, 0xfc001e00, "ucM5(s,b),t", pa10, 0}, +{ "cstws", 0x24001200, 0xfc00de00, "ucMt,5(b)", pa10, FLAG_STRICT}, +{ "cstws", 0x24001200, 0xfc001e00, "ucMt,5(s,b)", pa10, FLAG_STRICT}, +{ "cstws", 0x24001200, 0xfc00d200, "ucmcCt,5(b)", pa11, FLAG_STRICT}, +{ "cstws", 0x24001200, 0xfc001200, "ucmcCt,5(s,b)", pa11, FLAG_STRICT}, +{ "cstws", 0x24001200, 0xfc00de00, "ucMt,5(b)", pa10, 0}, +{ "cstws", 0x24001200, 0xfc001e00, "ucMt,5(s,b)", pa10, 0}, +{ "cstds", 0x2c001200, 0xfc00de00, "ucMt,5(b)", pa10, FLAG_STRICT}, +{ "cstds", 0x2c001200, 0xfc001e00, "ucMt,5(s,b)", pa10, FLAG_STRICT}, +{ "cstds", 0x2c001200, 0xfc00d200, "ucmcCt,5(b)", pa11, FLAG_STRICT}, +{ "cstds", 0x2c001200, 0xfc001200, "ucmcCt,5(s,b)", pa11, FLAG_STRICT}, +{ "cstds", 0x2c001200, 0xfc00de00, "ucMt,5(b)", pa10, 0}, +{ "cstds", 0x2c001200, 0xfc001e00, "ucMt,5(s,b)", pa10, 0}, + +/* More pseudo instructions which must follow the main table. */ +{ "call", 0xe800f000, 0xfc1ffffd, "n(b)", pa20, FLAG_STRICT}, +{ "call", 0xe800a000, 0xffe0e000, "nW", pa10, FLAG_STRICT}, +{ "ret", 0xe840d000, 0xfffffffd, "n", pa20, FLAG_STRICT}, + +}; + +#define NUMOPCODES ((sizeof pa_opcodes)/(sizeof pa_opcodes[0])) + +/* SKV 12/18/92. Added some denotations for various operands. */ + +#define PA_IMM11_AT_31 'i' +#define PA_IMM14_AT_31 'j' +#define PA_IMM21_AT_31 'k' +#define PA_DISP12 'w' +#define PA_DISP17 'W' + +#define N_HPPA_OPERAND_FORMATS 5 + +/* Integer register names, indexed by the numbers which appear in the + opcodes. */ +static const char *const reg_names[] = +{ + "flags", "r1", "rp", "r3", "r4", "r5", "r6", "r7", "r8", "r9", + "r10", "r11", "r12", "r13", "r14", "r15", "r16", "r17", "r18", "r19", + "r20", "r21", "r22", "r23", "r24", "r25", "r26", "dp", "ret0", "ret1", + "sp", "r31" +}; + +/* Floating point register names, indexed by the numbers which appear in the + opcodes. */ +static const char *const fp_reg_names[] = +{ + "fpsr", "fpe2", "fpe4", "fpe6", + "fr4", "fr5", "fr6", "fr7", "fr8", + "fr9", "fr10", "fr11", "fr12", "fr13", "fr14", "fr15", + "fr16", "fr17", "fr18", "fr19", "fr20", "fr21", "fr22", "fr23", + "fr24", "fr25", "fr26", "fr27", "fr28", "fr29", "fr30", "fr31" +}; + +typedef unsigned int CORE_ADDR; + +/* Get at various relevant fields of an instruction word. */ + +#define MASK_5 0x1f +#define MASK_10 0x3ff +#define MASK_11 0x7ff +#define MASK_14 0x3fff +#define MASK_16 0xffff +#define MASK_21 0x1fffff + +/* These macros get bit fields using HP's numbering (MSB = 0). */ + +#define GET_FIELD(X, FROM, TO) \ + ((X) >> (31 - (TO)) & ((1 << ((TO) - (FROM) + 1)) - 1)) + +#define GET_BIT(X, WHICH) \ + GET_FIELD (X, WHICH, WHICH) + +/* Some of these have been converted to 2-d arrays because they + consume less storage this way. If the maintenance becomes a + problem, convert them back to const 1-d pointer arrays. */ +static const char *const control_reg[] = +{ + "rctr", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", + "pidr1", "pidr2", "ccr", "sar", "pidr3", "pidr4", + "iva", "eiem", "itmr", "pcsq", "pcoq", "iir", "isr", + "ior", "ipsw", "eirr", "tr0", "tr1", "tr2", "tr3", + "tr4", "tr5", "tr6", "tr7" +}; + +static const char *const compare_cond_names[] = +{ + "", ",=", ",<", ",<=", ",<<", ",<<=", ",sv", ",od", + ",tr", ",<>", ",>=", ",>", ",>>=", ",>>", ",nsv", ",ev" +}; +static const char *const compare_cond_64_names[] = +{ + "", ",*=", ",*<", ",*<=", ",*<<", ",*<<=", ",*sv", ",*od", + ",*tr", ",*<>", ",*>=", ",*>", ",*>>=", ",*>>", ",*nsv", ",*ev" +}; +static const char *const cmpib_cond_64_names[] = +{ + ",*<<", ",*=", ",*<", ",*<=", ",*>>=", ",*<>", ",*>=", ",*>" +}; +static const char *const add_cond_names[] = +{ + "", ",=", ",<", ",<=", ",nuv", ",znv", ",sv", ",od", + ",tr", ",<>", ",>=", ",>", ",uv", ",vnz", ",nsv", ",ev" +}; +static const char *const add_cond_64_names[] = +{ + "", ",*=", ",*<", ",*<=", ",*nuv", ",*znv", ",*sv", ",*od", + ",*tr", ",*<>", ",*>=", ",*>", ",*uv", ",*vnz", ",*nsv", ",*ev" +}; +static const char *const wide_add_cond_names[] = +{ + "", ",=", ",<", ",<=", ",nuv", ",*=", ",*<", ",*<=", + ",tr", ",<>", ",>=", ",>", ",uv", ",*<>", ",*>=", ",*>" +}; +static const char *const logical_cond_names[] = +{ + "", ",=", ",<", ",<=", 0, 0, 0, ",od", + ",tr", ",<>", ",>=", ",>", 0, 0, 0, ",ev"}; +static const char *const logical_cond_64_names[] = +{ + "", ",*=", ",*<", ",*<=", 0, 0, 0, ",*od", + ",*tr", ",*<>", ",*>=", ",*>", 0, 0, 0, ",*ev"}; +static const char *const unit_cond_names[] = +{ + "", ",swz", ",sbz", ",shz", ",sdc", ",swc", ",sbc", ",shc", + ",tr", ",nwz", ",nbz", ",nhz", ",ndc", ",nwc", ",nbc", ",nhc" +}; +static const char *const unit_cond_64_names[] = +{ + "", ",*swz", ",*sbz", ",*shz", ",*sdc", ",*swc", ",*sbc", ",*shc", + ",*tr", ",*nwz", ",*nbz", ",*nhz", ",*ndc", ",*nwc", ",*nbc", ",*nhc" +}; +static const char *const shift_cond_names[] = +{ + "", ",=", ",<", ",od", ",tr", ",<>", ",>=", ",ev" +}; +static const char *const shift_cond_64_names[] = +{ + "", ",*=", ",*<", ",*od", ",*tr", ",*<>", ",*>=", ",*ev" +}; +static const char *const bb_cond_64_names[] = +{ + ",*<", ",*>=" +}; +static const char *const index_compl_names[] = {"", ",m", ",s", ",sm"}; +static const char *const short_ldst_compl_names[] = {"", ",ma", "", ",mb"}; +static const char *const short_bytes_compl_names[] = +{ + "", ",b,m", ",e", ",e,m" +}; +static const char *const float_format_names[] = {",sgl", ",dbl", "", ",quad"}; +static const char *const fcnv_fixed_names[] = {",w", ",dw", "", ",qw"}; +static const char *const fcnv_ufixed_names[] = {",uw", ",udw", "", ",uqw"}; +static const char *const float_comp_names[] = +{ + ",false?", ",false", ",?", ",!<=>", ",=", ",=t", ",?=", ",!<>", + ",!?>=", ",<", ",?<", ",!>=", ",!?>", ",<=", ",?<=", ",!>", + ",!?<=", ",>", ",?>", ",!<=", ",!?<", ",>=", ",?>=", ",!<", + ",!?=", ",<>", ",!=", ",!=t", ",!?", ",<=>", ",true?", ",true" +}; +static const char *const signed_unsigned_names[] = {",u", ",s"}; +static const char *const mix_half_names[] = {",l", ",r"}; +static const char *const saturation_names[] = {",us", ",ss", 0, ""}; +static const char *const read_write_names[] = {",r", ",w"}; +static const char *const add_compl_names[] = { 0, "", ",l", ",tsv" }; + +/* For a bunch of different instructions form an index into a + completer name table. */ +#define GET_COMPL(insn) (GET_FIELD (insn, 26, 26) | \ + GET_FIELD (insn, 18, 18) << 1) + +#define GET_COND(insn) (GET_FIELD ((insn), 16, 18) + \ + (GET_FIELD ((insn), 19, 19) ? 8 : 0)) + +/* Utility function to print registers. Put these first, so gcc's function + inlining can do its stuff. */ + +#define fputs_filtered(STR,F) (*info->fprintf_func) (info->stream, "%s", STR) + +static void +fput_reg (unsigned reg, disassemble_info *info) +{ + (*info->fprintf_func) (info->stream, "%s", reg ? reg_names[reg] : "r0"); +} + +static void +fput_fp_reg (unsigned reg, disassemble_info *info) +{ + (*info->fprintf_func) (info->stream, "%s", reg ? fp_reg_names[reg] : "fr0"); +} + +static void +fput_fp_reg_r (unsigned reg, disassemble_info *info) +{ + /* Special case floating point exception registers. */ + if (reg < 4) + (*info->fprintf_func) (info->stream, "fpe%d", reg * 2 + 1); + else + (*info->fprintf_func) (info->stream, "%sR", + reg ? fp_reg_names[reg] : "fr0"); +} + +static void +fput_creg (unsigned reg, disassemble_info *info) +{ + (*info->fprintf_func) (info->stream, "%s", control_reg[reg]); +} + +/* Print constants with sign. */ + +static void +fput_const (unsigned num, disassemble_info *info) +{ + if ((int) num < 0) + (*info->fprintf_func) (info->stream, "-%x", - (int) num); + else + (*info->fprintf_func) (info->stream, "%x", num); +} + +/* Routines to extract various sized constants out of hppa + instructions. */ + +/* Extract a 3-bit space register number from a be, ble, mtsp or mfsp. */ +static int +extract_3 (unsigned word) +{ + return GET_FIELD (word, 18, 18) << 2 | GET_FIELD (word, 16, 17); +} + +static int +extract_5_load (unsigned word) +{ + return low_sign_extend (word >> 16 & MASK_5, 5); +} + +/* Extract the immediate field from a st{bhw}s instruction. */ + +static int +extract_5_store (unsigned word) +{ + return low_sign_extend (word & MASK_5, 5); +} + +/* Extract the immediate field from a break instruction. */ + +static unsigned +extract_5r_store (unsigned word) +{ + return (word & MASK_5); +} + +/* Extract the immediate field from a {sr}sm instruction. */ + +static unsigned +extract_5R_store (unsigned word) +{ + return (word >> 16 & MASK_5); +} + +/* Extract the 10 bit immediate field from a {sr}sm instruction. */ + +static unsigned +extract_10U_store (unsigned word) +{ + return (word >> 16 & MASK_10); +} + +/* Extract the immediate field from a bb instruction. */ + +static unsigned +extract_5Q_store (unsigned word) +{ + return (word >> 21 & MASK_5); +} + +/* Extract an 11 bit immediate field. */ + +static int +extract_11 (unsigned word) +{ + return low_sign_extend (word & MASK_11, 11); +} + +/* Extract a 14 bit immediate field. */ + +static int +extract_14 (unsigned word) +{ + return low_sign_extend (word & MASK_14, 14); +} + +/* Extract a 16 bit immediate field (PA2.0 wide only). */ + +static int +extract_16 (unsigned word) +{ + int m15, m0, m1; + + m0 = GET_BIT (word, 16); + m1 = GET_BIT (word, 17); + m15 = GET_BIT (word, 31); + word = (word >> 1) & 0x1fff; + word = word | (m15 << 15) | ((m15 ^ m0) << 14) | ((m15 ^ m1) << 13); + return sign_extend (word, 16); +} + +/* Extract a 21 bit constant. */ + +static int +extract_21 (unsigned word) +{ + int val; + + word &= MASK_21; + word <<= 11; + val = GET_FIELD (word, 20, 20); + val <<= 11; + val |= GET_FIELD (word, 9, 19); + val <<= 2; + val |= GET_FIELD (word, 5, 6); + val <<= 5; + val |= GET_FIELD (word, 0, 4); + val <<= 2; + val |= GET_FIELD (word, 7, 8); + return sign_extend (val, 21) << 11; +} + +/* Extract a 12 bit constant from branch instructions. */ + +static int +extract_12 (unsigned word) +{ + return sign_extend (GET_FIELD (word, 19, 28) + | GET_FIELD (word, 29, 29) << 10 + | (word & 0x1) << 11, 12) << 2; +} + +/* Extract a 17 bit constant from branch instructions, returning the + 19 bit signed value. */ + +static int +extract_17 (unsigned word) +{ + return sign_extend (GET_FIELD (word, 19, 28) + | GET_FIELD (word, 29, 29) << 10 + | GET_FIELD (word, 11, 15) << 11 + | (word & 0x1) << 16, 17) << 2; +} + +static int +extract_22 (unsigned word) +{ + return sign_extend (GET_FIELD (word, 19, 28) + | GET_FIELD (word, 29, 29) << 10 + | GET_FIELD (word, 11, 15) << 11 + | GET_FIELD (word, 6, 10) << 16 + | (word & 0x1) << 21, 22) << 2; +} + +/* Print one instruction. */ + +int +print_insn_hppa (bfd_vma memaddr, disassemble_info *info) +{ + bfd_byte buffer[4]; + unsigned int insn, i; + + { + int status = + (*info->read_memory_func) (memaddr, buffer, sizeof (buffer), info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + } + + insn = bfd_getb32 (buffer); + + for (i = 0; i < NUMOPCODES; ++i) + { + const struct pa_opcode *opcode = &pa_opcodes[i]; + + if ((insn & opcode->mask) == opcode->match) + { + const char *s; +#ifndef BFD64 + if (opcode->arch == pa20w) + continue; +#endif + (*info->fprintf_func) (info->stream, "%s", opcode->name); + + if (!strchr ("cfCY?-+nHNZFIuv{", opcode->args[0])) + (*info->fprintf_func) (info->stream, " "); + for (s = opcode->args; *s != '\0'; ++s) + { + switch (*s) + { + case 'x': + fput_reg (GET_FIELD (insn, 11, 15), info); + break; + case 'a': + case 'b': + fput_reg (GET_FIELD (insn, 6, 10), info); + break; + case '^': + fput_creg (GET_FIELD (insn, 6, 10), info); + break; + case 't': + fput_reg (GET_FIELD (insn, 27, 31), info); + break; + + /* Handle floating point registers. */ + case 'f': + switch (*++s) + { + case 't': + fput_fp_reg (GET_FIELD (insn, 27, 31), info); + break; + case 'T': + if (GET_FIELD (insn, 25, 25)) + fput_fp_reg_r (GET_FIELD (insn, 27, 31), info); + else + fput_fp_reg (GET_FIELD (insn, 27, 31), info); + break; + case 'a': + if (GET_FIELD (insn, 25, 25)) + fput_fp_reg_r (GET_FIELD (insn, 6, 10), info); + else + fput_fp_reg (GET_FIELD (insn, 6, 10), info); + break; + + /* 'fA' will not generate a space before the regsiter + name. Normally that is fine. Except that it + causes problems with xmpyu which has no FP format + completer. */ + case 'X': + fputs_filtered (" ", info); + /* FALLTHRU */ + + case 'A': + if (GET_FIELD (insn, 24, 24)) + fput_fp_reg_r (GET_FIELD (insn, 6, 10), info); + else + fput_fp_reg (GET_FIELD (insn, 6, 10), info); + break; + case 'b': + if (GET_FIELD (insn, 25, 25)) + fput_fp_reg_r (GET_FIELD (insn, 11, 15), info); + else + fput_fp_reg (GET_FIELD (insn, 11, 15), info); + break; + case 'B': + if (GET_FIELD (insn, 19, 19)) + fput_fp_reg_r (GET_FIELD (insn, 11, 15), info); + else + fput_fp_reg (GET_FIELD (insn, 11, 15), info); + break; + case 'C': + { + int reg = GET_FIELD (insn, 21, 22); + reg |= GET_FIELD (insn, 16, 18) << 2; + if (GET_FIELD (insn, 23, 23) != 0) + fput_fp_reg_r (reg, info); + else + fput_fp_reg (reg, info); + break; + } + case 'i': + { + int reg = GET_FIELD (insn, 6, 10); + + reg |= (GET_FIELD (insn, 26, 26) << 4); + fput_fp_reg (reg, info); + break; + } + case 'j': + { + int reg = GET_FIELD (insn, 11, 15); + + reg |= (GET_FIELD (insn, 26, 26) << 4); + fput_fp_reg (reg, info); + break; + } + case 'k': + { + int reg = GET_FIELD (insn, 27, 31); + + reg |= (GET_FIELD (insn, 26, 26) << 4); + fput_fp_reg (reg, info); + break; + } + case 'l': + { + int reg = GET_FIELD (insn, 21, 25); + + reg |= (GET_FIELD (insn, 26, 26) << 4); + fput_fp_reg (reg, info); + break; + } + case 'm': + { + int reg = GET_FIELD (insn, 16, 20); + + reg |= (GET_FIELD (insn, 26, 26) << 4); + fput_fp_reg (reg, info); + break; + } + + /* 'fe' will not generate a space before the register + name. Normally that is fine. Except that it + causes problems with fstw fe,y(b) which has no FP + format completer. */ + case 'E': + fputs_filtered (" ", info); + /* FALLTHRU */ + + case 'e': + if (GET_FIELD (insn, 30, 30)) + fput_fp_reg_r (GET_FIELD (insn, 11, 15), info); + else + fput_fp_reg (GET_FIELD (insn, 11, 15), info); + break; + case 'x': + fput_fp_reg (GET_FIELD (insn, 11, 15), info); + break; + } + break; + + case '5': + fput_const (extract_5_load (insn), info); + break; + case 's': + { + int space = GET_FIELD (insn, 16, 17); + /* Zero means implicit addressing, not use of sr0. */ + if (space != 0) + (*info->fprintf_func) (info->stream, "sr%d", space); + } + break; + + case 'S': + (*info->fprintf_func) (info->stream, "sr%d", + extract_3 (insn)); + break; + + /* Handle completers. */ + case 'c': + switch (*++s) + { + case 'x': + (*info->fprintf_func) + (info->stream, "%s", + index_compl_names[GET_COMPL (insn)]); + break; + case 'X': + (*info->fprintf_func) + (info->stream, "%s ", + index_compl_names[GET_COMPL (insn)]); + break; + case 'm': + (*info->fprintf_func) + (info->stream, "%s", + short_ldst_compl_names[GET_COMPL (insn)]); + break; + case 'M': + (*info->fprintf_func) + (info->stream, "%s ", + short_ldst_compl_names[GET_COMPL (insn)]); + break; + case 'A': + (*info->fprintf_func) + (info->stream, "%s ", + short_bytes_compl_names[GET_COMPL (insn)]); + break; + case 's': + (*info->fprintf_func) + (info->stream, "%s", + short_bytes_compl_names[GET_COMPL (insn)]); + break; + case 'c': + case 'C': + switch (GET_FIELD (insn, 20, 21)) + { + case 1: + (*info->fprintf_func) (info->stream, ",bc "); + break; + case 2: + (*info->fprintf_func) (info->stream, ",sl "); + break; + default: + (*info->fprintf_func) (info->stream, " "); + } + break; + case 'd': + switch (GET_FIELD (insn, 20, 21)) + { + case 1: + (*info->fprintf_func) (info->stream, ",co "); + break; + default: + (*info->fprintf_func) (info->stream, " "); + } + break; + case 'o': + (*info->fprintf_func) (info->stream, ",o"); + break; + case 'g': + (*info->fprintf_func) (info->stream, ",gate"); + break; + case 'p': + (*info->fprintf_func) (info->stream, ",l,push"); + break; + case 'P': + (*info->fprintf_func) (info->stream, ",pop"); + break; + case 'l': + case 'L': + (*info->fprintf_func) (info->stream, ",l"); + break; + case 'w': + (*info->fprintf_func) + (info->stream, "%s ", + read_write_names[GET_FIELD (insn, 25, 25)]); + break; + case 'W': + (*info->fprintf_func) (info->stream, ",w "); + break; + case 'r': + if (GET_FIELD (insn, 23, 26) == 5) + (*info->fprintf_func) (info->stream, ",r"); + break; + case 'Z': + if (GET_FIELD (insn, 26, 26)) + (*info->fprintf_func) (info->stream, ",m "); + else + (*info->fprintf_func) (info->stream, " "); + break; + case 'i': + if (GET_FIELD (insn, 25, 25)) + (*info->fprintf_func) (info->stream, ",i"); + break; + case 'z': + if (!GET_FIELD (insn, 21, 21)) + (*info->fprintf_func) (info->stream, ",z"); + break; + case 'a': + (*info->fprintf_func) + (info->stream, "%s", + add_compl_names[GET_FIELD (insn, 20, 21)]); + break; + case 'Y': + (*info->fprintf_func) + (info->stream, ",dc%s", + add_compl_names[GET_FIELD (insn, 20, 21)]); + break; + case 'y': + (*info->fprintf_func) + (info->stream, ",c%s", + add_compl_names[GET_FIELD (insn, 20, 21)]); + break; + case 'v': + if (GET_FIELD (insn, 20, 20)) + (*info->fprintf_func) (info->stream, ",tsv"); + break; + case 't': + (*info->fprintf_func) (info->stream, ",tc"); + if (GET_FIELD (insn, 20, 20)) + (*info->fprintf_func) (info->stream, ",tsv"); + break; + case 'B': + (*info->fprintf_func) (info->stream, ",db"); + if (GET_FIELD (insn, 20, 20)) + (*info->fprintf_func) (info->stream, ",tsv"); + break; + case 'b': + (*info->fprintf_func) (info->stream, ",b"); + if (GET_FIELD (insn, 20, 20)) + (*info->fprintf_func) (info->stream, ",tsv"); + break; + case 'T': + if (GET_FIELD (insn, 25, 25)) + (*info->fprintf_func) (info->stream, ",tc"); + break; + case 'S': + /* EXTRD/W has a following condition. */ + if (*(s + 1) == '?') + (*info->fprintf_func) + (info->stream, "%s", + signed_unsigned_names[GET_FIELD (insn, 21, 21)]); + else + (*info->fprintf_func) + (info->stream, "%s ", + signed_unsigned_names[GET_FIELD (insn, 21, 21)]); + break; + case 'h': + (*info->fprintf_func) + (info->stream, "%s", + mix_half_names[GET_FIELD (insn, 17, 17)]); + break; + case 'H': + (*info->fprintf_func) + (info->stream, "%s ", + saturation_names[GET_FIELD (insn, 24, 25)]); + break; + case '*': + (*info->fprintf_func) + (info->stream, ",%d%d%d%d ", + GET_FIELD (insn, 17, 18), GET_FIELD (insn, 20, 21), + GET_FIELD (insn, 22, 23), GET_FIELD (insn, 24, 25)); + break; + + case 'q': + { + int m, a; + + m = GET_FIELD (insn, 28, 28); + a = GET_FIELD (insn, 29, 29); + + if (m && !a) + fputs_filtered (",ma ", info); + else if (m && a) + fputs_filtered (",mb ", info); + else + fputs_filtered (" ", info); + break; + } + + case 'J': + { + int opc = GET_FIELD (insn, 0, 5); + + if (opc == 0x16 || opc == 0x1e) + { + if (GET_FIELD (insn, 29, 29) == 0) + fputs_filtered (",ma ", info); + else + fputs_filtered (",mb ", info); + } + else + fputs_filtered (" ", info); + break; + } + + case 'e': + { + int opc = GET_FIELD (insn, 0, 5); + + if (opc == 0x13 || opc == 0x1b) + { + if (GET_FIELD (insn, 18, 18) == 1) + fputs_filtered (",mb ", info); + else + fputs_filtered (",ma ", info); + } + else if (opc == 0x17 || opc == 0x1f) + { + if (GET_FIELD (insn, 31, 31) == 1) + fputs_filtered (",ma ", info); + else + fputs_filtered (",mb ", info); + } + else + fputs_filtered (" ", info); + + break; + } + } + break; + + /* Handle conditions. */ + case '?': + { + s++; + switch (*s) + { + case 'f': + (*info->fprintf_func) + (info->stream, "%s ", + float_comp_names[GET_FIELD (insn, 27, 31)]); + break; + + /* These four conditions are for the set of instructions + which distinguish true/false conditions by opcode + rather than by the 'f' bit (sigh): comb, comib, + addb, addib. */ + case 't': + fputs_filtered + (compare_cond_names[GET_FIELD (insn, 16, 18)], info); + break; + case 'n': + fputs_filtered + (compare_cond_names[GET_FIELD (insn, 16, 18) + + GET_FIELD (insn, 4, 4) * 8], + info); + break; + case 'N': + fputs_filtered + (compare_cond_64_names[GET_FIELD (insn, 16, 18) + + GET_FIELD (insn, 2, 2) * 8], + info); + break; + case 'Q': + fputs_filtered + (cmpib_cond_64_names[GET_FIELD (insn, 16, 18)], + info); + break; + case '@': + fputs_filtered + (add_cond_names[GET_FIELD (insn, 16, 18) + + GET_FIELD (insn, 4, 4) * 8], + info); + break; + case 's': + (*info->fprintf_func) + (info->stream, "%s ", + compare_cond_names[GET_COND (insn)]); + break; + case 'S': + (*info->fprintf_func) + (info->stream, "%s ", + compare_cond_64_names[GET_COND (insn)]); + break; + case 'a': + (*info->fprintf_func) + (info->stream, "%s ", + add_cond_names[GET_COND (insn)]); + break; + case 'A': + (*info->fprintf_func) + (info->stream, "%s ", + add_cond_64_names[GET_COND (insn)]); + break; + case 'd': + (*info->fprintf_func) + (info->stream, "%s", + add_cond_names[GET_FIELD (insn, 16, 18)]); + break; + + case 'W': + (*info->fprintf_func) + (info->stream, "%s", + wide_add_cond_names[GET_FIELD (insn, 16, 18) + + GET_FIELD (insn, 4, 4) * 8]); + break; + + case 'l': + (*info->fprintf_func) + (info->stream, "%s ", + logical_cond_names[GET_COND (insn)]); + break; + case 'L': + (*info->fprintf_func) + (info->stream, "%s ", + logical_cond_64_names[GET_COND (insn)]); + break; + case 'u': + (*info->fprintf_func) + (info->stream, "%s ", + unit_cond_names[GET_COND (insn)]); + break; + case 'U': + (*info->fprintf_func) + (info->stream, "%s ", + unit_cond_64_names[GET_COND (insn)]); + break; + case 'y': + case 'x': + case 'b': + (*info->fprintf_func) + (info->stream, "%s", + shift_cond_names[GET_FIELD (insn, 16, 18)]); + + /* If the next character in args is 'n', it will handle + putting out the space. */ + if (s[1] != 'n') + (*info->fprintf_func) (info->stream, " "); + break; + case 'X': + (*info->fprintf_func) + (info->stream, "%s ", + shift_cond_64_names[GET_FIELD (insn, 16, 18)]); + break; + case 'B': + (*info->fprintf_func) + (info->stream, "%s", + bb_cond_64_names[GET_FIELD (insn, 16, 16)]); + + /* If the next character in args is 'n', it will handle + putting out the space. */ + if (s[1] != 'n') + (*info->fprintf_func) (info->stream, " "); + break; + } + break; + } + + case 'V': + fput_const (extract_5_store (insn), info); + break; + case 'r': + fput_const (extract_5r_store (insn), info); + break; + case 'R': + fput_const (extract_5R_store (insn), info); + break; + case 'U': + fput_const (extract_10U_store (insn), info); + break; + case 'B': + case 'Q': + fput_const (extract_5Q_store (insn), info); + break; + case 'i': + fput_const (extract_11 (insn), info); + break; + case 'j': + fput_const (extract_14 (insn), info); + break; + case 'k': + fputs_filtered ("L%", info); + fput_const (extract_21 (insn), info); + break; + case '<': + case 'l': + /* 16-bit long disp., PA2.0 wide only. */ + fput_const (extract_16 (insn), info); + break; + case 'n': + if (insn & 0x2) + (*info->fprintf_func) (info->stream, ",n "); + else + (*info->fprintf_func) (info->stream, " "); + break; + case 'N': + if ((insn & 0x20) && s[1]) + (*info->fprintf_func) (info->stream, ",n "); + else if (insn & 0x20) + (*info->fprintf_func) (info->stream, ",n"); + else if (s[1]) + (*info->fprintf_func) (info->stream, " "); + break; + case 'w': + (*info->print_address_func) + (memaddr + 8 + extract_12 (insn), info); + break; + case 'W': + /* 17 bit PC-relative branch. */ + (*info->print_address_func) + ((memaddr + 8 + extract_17 (insn)), info); + break; + case 'z': + /* 17 bit displacement. This is an offset from a register + so it gets disasssembled as just a number, not any sort + of address. */ + fput_const (extract_17 (insn), info); + break; + + case 'Z': + /* addil %r1 implicit output. */ + fputs_filtered ("r1", info); + break; + + case 'Y': + /* be,l %sr0,%r31 implicit output. */ + fputs_filtered ("sr0,r31", info); + break; + + case '@': + (*info->fprintf_func) (info->stream, "0"); + break; + + case '.': + (*info->fprintf_func) (info->stream, "%d", + GET_FIELD (insn, 24, 25)); + break; + case '*': + (*info->fprintf_func) (info->stream, "%d", + GET_FIELD (insn, 22, 25)); + break; + case '!': + fputs_filtered ("sar", info); + break; + case 'p': + (*info->fprintf_func) (info->stream, "%d", + 31 - GET_FIELD (insn, 22, 26)); + break; + case '~': + { + int num; + num = GET_FIELD (insn, 20, 20) << 5; + num |= GET_FIELD (insn, 22, 26); + (*info->fprintf_func) (info->stream, "%d", 63 - num); + break; + } + case 'P': + (*info->fprintf_func) (info->stream, "%d", + GET_FIELD (insn, 22, 26)); + break; + case 'q': + { + int num; + num = GET_FIELD (insn, 20, 20) << 5; + num |= GET_FIELD (insn, 22, 26); + (*info->fprintf_func) (info->stream, "%d", num); + break; + } + case 'T': + (*info->fprintf_func) (info->stream, "%d", + 32 - GET_FIELD (insn, 27, 31)); + break; + case '%': + { + int num; + num = (GET_FIELD (insn, 23, 23) + 1) * 32; + num -= GET_FIELD (insn, 27, 31); + (*info->fprintf_func) (info->stream, "%d", num); + break; + } + case '|': + { + int num; + num = (GET_FIELD (insn, 19, 19) + 1) * 32; + num -= GET_FIELD (insn, 27, 31); + (*info->fprintf_func) (info->stream, "%d", num); + break; + } + case '$': + fput_const (GET_FIELD (insn, 20, 28), info); + break; + case 'A': + fput_const (GET_FIELD (insn, 6, 18), info); + break; + case 'D': + fput_const (GET_FIELD (insn, 6, 31), info); + break; + case 'v': + (*info->fprintf_func) (info->stream, ",%d", + GET_FIELD (insn, 23, 25)); + break; + case 'O': + fput_const ((GET_FIELD (insn, 6,20) << 5 | + GET_FIELD (insn, 27, 31)), info); + break; + case 'o': + fput_const (GET_FIELD (insn, 6, 20), info); + break; + case '2': + fput_const ((GET_FIELD (insn, 6, 22) << 5 | + GET_FIELD (insn, 27, 31)), info); + break; + case '1': + fput_const ((GET_FIELD (insn, 11, 20) << 5 | + GET_FIELD (insn, 27, 31)), info); + break; + case '0': + fput_const ((GET_FIELD (insn, 16, 20) << 5 | + GET_FIELD (insn, 27, 31)), info); + break; + case 'u': + (*info->fprintf_func) (info->stream, ",%d", + GET_FIELD (insn, 23, 25)); + break; + case 'F': + /* If no destination completer and not before a completer + for fcmp, need a space here. */ + if (s[1] == 'G' || s[1] == '?') + fputs_filtered + (float_format_names[GET_FIELD (insn, 19, 20)], info); + else + (*info->fprintf_func) + (info->stream, "%s ", + float_format_names[GET_FIELD (insn, 19, 20)]); + break; + case 'G': + (*info->fprintf_func) + (info->stream, "%s ", + float_format_names[GET_FIELD (insn, 17, 18)]); + break; + case 'H': + if (GET_FIELD (insn, 26, 26) == 1) + (*info->fprintf_func) (info->stream, "%s ", + float_format_names[0]); + else + (*info->fprintf_func) (info->stream, "%s ", + float_format_names[1]); + break; + case 'I': + /* If no destination completer and not before a completer + for fcmp, need a space here. */ + if (s[1] == '?') + fputs_filtered + (float_format_names[GET_FIELD (insn, 20, 20)], info); + else + (*info->fprintf_func) + (info->stream, "%s ", + float_format_names[GET_FIELD (insn, 20, 20)]); + break; + + case 'J': + fput_const (extract_14 (insn), info); + break; + + case '#': + { + int sign = GET_FIELD (insn, 31, 31); + int imm10 = GET_FIELD (insn, 18, 27); + int disp; + + if (sign) + disp = (-1 << 10) | imm10; + else + disp = imm10; + + disp <<= 3; + fput_const (disp, info); + break; + } + case 'K': + case 'd': + { + int sign = GET_FIELD (insn, 31, 31); + int imm11 = GET_FIELD (insn, 18, 28); + int disp; + + if (sign) + disp = (-1 << 11) | imm11; + else + disp = imm11; + + disp <<= 2; + fput_const (disp, info); + break; + } + + case '>': + case 'y': + { + /* 16-bit long disp., PA2.0 wide only. */ + int disp = extract_16 (insn); + disp &= ~3; + fput_const (disp, info); + break; + } + + case '&': + { + /* 16-bit long disp., PA2.0 wide only. */ + int disp = extract_16 (insn); + disp &= ~7; + fput_const (disp, info); + break; + } + + case '_': + break; /* Dealt with by '{' */ + + case '{': + { + int sub = GET_FIELD (insn, 14, 16); + int df = GET_FIELD (insn, 17, 18); + int sf = GET_FIELD (insn, 19, 20); + const char * const * source = float_format_names; + const char * const * dest = float_format_names; + const char *t = ""; + + if (sub == 4) + { + fputs_filtered (",UND ", info); + break; + } + if ((sub & 3) == 3) + t = ",t"; + if ((sub & 3) == 1) + source = sub & 4 ? fcnv_ufixed_names : fcnv_fixed_names; + if (sub & 2) + dest = sub & 4 ? fcnv_ufixed_names : fcnv_fixed_names; + + (*info->fprintf_func) (info->stream, "%s%s%s ", + t, source[sf], dest[df]); + break; + } + + case 'm': + { + int y = GET_FIELD (insn, 16, 18); + + if (y != 1) + fput_const ((y ^ 1) - 1, info); + } + break; + + case 'h': + { + int cbit; + + cbit = GET_FIELD (insn, 16, 18); + + if (cbit > 0) + (*info->fprintf_func) (info->stream, ",%d", cbit - 1); + break; + } + + case '=': + { + int cond = GET_FIELD (insn, 27, 31); + + switch (cond) + { + case 0: fputs_filtered (" ", info); break; + case 1: fputs_filtered ("acc ", info); break; + case 2: fputs_filtered ("rej ", info); break; + case 5: fputs_filtered ("acc8 ", info); break; + case 6: fputs_filtered ("rej8 ", info); break; + case 9: fputs_filtered ("acc6 ", info); break; + case 13: fputs_filtered ("acc4 ", info); break; + case 17: fputs_filtered ("acc2 ", info); break; + default: break; + } + break; + } + + case 'X': + (*info->print_address_func) + (memaddr + 8 + extract_22 (insn), info); + break; + case 'L': + fputs_filtered (",rp", info); + break; + default: + (*info->fprintf_func) (info->stream, "%c", *s); + break; + } + } + return sizeof (insn); + } + } + (*info->fprintf_func) (info->stream, "#%8x", insn); + return sizeof (insn); +} diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index f5aed72e8f..f05c8658c1 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -116,6 +116,8 @@ float32 float32_default_nan(float_status *status) #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ defined(TARGET_XTENSA) || defined(TARGET_S390X) || defined(TARGET_TRICORE) return const_float32(0x7FC00000); +#elif defined(TARGET_HPPA) + return const_float32(0x7FA00000); #else if (status->snan_bit_is_one) { return const_float32(0x7FBFFFFF); @@ -139,6 +141,8 @@ float64 float64_default_nan(float_status *status) #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ defined(TARGET_S390X) return const_float64(LIT64(0x7FF8000000000000)); +#elif defined(TARGET_HPPA) + return const_float64(LIT64(0x7FF4000000000000)); #else if (status->snan_bit_is_one) { return const_float64(LIT64(0x7FF7FFFFFFFFFFFF)); @@ -361,7 +365,14 @@ float32 float32_maybe_silence_nan(float32 a_, float_status *status) { if (float32_is_signaling_nan(a_, status)) { if (status->snan_bit_is_one) { +#ifdef TARGET_HPPA + uint32_t a = float32_val(a_); + a &= ~0x00400000; + a |= 0x00200000; + return make_float32(a); +#else return float32_default_nan(status); +#endif } else { uint32_t a = float32_val(a_); a |= (1 << 22); @@ -449,7 +460,7 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, return 1; } } -#elif defined(TARGET_MIPS) +#elif defined(TARGET_MIPS) || defined(TARGET_HPPA) static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, flag aIsLargerSignificand) { @@ -794,7 +805,14 @@ float64 float64_maybe_silence_nan(float64 a_, float_status *status) { if (float64_is_signaling_nan(a_, status)) { if (status->snan_bit_is_one) { +#ifdef TARGET_HPPA + uint64_t a = float64_val(a_); + a &= ~0x0008000000000000ULL; + a |= 0x0004000000000000ULL; + return make_float64(a); +#else return float64_default_nan(status); +#endif } else { uint64_t a = float64_val(a_); a |= LIT64(0x0008000000000000); diff --git a/linux-user/alpha/target_syscall.h b/linux-user/alpha/target_syscall.h index b580fc5b37..3426cc5b4e 100644 --- a/linux-user/alpha/target_syscall.h +++ b/linux-user/alpha/target_syscall.h @@ -235,6 +235,8 @@ struct target_pt_regs { #define TARGET_ENOTRECOVERABLE 137 #undef TARGET_ERFKILL #define TARGET_ERFKILL 138 +#undef TARGET_EHWPOISON +#define TARGET_EHWPOISON 139 // For sys_osf_getsysinfo #define TARGET_GSI_UACPROC 8 diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 547053c27a..51794bbb45 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1215,6 +1215,30 @@ static inline void init_thread(struct target_pt_regs *regs, #endif /* TARGET_TILEGX */ +#ifdef TARGET_HPPA + +#define ELF_START_MMAP 0x80000000 +#define ELF_CLASS ELFCLASS32 +#define ELF_ARCH EM_PARISC +#define ELF_PLATFORM "PARISC" +#define STACK_GROWS_DOWN 0 +#define STACK_ALIGNMENT 64 + +static inline void init_thread(struct target_pt_regs *regs, + struct image_info *infop) +{ + regs->iaoq[0] = infop->entry; + regs->iaoq[1] = infop->entry + 4; + regs->gr[23] = 0; + regs->gr[24] = infop->arg_start; + regs->gr[25] = (infop->arg_end - infop->arg_start) / sizeof(abi_ulong); + /* The top-of-stack contains a linkage buffer. */ + regs->gr[30] = infop->start_stack + 64; + regs->gr[31] = infop->entry; +} + +#endif /* TARGET_HPPA */ + #ifndef ELF_PLATFORM #define ELF_PLATFORM (NULL) #endif @@ -1231,6 +1255,14 @@ static inline void init_thread(struct target_pt_regs *regs, #define ELF_HWCAP 0 #endif +#ifndef STACK_GROWS_DOWN +#define STACK_GROWS_DOWN 1 +#endif + +#ifndef STACK_ALIGNMENT +#define STACK_ALIGNMENT 16 +#endif + #ifdef TARGET_ABI32 #undef ELF_CLASS #define ELF_CLASS ELFCLASS32 @@ -1374,45 +1406,78 @@ static abi_ulong copy_elf_strings(int argc, char **argv, char *scratch, abi_ulong p, abi_ulong stack_limit) { char *tmp; - int len, offset; + int len, i; abi_ulong top = p; if (!p) { return 0; /* bullet-proofing */ } - offset = ((p - 1) % TARGET_PAGE_SIZE) + 1; + if (STACK_GROWS_DOWN) { + int offset = ((p - 1) % TARGET_PAGE_SIZE) + 1; + for (i = argc - 1; i >= 0; --i) { + tmp = argv[i]; + if (!tmp) { + fprintf(stderr, "VFS: argc is wrong"); + exit(-1); + } + len = strlen(tmp) + 1; + tmp += len; - while (argc-- > 0) { - tmp = argv[argc]; - if (!tmp) { - fprintf(stderr, "VFS: argc is wrong"); - exit(-1); + if (len > (p - stack_limit)) { + return 0; + } + while (len) { + int bytes_to_copy = (len > offset) ? offset : len; + tmp -= bytes_to_copy; + p -= bytes_to_copy; + offset -= bytes_to_copy; + len -= bytes_to_copy; + + memcpy_fromfs(scratch + offset, tmp, bytes_to_copy); + + if (offset == 0) { + memcpy_to_target(p, scratch, top - p); + top = p; + offset = TARGET_PAGE_SIZE; + } + } } - len = strlen(tmp) + 1; - tmp += len; - - if (len > (p - stack_limit)) { - return 0; + if (p != top) { + memcpy_to_target(p, scratch + offset, top - p); } - while (len) { - int bytes_to_copy = (len > offset) ? offset : len; - tmp -= bytes_to_copy; - p -= bytes_to_copy; - offset -= bytes_to_copy; - len -= bytes_to_copy; - - memcpy_fromfs(scratch + offset, tmp, bytes_to_copy); - - if (offset == 0) { - memcpy_to_target(p, scratch, top - p); - top = p; - offset = TARGET_PAGE_SIZE; + } else { + int remaining = TARGET_PAGE_SIZE - (p % TARGET_PAGE_SIZE); + for (i = 0; i < argc; ++i) { + tmp = argv[i]; + if (!tmp) { + fprintf(stderr, "VFS: argc is wrong"); + exit(-1); + } + len = strlen(tmp) + 1; + if (len > (stack_limit - p)) { + return 0; + } + while (len) { + int bytes_to_copy = (len > remaining) ? remaining : len; + + memcpy_fromfs(scratch + (p - top), tmp, bytes_to_copy); + + tmp += bytes_to_copy; + remaining -= bytes_to_copy; + p += bytes_to_copy; + len -= bytes_to_copy; + + if (remaining == 0) { + memcpy_to_target(top, scratch, p - top); + top = p; + remaining = TARGET_PAGE_SIZE; + } } } - } - if (offset) { - memcpy_to_target(p, scratch + offset, top - p); + if (p != top) { + memcpy_to_target(top, scratch, p - top); + } } return p; @@ -1447,11 +1512,15 @@ static abi_ulong setup_arg_pages(struct linux_binprm *bprm, } /* We reserve one extra page at the top of the stack as guard. */ - target_mprotect(error, guard, PROT_NONE); - - info->stack_limit = error + guard; - - return info->stack_limit + size - sizeof(void *); + if (STACK_GROWS_DOWN) { + target_mprotect(error, guard, PROT_NONE); + info->stack_limit = error + guard; + return info->stack_limit + size - sizeof(void *); + } else { + target_mprotect(error + size, guard, PROT_NONE); + info->stack_limit = error + size; + return error; + } } /* Map and zero the bss. We need to explicitly zero any fractional pages @@ -1529,7 +1598,7 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, struct image_info *interp_info) { abi_ulong sp; - abi_ulong sp_auxv; + abi_ulong u_argc, u_argv, u_envp, u_auxv; int size; int i; abi_ulong u_rand_bytes; @@ -1558,10 +1627,25 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, k_platform = ELF_PLATFORM; if (k_platform) { size_t len = strlen(k_platform) + 1; - sp -= (len + n - 1) & ~(n - 1); - u_platform = sp; - /* FIXME - check return value of memcpy_to_target() for failure */ - memcpy_to_target(sp, k_platform, len); + if (STACK_GROWS_DOWN) { + sp -= (len + n - 1) & ~(n - 1); + u_platform = sp; + /* FIXME - check return value of memcpy_to_target() for failure */ + memcpy_to_target(sp, k_platform, len); + } else { + memcpy_to_target(sp, k_platform, len); + u_platform = sp; + sp += len + 1; + } + } + + /* Provide 16 byte alignment for the PRNG, and basic alignment for + * the argv and envp pointers. + */ + if (STACK_GROWS_DOWN) { + sp = QEMU_ALIGN_DOWN(sp, 16); + } else { + sp = QEMU_ALIGN_UP(sp, 16); } /* @@ -1571,15 +1655,17 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, for (i = 0; i < 16; i++) { k_rand_bytes[i] = rand(); } - sp -= 16; - u_rand_bytes = sp; - /* FIXME - check return value of memcpy_to_target() for failure */ - memcpy_to_target(sp, k_rand_bytes, 16); + if (STACK_GROWS_DOWN) { + sp -= 16; + u_rand_bytes = sp; + /* FIXME - check return value of memcpy_to_target() for failure */ + memcpy_to_target(sp, k_rand_bytes, 16); + } else { + memcpy_to_target(sp, k_rand_bytes, 16); + u_rand_bytes = sp; + sp += 16; + } - /* - * Force 16 byte _final_ alignment here for generality. - */ - sp = sp &~ (abi_ulong)15; size = (DLINFO_ITEMS + 1) * 2; if (k_platform) size += 2; @@ -1592,20 +1678,31 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, size += envc + argc + 2; size += 1; /* argc itself */ size *= n; - if (size & 15) - sp -= 16 - (size & 15); + + /* Allocate space and finalize stack alignment for entry now. */ + if (STACK_GROWS_DOWN) { + u_argc = QEMU_ALIGN_DOWN(sp - size, STACK_ALIGNMENT); + sp = u_argc; + } else { + u_argc = sp; + sp = QEMU_ALIGN_UP(sp + size, STACK_ALIGNMENT); + } + + u_argv = u_argc + n; + u_envp = u_argv + (argc + 1) * n; + u_auxv = u_envp + (envc + 1) * n; + info->saved_auxv = u_auxv; + info->arg_start = u_argv; + info->arg_end = u_argv + argc * n; /* This is correct because Linux defines * elf_addr_t as Elf32_Off / Elf64_Off */ #define NEW_AUX_ENT(id, val) do { \ - sp -= n; put_user_ual(val, sp); \ - sp -= n; put_user_ual(id, sp); \ + put_user_ual(id, u_auxv); u_auxv += n; \ + put_user_ual(val, u_auxv); u_auxv += n; \ } while(0) - sp_auxv = sp; - NEW_AUX_ENT (AT_NULL, 0); - /* There must be exactly DLINFO_ITEMS entries here. */ NEW_AUX_ENT(AT_PHDR, (abi_ulong)(info->load_addr + exec->e_phoff)); NEW_AUX_ENT(AT_PHENT, (abi_ulong)(sizeof (struct elf_phdr))); @@ -1626,8 +1723,9 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, NEW_AUX_ENT(AT_HWCAP2, (abi_ulong) ELF_HWCAP2); #endif - if (k_platform) + if (u_platform) { NEW_AUX_ENT(AT_PLATFORM, u_platform); + } #ifdef ARCH_DLINFO /* * ARCH_DLINFO must come last so platform specific code can enforce @@ -1635,14 +1733,29 @@ static abi_ulong create_elf_tables(abi_ulong p, int argc, int envc, */ ARCH_DLINFO; #endif + NEW_AUX_ENT (AT_NULL, 0); #undef NEW_AUX_ENT - info->saved_auxv = sp; - info->auxv_len = sp_auxv - sp; + info->auxv_len = u_argv - info->saved_auxv; + + put_user_ual(argc, u_argc); + + p = info->arg_strings; + for (i = 0; i < argc; ++i) { + put_user_ual(p, u_argv); + u_argv += n; + p += target_strlen(p) + 1; + } + put_user_ual(0, u_argv); + + p = info->env_strings; + for (i = 0; i < envc; ++i) { + put_user_ual(p, u_envp); + u_envp += n; + p += target_strlen(p) + 1; + } + put_user_ual(0, u_envp); - sp = loader_build_argptr(envc, argc, sp, p, 0); - /* Check the right amount of stack was allocated for auxvec, envp & argv. */ - assert(sp_auxv - sp == size); return sp; } @@ -2213,12 +2326,28 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) bprm->p = setup_arg_pages(bprm, info); scratch = g_new0(char, TARGET_PAGE_SIZE); - bprm->p = copy_elf_strings(1, &bprm->filename, scratch, - bprm->p, info->stack_limit); - bprm->p = copy_elf_strings(bprm->envc, bprm->envp, scratch, - bprm->p, info->stack_limit); - bprm->p = copy_elf_strings(bprm->argc, bprm->argv, scratch, - bprm->p, info->stack_limit); + if (STACK_GROWS_DOWN) { + bprm->p = copy_elf_strings(1, &bprm->filename, scratch, + bprm->p, info->stack_limit); + info->file_string = bprm->p; + bprm->p = copy_elf_strings(bprm->envc, bprm->envp, scratch, + bprm->p, info->stack_limit); + info->env_strings = bprm->p; + bprm->p = copy_elf_strings(bprm->argc, bprm->argv, scratch, + bprm->p, info->stack_limit); + info->arg_strings = bprm->p; + } else { + info->arg_strings = bprm->p; + bprm->p = copy_elf_strings(bprm->argc, bprm->argv, scratch, + bprm->p, info->stack_limit); + info->env_strings = bprm->p; + bprm->p = copy_elf_strings(bprm->envc, bprm->envp, scratch, + bprm->p, info->stack_limit); + info->file_string = bprm->p; + bprm->p = copy_elf_strings(1, &bprm->filename, scratch, + bprm->p, info->stack_limit); + } + g_free(scratch); if (!bprm->p) { diff --git a/linux-user/errno_defs.h b/linux-user/errno_defs.h index 65522c4516..55fbebda51 100644 --- a/linux-user/errno_defs.h +++ b/linux-user/errno_defs.h @@ -140,6 +140,9 @@ #define TARGET_EOWNERDEAD 130 /* Owner died */ #define TARGET_ENOTRECOVERABLE 131 /* State not recoverable */ +#define TARGET_ERFKILL 132 /* Operation not possible due to RF-kill */ +#define TARGET_EHWPOISON 133 /* Memory page has hardware error */ + /* QEMU internal, not visible to the guest. This is returned when a * system call should be restarted, to tell the main loop that it * should wind the guest PC backwards so it will re-execute the syscall diff --git a/linux-user/hppa/sockbits.h b/linux-user/hppa/sockbits.h new file mode 100644 index 0000000000..5044619e16 --- /dev/null +++ b/linux-user/hppa/sockbits.h @@ -0,0 +1,97 @@ +#define TARGET_SOL_SOCKET 0xffff + +#define TARGET_SO_DEBUG 0x0001 +#define TARGET_SO_REUSEADDR 0x0004 +#define TARGET_SO_KEEPALIVE 0x0008 +#define TARGET_SO_DONTROUTE 0x0010 +#define TARGET_SO_BROADCAST 0x0020 +#define TARGET_SO_LINGER 0x0080 +#define TARGET_SO_OOBINLINE 0x0100 +#define TARGET_SO_REUSEPORT 0x0200 +#define TARGET_SO_SNDBUF 0x1001 +#define TARGET_SO_RCVBUF 0x1002 +#define TARGET_SO_SNDBUFFORCE 0x100a +#define TARGET_SO_RCVBUFFORCE 0x100b +#define TARGET_SO_SNDLOWAT 0x1003 +#define TARGET_SO_RCVLOWAT 0x1004 +#define TARGET_SO_SNDTIMEO 0x1005 +#define TARGET_SO_RCVTIMEO 0x1006 +#define TARGET_SO_ERROR 0x1007 +#define TARGET_SO_TYPE 0x1008 +#define TARGET_SO_PROTOCOL 0x1028 +#define TARGET_SO_DOMAIN 0x1029 +#define TARGET_SO_PEERNAME 0x2000 +#define TARGET_SO_NO_CHECK 0x400b +#define TARGET_SO_PRIORITY 0x400c +#define TARGET_SO_BSDCOMPAT 0x400e +#define TARGET_SO_PASSCRED 0x4010 +#define TARGET_SO_PEERCRED 0x4011 +#define TARGET_SO_TIMESTAMP 0x4012 +#define TARGET_SCM_TIMESTAMP TARGET_SO_TIMESTAMP +#define TARGET_SO_TIMESTAMPNS 0x4013 +#define TARGET_SCM_TIMESTAMPNS TARGET_SO_TIMESTAMPNS + +#define TARGET_SO_SECURITY_AUTHENTICATION 0x4016 +#define TARGET_SO_SECURITY_ENCRYPTION_TRANSPORT 0x4017 +#define TARGET_SO_SECURITY_ENCRYPTION_NETWORK 0x4018 + +#define TARGET_SO_BINDTODEVICE 0x4019 +#define TARGET_SO_ATTACH_FILTER 0x401a +#define TARGET_SO_DETACH_FILTER 0x401b +#define TARGET_SO_GET_FILTER TARGET_SO_ATTACH_FILTER +#define TARGET_SO_ACCEPTCONN 0x401c +#define TARGET_SO_PEERSEC 0x401d +#define TARGET_SO_PASSSEC 0x401e +#define TARGET_SO_MARK 0x401f +#define TARGET_SO_TIMESTAMPING 0x4020 +#define TARGET_SCM_TIMESTAMPING TARGET_SO_TIMESTAMPING +#define TARGET_SO_RXQ_OVFL 0x4021 +#define TARGET_SO_WIFI_STATUS 0x4022 +#define TARGET_SCM_WIFI_STATUS TARGET_SO_WIFI_STATUS +#define TARGET_SO_PEEK_OFF 0x4023 +#define TARGET_SO_NOFCS 0x4024 +#define TARGET_SO_LOCK_FILTER 0x4025 +#define TARGET_SO_SELECT_ERR_QUEUE 0x4026 +#define TARGET_SO_BUSY_POLL 0x4027 +#define TARGET_SO_MAX_PACING_RATE 0x4028 +#define TARGET_SO_BPF_EXTENSIONS 0x4029 +#define TARGET_SO_INCOMING_CPU 0x402A +#define TARGET_SO_ATTACH_BPF 0x402B +#define TARGET_SO_DETACH_BPF TARGET_SO_DETACH_FILTER + +#define TARGET_SO_ATTACH_REUSEPORT_CBPF 0x402C +#define TARGET_SO_ATTACH_REUSEPORT_EBPF 0x402D + +#define TARGET_SO_CNX_ADVICE 0x402E + +/** sock_type - Socket types - default values + * + * + * @SOCK_STREAM - stream (connection) socket + * @SOCK_DGRAM - datagram (conn.less) socket + * @SOCK_RAW - raw socket + * @SOCK_RDM - reliably-delivered message + * @SOCK_SEQPACKET - sequential packet socket + * @SOCK_DCCP - Datagram Congestion Control Protocol socket + * @SOCK_PACKET - linux specific way of getting packets at the dev level. + * For writing rarp and other similar things on the user + * level. + * @SOCK_CLOEXEC - sets the close-on-exec (FD_CLOEXEC) flag. + * @SOCK_NONBLOCK - sets the O_NONBLOCK file status flag. + */ +enum sock_type { + TARGET_SOCK_STREAM = 1, + TARGET_SOCK_DGRAM = 2, + TARGET_SOCK_RAW = 3, + TARGET_SOCK_RDM = 4, + TARGET_SOCK_SEQPACKET = 5, + TARGET_SOCK_DCCP = 6, + TARGET_SOCK_PACKET = 10, + TARGET_SOCK_CLOEXEC = 010000000, + TARGET_SOCK_NONBLOCK = 0x40000000, +}; + +#define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) +#define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ + +#define ARCH_HAS_SOCKET_TYPES 1 diff --git a/linux-user/hppa/syscall_nr.h b/linux-user/hppa/syscall_nr.h new file mode 100644 index 0000000000..0f396fa1e2 --- /dev/null +++ b/linux-user/hppa/syscall_nr.h @@ -0,0 +1,353 @@ +/* + * This file contains the system call numbers. + */ + +#define TARGET_NR_restart_syscall 0 +#define TARGET_NR_exit 1 +#define TARGET_NR_fork 2 +#define TARGET_NR_read 3 +#define TARGET_NR_write 4 +#define TARGET_NR_open 5 +#define TARGET_NR_close 6 +#define TARGET_NR_waitpid 7 +#define TARGET_NR_creat 8 +#define TARGET_NR_link 9 +#define TARGET_NR_unlink 10 +#define TARGET_NR_execve 11 +#define TARGET_NR_chdir 12 +#define TARGET_NR_time 13 +#define TARGET_NR_mknod 14 +#define TARGET_NR_chmod 15 +#define TARGET_NR_lchown 16 +#define TARGET_NR_socket 17 +#define TARGET_NR_stat 18 +#define TARGET_NR_lseek 19 +#define TARGET_NR_getpid 20 +#define TARGET_NR_mount 21 +#define TARGET_NR_bind 22 +#define TARGET_NR_setuid 23 +#define TARGET_NR_getuid 24 +#define TARGET_NR_stime 25 +#define TARGET_NR_ptrace 26 +#define TARGET_NR_alarm 27 +#define TARGET_NR_fstat 28 +#define TARGET_NR_pause 29 +#define TARGET_NR_utime 30 +#define TARGET_NR_connect 31 +#define TARGET_NR_listen 32 +#define TARGET_NR_access 33 +#define TARGET_NR_nice 34 +#define TARGET_NR_accept 35 +#define TARGET_NR_sync 36 +#define TARGET_NR_kill 37 +#define TARGET_NR_rename 38 +#define TARGET_NR_mkdir 39 +#define TARGET_NR_rmdir 40 +#define TARGET_NR_dup 41 +#define TARGET_NR_pipe 42 +#define TARGET_NR_times 43 +#define TARGET_NR_getsockname 44 +#define TARGET_NR_brk 45 +#define TARGET_NR_setgid 46 +#define TARGET_NR_getgid 47 +#define TARGET_NR_signal 48 +#define TARGET_NR_geteuid 49 +#define TARGET_NR_getegid 50 +#define TARGET_NR_acct 51 +#define TARGET_NR_umount2 52 +#define TARGET_NR_getpeername 53 +#define TARGET_NR_ioctl 54 +#define TARGET_NR_fcntl 55 +#define TARGET_NR_socketpair 56 +#define TARGET_NR_setpgid 57 +#define TARGET_NR_send 58 +#define TARGET_NR_uname 59 +#define TARGET_NR_umask 60 +#define TARGET_NR_chroot 61 +#define TARGET_NR_ustat 62 +#define TARGET_NR_dup2 63 +#define TARGET_NR_getppid 64 +#define TARGET_NR_getpgrp 65 +#define TARGET_NR_setsid 66 +#define TARGET_NR_pivot_root 67 +#define TARGET_NR_sgetmask 68 +#define TARGET_NR_ssetmask 69 +#define TARGET_NR_setreuid 70 +#define TARGET_NR_setregid 71 +#define TARGET_NR_mincore 72 +#define TARGET_NR_sigpending 73 +#define TARGET_NR_sethostname 74 +#define TARGET_NR_setrlimit 75 +#define TARGET_NR_getrlimit 76 +#define TARGET_NR_getrusage 77 +#define TARGET_NR_gettimeofday 78 +#define TARGET_NR_settimeofday 79 +#define TARGET_NR_getgroups 80 +#define TARGET_NR_setgroups 81 +#define TARGET_NR_sendto 82 +#define TARGET_NR_symlink 83 +#define TARGET_NR_lstat 84 +#define TARGET_NR_readlink 85 +#define TARGET_NR_uselib 86 +#define TARGET_NR_swapon 87 +#define TARGET_NR_reboot 88 +#define TARGET_NR_mmap2 89 +#define TARGET_NR_mmap 90 +#define TARGET_NR_munmap 91 +#define TARGET_NR_truncate 92 +#define TARGET_NR_ftruncate 93 +#define TARGET_NR_fchmod 94 +#define TARGET_NR_fchown 95 +#define TARGET_NR_getpriority 96 +#define TARGET_NR_setpriority 97 +#define TARGET_NR_recv 98 +#define TARGET_NR_statfs 99 +#define TARGET_NR_fstatfs 100 +#define TARGET_NR_stat64 101 +#define TARGET_NR_socketcall 102 +#define TARGET_NR_syslog 103 +#define TARGET_NR_setitimer 104 +#define TARGET_NR_getitimer 105 +#define TARGET_NR_capget 106 +#define TARGET_NR_capset 107 +#define TARGET_NR_pread64 108 +#define TARGET_NR_pwrite64 109 +#define TARGET_NR_getcwd 110 +#define TARGET_NR_vhangup 111 +#define TARGET_NR_fstat64 112 +#define TARGET_NR_vfork 113 +#define TARGET_NR_wait4 114 +#define TARGET_NR_swapoff 115 +#define TARGET_NR_sysinfo 116 +#define TARGET_NR_shutdown 117 +#define TARGET_NR_fsync 118 +#define TARGET_NR_madvise 119 +#define TARGET_NR_clone 120 +#define TARGET_NR_setdomainname 121 +#define TARGET_NR_sendfile 122 +#define TARGET_NR_recvfrom 123 +#define TARGET_NR_adjtimex 124 +#define TARGET_NR_mprotect 125 +#define TARGET_NR_sigprocmask 126 +#define TARGET_NR_create_module 127 +#define TARGET_NR_init_module 128 +#define TARGET_NR_delete_module 129 +#define TARGET_NR_get_kernel_syms 130 +#define TARGET_NR_quotactl 131 +#define TARGET_NR_getpgid 132 +#define TARGET_NR_fchdir 133 +#define TARGET_NR_bdflush 134 +#define TARGET_NR_sysfs 135 +#define TARGET_NR_personality 136 +#define TARGET_NR_afs_syscall 137 /* Syscall for Andrew File System */ +#define TARGET_NR_setfsuid 138 +#define TARGET_NR_setfsgid 139 +#define TARGET_NR__llseek 140 +#define TARGET_NR_getdents 141 +#define TARGET_NR__newselect 142 +#define TARGET_NR_flock 143 +#define TARGET_NR_msync 144 +#define TARGET_NR_readv 145 +#define TARGET_NR_writev 146 +#define TARGET_NR_getsid 147 +#define TARGET_NR_fdatasync 148 +#define TARGET_NR__sysctl 149 +#define TARGET_NR_mlock 150 +#define TARGET_NR_munlock 151 +#define TARGET_NR_mlockall 152 +#define TARGET_NR_munlockall 153 +#define TARGET_NR_sched_setparam 154 +#define TARGET_NR_sched_getparam 155 +#define TARGET_NR_sched_setscheduler 156 +#define TARGET_NR_sched_getscheduler 157 +#define TARGET_NR_sched_yield 158 +#define TARGET_NR_sched_get_priority_max 159 +#define TARGET_NR_sched_get_priority_min 160 +#define TARGET_NR_sched_rr_get_interval 161 +#define TARGET_NR_nanosleep 162 +#define TARGET_NR_mremap 163 +#define TARGET_NR_setresuid 164 +#define TARGET_NR_getresuid 165 +#define TARGET_NR_sigaltstack 166 +#define TARGET_NR_query_module 167 +#define TARGET_NR_poll 168 +#define TARGET_NR_nfsservctl 169 +#define TARGET_NR_setresgid 170 +#define TARGET_NR_getresgid 171 +#define TARGET_NR_prctl 172 +#define TARGET_NR_rt_sigreturn 173 +#define TARGET_NR_rt_sigaction 174 +#define TARGET_NR_rt_sigprocmask 175 +#define TARGET_NR_rt_sigpending 176 +#define TARGET_NR_rt_sigtimedwait 177 +#define TARGET_NR_rt_sigqueueinfo 178 +#define TARGET_NR_rt_sigsuspend 179 +#define TARGET_NR_chown 180 +#define TARGET_NR_setsockopt 181 +#define TARGET_NR_getsockopt 182 +#define TARGET_NR_sendmsg 183 +#define TARGET_NR_recvmsg 184 +#define TARGET_NR_semop 185 +#define TARGET_NR_semget 186 +#define TARGET_NR_semctl 187 +#define TARGET_NR_msgsnd 188 +#define TARGET_NR_msgrcv 189 +#define TARGET_NR_msgget 190 +#define TARGET_NR_msgctl 191 +#define TARGET_NR_shmat 192 +#define TARGET_NR_shmdt 193 +#define TARGET_NR_shmget 194 +#define TARGET_NR_shmctl 195 +#define TARGET_NR_getpmsg 196 +#define TARGET_NR_putpmsg 197 +#define TARGET_NR_lstat64 198 +#define TARGET_NR_truncate64 199 +#define TARGET_NR_ftruncate64 200 +#define TARGET_NR_getdents64 201 +#define TARGET_NR_fcntl64 202 +#define TARGET_NR_attrctl 203 +#define TARGET_NR_acl_get 204 +#define TARGET_NR_acl_set 205 +#define TARGET_NR_gettid 206 +#define TARGET_NR_readahead 207 +#define TARGET_NR_tkill 208 +#define TARGET_NR_sendfile64 209 +#define TARGET_NR_futex 210 +#define TARGET_NR_sched_setaffinity 211 +#define TARGET_NR_sched_getaffinity 212 +#define TARGET_NR_set_thread_area 213 +#define TARGET_NR_get_thread_area 214 +#define TARGET_NR_io_setup 215 +#define TARGET_NR_io_destroy 216 +#define TARGET_NR_io_getevents 217 +#define TARGET_NR_io_submit 218 +#define TARGET_NR_io_cancel 219 +#define TARGET_NR_alloc_hugepages 220 +#define TARGET_NR_free_hugepages 221 +#define TARGET_NR_exit_group 222 +#define TARGET_NR_lookup_dcookie 223 +#define TARGET_NR_epoll_create 224 +#define TARGET_NR_epoll_ctl 225 +#define TARGET_NR_epill_wait 226 +#define TARGET_NR_remap_file_pages 227 +#define TARGET_NR_semtimedop 228 +#define TARGET_NR_mq_open 229 +#define TARGET_NR_mq_unlink 230 +#define TARGET_NR_mq_timedsend 231 +#define TARGET_NR_mq_timedreceive 232 +#define TARGET_NR_mq_notify 233 +#define TARGET_NR_mq_getsetattr 234 +#define TARGET_NR_waitid 235 +#define TARGET_NR_fadvise64_64 236 +#define TARGET_NR_set_tid_address 237 +#define TARGET_NR_setxattr 238 +#define TARGET_NR_lsetxattr 239 +#define TARGET_NR_fsetxattr 240 +#define TARGET_NR_getxattr 241 +#define TARGET_NR_lgetxattr 242 +#define TARGET_NR_fgetxattr 243 +#define TARGET_NR_listxattr 244 +#define TARGET_NR_llistxattr 245 +#define TARGET_NR_flistxattr 246 +#define TARGET_NR_removexattr 247 +#define TARGET_NR_lremovexattr 248 +#define TARGET_NR_fremovexattr 249 +#define TARGET_NR_timer_create 250 +#define TARGET_NR_timer_settime 251 +#define TARGET_NR_timer_gettime 252 +#define TARGET_NR_timer_getoverrun 253 +#define TARGET_NR_timer_delete 254 +#define TARGET_NR_clock_settime 255 +#define TARGET_NR_clock_gettime 256 +#define TARGET_NR_clock_getres 257 +#define TARGET_NR_clock_nanosleep 258 +#define TARGET_NR_tgkill 259 +#define TARGET_NR_mbind 260 +#define TARGET_NR_get_mempolicy 261 +#define TARGET_NR_set_mempolicy 262 +#define TARGET_NR_vserver 263 +#define TARGET_NR_add_key 264 +#define TARGET_NR_request_key 265 +#define TARGET_NR_keyctl 266 +#define TARGET_NR_ioprio_set 267 +#define TARGET_NR_ioprio_get 268 +#define TARGET_NR_inotify_init 269 +#define TARGET_NR_inotify_add_watch 270 +#define TARGET_NR_inotify_rm_watch 271 +#define TARGET_NR_migrate_pages 272 +#define TARGET_NR_pselect6 273 +#define TARGET_NR_ppoll 274 +#define TARGET_NR_openat 275 +#define TARGET_NR_mkdirat 276 +#define TARGET_NR_mknotat 277 +#define TARGET_NR_fchownat 278 +#define TARGET_NR_futimesat 279 +#define TARGET_NR_fstatat64 280 +#define TARGET_NR_unlinkat 281 +#define TARGET_NR_renameat 282 +#define TARGET_NR_linkat 283 +#define TARGET_NR_symlinkat 284 +#define TARGET_NR_readlinkat 285 +#define TARGET_NR_fchmodat 286 +#define TARGET_NR_faccessat 287 +#define TARGET_NR_unshare 288 +#define TARGET_NR_set_robust_list 289 +#define TARGET_NR_get_robust_list 290 +#define TARGET_NR_splice 291 +#define TARGET_NR_sync_file_range 292 +#define TARGET_NR_tee 293 +#define TARGET_NR_vmsplice 294 +#define TARGET_NR_move_pages 295 +#define TARGET_NR_getcpu 296 +#define TARGET_NR_epoll_pwait 297 +#define TARGET_NR_statfs64 298 +#define TARGET_NR_fstatfs64 299 +#define TARGET_NR_kexec_load 300 +#define TARGET_NR_utimensat 301 +#define TARGET_NR_signalfd 302 +#define TARGET_NR_timerfd 303 +#define TARGET_NR_eventfd 304 +#define TARGET_NR_fallocate 305 +#define TARGET_NR_timerfd_create 306 +#define TARGET_NR_timerfd_settime 307 +#define TARGET_NR_timerfd_gettime 308 +#define TARGET_NR_signalfd4 309 +#define TARGET_NR_eventfd2 310 +#define TARGET_NR_epoll_create1 311 +#define TARGET_NR_dup3 312 +#define TARGET_NR_pipe2 313 +#define TARGET_NR_inotify_init1 314 +#define TARGET_NR_preadv 315 +#define TARGET_NR_pwritev 316 +#define TARGET_NR_rt_tgsigqueueinfo 317 +#define TARGET_NR_perf_event_open 318 +#define TARGET_NR_recvmmsg 319 +#define TARGET_NR_accept4 320 +#define TARGET_NR_prlimit64 321 +#define TARGET_NR_fanotify_init 322 +#define TARGET_NR_fanotify_mark 323 +#define TARGET_NR_clock_adjtime 324 +#define TARGET_NR_name_to_handle_at 325 +#define TARGET_NR_open_by_handle_at 326 +#define TARGET_NR_syncfs 327 +#define TARGET_NR_setns 328 +#define TARGET_NR_sendmmsg 329 +#define TARGET_NR_process_vm_readv 330 +#define TARGET_NR_process_vm_writev 331 +#define TARGET_NR_kcmp 332 +#define TARGET_NR_finit_module 333 +#define TARGET_NR_sched_setattr 334 +#define TARGET_NR_sched_getattr 335 +#define TARGET_NR_utimes 336 +#define TARGET_NR_renameat2 337 +#define TARGET_NR_seccomp 338 +#define TARGET_NR_getrandom 339 +#define TARGET_NR_memfd_create 340 +#define TARGET_NR_bpf 341 +#define TARGET_NR_execveat 342 +#define TARGET_NR_membarrier 343 +#define TARGET_NR_userfaultfd 344 +#define TARGET_NR_mlock2 345 +#define TARGET_NR_copy_file_range 346 +#define TARGET_NR_preadv2 347 +#define TARGET_NR_pwritev2 348 diff --git a/linux-user/hppa/target_cpu.h b/linux-user/hppa/target_cpu.h new file mode 100644 index 0000000000..1a5cecad3c --- /dev/null +++ b/linux-user/hppa/target_cpu.h @@ -0,0 +1,35 @@ +/* + * HPPA specific CPU ABI and functions for linux-user + * + * Copyright (c) 2016 Richard Henderson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef ALPHA_TARGET_CPU_H +#define ALPHA_TARGET_CPU_H + +static inline void cpu_clone_regs(CPUHPPAState *env, target_ulong newsp) +{ + if (newsp) { + env->gr[30] = newsp; + } + env->gr[28] = 0; +} + +static inline void cpu_set_tls(CPUHPPAState *env, target_ulong newtls) +{ + env->cr27 = newtls; +} + +#endif diff --git a/linux-user/hppa/target_signal.h b/linux-user/hppa/target_signal.h new file mode 100644 index 0000000000..e115890b48 --- /dev/null +++ b/linux-user/hppa/target_signal.h @@ -0,0 +1,29 @@ +#ifndef HPPA_TARGET_SIGNAL_H +#define HPPA_TARGET_SIGNAL_H + +#include "cpu.h" + +/* this struct defines a stack used during syscall handling */ + +typedef struct target_sigaltstack { + abi_ulong ss_sp; + int32_t ss_flags; + abi_ulong ss_size; +} target_stack_t; + + +/* + * sigaltstack controls + */ +#define TARGET_SS_ONSTACK 1 +#define TARGET_SS_DISABLE 2 + +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_SIGSTKSZ 8192 + +static inline abi_ulong get_sp_from_cpustate(CPUHPPAState *state) +{ + return state->gr[30]; +} + +#endif /* HPPA_TARGET_SIGNAL_H */ diff --git a/linux-user/hppa/target_structs.h b/linux-user/hppa/target_structs.h new file mode 100644 index 0000000000..b560b1872b --- /dev/null +++ b/linux-user/hppa/target_structs.h @@ -0,0 +1,54 @@ +/* + * HPPA specific structures for linux-user + * + * Copyright (c) 2016 Richard Henderson + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ +#ifndef HPPA_TARGET_STRUCTS_H +#define HPPA_TARGET_STRUCTS_H + +struct target_ipc_perm { + abi_int __key; /* Key. */ + abi_uint uid; /* Owner's user ID. */ + abi_uint gid; /* Owner's group ID. */ + abi_uint cuid; /* Creator's user ID. */ + abi_uint cgid; /* Creator's group ID. */ + abi_ushort __pad1; + abi_ushort mode; /* Read/write permission. */ + abi_ushort __pad2; + abi_ushort __seq; /* Sequence number. */ + abi_uint __pad3; + uint64_t __unused1; + uint64_t __unused2; +}; + +struct target_shmid_ds { + struct target_ipc_perm shm_perm; /* operation permission struct */ + abi_uint __pad1; + abi_ulong shm_atime; /* time of last shmat() */ + abi_uint __pad2; + abi_ulong shm_dtime; /* time of last shmdt() */ + abi_uint __pad3; + abi_ulong shm_ctime; /* time of last change by shmctl() */ + abi_uint __pad4; + abi_long shm_segsz; /* size of segment in bytes */ + abi_int shm_cpid; /* pid of creator */ + abi_int shm_lpid; /* pid of last shmop */ + abi_ulong shm_nattch; /* number of current attaches */ + abi_ulong __unused1; + abi_ulong __unused2; +}; + +#endif diff --git a/linux-user/hppa/target_syscall.h b/linux-user/hppa/target_syscall.h new file mode 100644 index 0000000000..ac18a9c575 --- /dev/null +++ b/linux-user/hppa/target_syscall.h @@ -0,0 +1,237 @@ +#ifndef HPPA_TARGET_SYSCALL_H +#define HPPA_TARGET_SYSCALL_H + +struct target_pt_regs { + target_ulong gr[32]; + uint64_t fr[32]; + target_ulong sr[8]; + target_ulong iasq[2]; + target_ulong iaoq[2]; + target_ulong cr27; + target_ulong __pad0; + target_ulong orig_r28; + target_ulong ksp; + target_ulong kpc; + target_ulong sar; + target_ulong iir; + target_ulong isr; + target_ulong ior; + target_ulong ipsw; +}; + +#define UNAME_MACHINE "hppa" +#define UNAME_MINIMUM_RELEASE "2.6.32" +#define TARGET_CLONE_BACKWARDS +#define TARGET_MINSIGSTKSZ 2048 +#define TARGET_MLOCKALL_MCL_CURRENT 1 +#define TARGET_MLOCKALL_MCL_FUTURE 2 + +#undef TARGET_ENOMSG +#define TARGET_ENOMSG 35 +#undef TARGET_EIDRM +#define TARGET_EIDRM 36 +#undef TARGET_ECHRNG +#define TARGET_ECHRNG 37 +#undef TARGET_EL2NSYNC +#define TARGET_EL2NSYNC 38 +#undef TARGET_EL3HLT +#define TARGET_EL3HLT 39 +#undef TARGET_EL3RST +#define TARGET_EL3RST 40 +#undef TARGET_ELNRNG +#define TARGET_ELNRNG 41 +#undef TARGET_EUNATCH +#define TARGET_EUNATCH 42 +#undef TARGET_ENOCSI +#define TARGET_ENOCSI 43 +#undef TARGET_EL2HLT +#define TARGET_EL2HLT 44 +#undef TARGET_EDEADLK +#define TARGET_EDEADLK 45 +#undef TARGET_ENOLCK +#define TARGET_ENOLCK 46 +#undef TARGET_EILSEQ +#define TARGET_EILSEQ 47 + +#undef TARGET_ENONET +#define TARGET_ENONET 50 +#undef TARGET_ENODATA +#define TARGET_ENODATA 51 +#undef TARGET_ETIME +#define TARGET_ETIME 52 +#undef TARGET_ENOSR +#define TARGET_ENOSR 53 +#undef TARGET_ENOSTR +#define TARGET_ENOSTR 54 +#undef TARGET_ENOPKG +#define TARGET_ENOPKG 55 + +#undef TARGET_ENOLINK +#define TARGET_ENOLINK 57 +#undef TARGET_EADV +#define TARGET_EADV 58 +#undef TARGET_ESRMNT +#define TARGET_ESRMNT 59 +#undef TARGET_ECOMM +#define TARGET_ECOMM 60 +#undef TARGET_EPROTO +#define TARGET_EPROTO 61 + +#undef TARGET_EMULTIHOP +#define TARGET_EMULTIHOP 64 + +#undef TARGET_EDOTDOT +#define TARGET_EDOTDOT 66 +#undef TARGET_EBADMSG +#define TARGET_EBADMSG 67 +#undef TARGET_EUSERS +#define TARGET_EUSERS 68 +#undef TARGET_EDQUOT +#define TARGET_EDQUOT 69 +#undef TARGET_ESTALE +#define TARGET_ESTALE 70 +#undef TARGET_EREMOTE +#define TARGET_EREMOTE 71 +#undef TARGET_EOVERFLOW +#define TARGET_EOVERFLOW 72 + +#undef TARGET_EBADE +#define TARGET_EBADE 160 +#undef TARGET_EBADR +#define TARGET_EBADR 161 +#undef TARGET_EXFULL +#define TARGET_EXFULL 162 +#undef TARGET_ENOANO +#define TARGET_ENOANO 163 +#undef TARGET_EBADRQC +#define TARGET_EBADRQC 164 +#undef TARGET_EBADSLT +#define TARGET_EBADSLT 165 +#undef TARGET_EBFONT +#define TARGET_EBFONT 166 +#undef TARGET_ENOTUNIQ +#define TARGET_ENOTUNIQ 167 +#undef TARGET_EBADFD +#define TARGET_EBADFD 168 +#undef TARGET_EREMCHG +#define TARGET_EREMCHG 169 +#undef TARGET_ELIBACC +#define TARGET_ELIBACC 170 +#undef TARGET_ELIBBAD +#define TARGET_ELIBBAD 171 +#undef TARGET_ELIBSCN +#define TARGET_ELIBSCN 172 +#undef TARGET_ELIBMAX +#define TARGET_ELIBMAX 173 +#undef TARGET_ELIBEXEC +#define TARGET_ELIBEXEC 174 +#undef TARGET_ERESTART +#define TARGET_ERESTART 175 +#undef TARGET_ESTRPIPE +#define TARGET_ESTRPIPE 176 +#undef TARGET_EUCLEAN +#define TARGET_EUCLEAN 177 +#undef TARGET_ENOTNAM +#define TARGET_ENOTNAM 178 +#undef TARGET_ENAVAIL +#define TARGET_ENAVAIL 179 +#undef TARGET_EISNAM +#define TARGET_EISNAM 180 +#undef TARGET_EREMOTEIO +#define TARGET_EREMOTEIO 181 +#undef TARGET_ENOMEDIUM +#define TARGET_ENOMEDIUM 182 +#undef TARGET_EMEDIUMTYPE +#define TARGET_EMEDIUMTYPE 183 +#undef TARGET_ENOKEY +#define TARGET_ENOKEY 184 +#undef TARGET_EKEYEXPIRED +#define TARGET_EKEYEXPIRED 185 +#undef TARGET_EKEYREVOKED +#define TARGET_EKEYREVOKED 186 +#undef TARGET_EKEYREJECTED +#define TARGET_EKEYREJECTED 187 + +/* Never used in linux. */ +/* #define TARGET_ENOSYM 215 */ +#undef TARGET_ENOTSOCK +#define TARGET_ENOTSOCK 216 +#undef TARGET_EDESTADDRREQ +#define TARGET_EDESTADDRREQ 217 +#undef TARGET_EMSGSIZE +#define TARGET_EMSGSIZE 218 +#undef TARGET_EPROTOTYPE +#define TARGET_EPROTOTYPE 219 +#undef TARGET_ENOPROTOOPT +#define TARGET_ENOPROTOOPT 220 +#undef TARGET_EPROTONOSUPPORT +#define TARGET_EPROTONOSUPPORT 221 +#undef TARGET_ESOCKTNOSUPPORT +#define TARGET_ESOCKTNOSUPPORT 222 +#undef TARGET_EOPNOTSUPP +#define TARGET_EOPNOTSUPP 223 +#undef TARGET_EPFNOSUPPORT +#define TARGET_EPFNOSUPPORT 224 +#undef TARGET_EAFNOSUPPORT +#define TARGET_EAFNOSUPPORT 225 +#undef TARGET_EADDRINUSE +#define TARGET_EADDRINUSE 226 +#undef TARGET_EADDRNOTAVAIL +#define TARGET_EADDRNOTAVAIL 227 +#undef TARGET_ENETDOWN +#define TARGET_ENETDOWN 228 +#undef TARGET_ENETUNREACH +#define TARGET_ENETUNREACH 229 +#undef TARGET_ENETRESET +#define TARGET_ENETRESET 230 +#undef TARGET_ECONNABORTED +#define TARGET_ECONNABORTED 231 +#undef TARGET_ECONNRESET +#define TARGET_ECONNRESET 232 +#undef TARGET_ENOBUFS +#define TARGET_ENOBUFS 233 +#undef TARGET_EISCONN +#define TARGET_EISCONN 234 +#undef TARGET_ENOTCONN +#define TARGET_ENOTCONN 235 +#undef TARGET_ESHUTDOWN +#define TARGET_ESHUTDOWN 236 +#undef TARGET_ETOOMANYREFS +#define TARGET_ETOOMANYREFS 237 +#undef TARGET_ETIMEDOUT +#define TARGET_ETIMEDOUT 238 +#undef TARGET_ECONNREFUSED +#define TARGET_ECONNREFUSED 239 +#define TARGET_EREMOTERELEASE 240 +#undef TARGET_EHOSTDOWN +#define TARGET_EHOSTDOWN 241 +#undef TARGET_EHOSTUNREACH +#define TARGET_EHOSTUNREACH 242 + +#undef TARGET_EALREADY +#define TARGET_EALREADY 244 +#undef TARGET_EINPROGRESS +#define TARGET_EINPROGRESS 245 +#undef TARGET_ENOTEMPTY +#define TARGET_ENOTEMPTY 247 +#undef TARGET_ENAMETOOLONG +#define TARGET_ENAMETOOLONG 248 +#undef TARGET_ELOOP +#define TARGET_ELOOP 249 +#undef TARGET_ENOSYS +#define TARGET_ENOSYS 251 + +#undef TARGET_ECANCELED +#define TARGET_ECANCELED 253 + +#undef TARGET_EOWNERDEAD +#define TARGET_EOWNERDEAD 254 +#undef TARGET_ENOTRECOVERABLE +#define TARGET_ENOTRECOVERABLE 255 + +#undef TARGET_ERFKILL +#define TARGET_ERFKILL 256 +#undef TARGET_EHWPOISON +#define TARGET_EHWPOISON 257 + +#endif /* HPPA_TARGET_SYSCALL_H */ diff --git a/linux-user/hppa/termbits.h b/linux-user/hppa/termbits.h new file mode 100644 index 0000000000..e9633ef119 --- /dev/null +++ b/linux-user/hppa/termbits.h @@ -0,0 +1,219 @@ +/* from asm/termbits.h */ + +#define TARGET_NCCS 19 + +struct target_termios { + unsigned int c_iflag; /* input mode flags */ + unsigned int c_oflag; /* output mode flags */ + unsigned int c_cflag; /* control mode flags */ + unsigned int c_lflag; /* local mode flags */ + unsigned char c_line; /* line discipline */ + unsigned char c_cc[TARGET_NCCS]; /* control characters */ +}; + +/* c_iflag bits */ +#define TARGET_IGNBRK 0000001 +#define TARGET_BRKINT 0000002 +#define TARGET_IGNPAR 0000004 +#define TARGET_PARMRK 0000010 +#define TARGET_INPCK 0000020 +#define TARGET_ISTRIP 0000040 +#define TARGET_INLCR 0000100 +#define TARGET_IGNCR 0000200 +#define TARGET_ICRNL 0000400 +#define TARGET_IUCLC 0001000 +#define TARGET_IXON 0002000 +#define TARGET_IXANY 0004000 +#define TARGET_IXOFF 0010000 +#define TARGET_IMAXBEL 0040000 +#define TARGET_IUTF8 0100000 + +/* c_oflag bits */ +#define TARGET_OPOST 0000001 +#define TARGET_OLCUC 0000002 +#define TARGET_ONLCR 0000004 +#define TARGET_OCRNL 0000010 +#define TARGET_ONOCR 0000020 +#define TARGET_ONLRET 0000040 +#define TARGET_OFILL 0000100 +#define TARGET_OFDEL 0000200 +#define TARGET_NLDLY 0000400 +#define TARGET_NL0 0000000 +#define TARGET_NL1 0000400 +#define TARGET_CRDLY 0003000 +#define TARGET_CR0 0000000 +#define TARGET_CR1 0001000 +#define TARGET_CR2 0002000 +#define TARGET_CR3 0003000 +#define TARGET_TABDLY 0014000 +#define TARGET_TAB0 0000000 +#define TARGET_TAB1 0004000 +#define TARGET_TAB2 0010000 +#define TARGET_TAB3 0014000 +#define TARGET_XTABS 0014000 +#define TARGET_BSDLY 0020000 +#define TARGET_BS0 0000000 +#define TARGET_BS1 0020000 +#define TARGET_VTDLY 0040000 +#define TARGET_VT0 0000000 +#define TARGET_VT1 0040000 +#define TARGET_FFDLY 0100000 +#define TARGET_FF0 0000000 +#define TARGET_FF1 0100000 + +/* c_cflag bit meaning */ +#define TARGET_CBAUD 0010017 +#define TARGET_B0 0000000 /* hang up */ +#define TARGET_B50 0000001 +#define TARGET_B75 0000002 +#define TARGET_B110 0000003 +#define TARGET_B134 0000004 +#define TARGET_B150 0000005 +#define TARGET_B200 0000006 +#define TARGET_B300 0000007 +#define TARGET_B600 0000010 +#define TARGET_B1200 0000011 +#define TARGET_B1800 0000012 +#define TARGET_B2400 0000013 +#define TARGET_B4800 0000014 +#define TARGET_B9600 0000015 +#define TARGET_B19200 0000016 +#define TARGET_B38400 0000017 +#define TARGET_EXTA B19200 +#define TARGET_EXTB B38400 +#define TARGET_CSIZE 0000060 +#define TARGET_CS5 0000000 +#define TARGET_CS6 0000020 +#define TARGET_CS7 0000040 +#define TARGET_CS8 0000060 +#define TARGET_CSTOPB 0000100 +#define TARGET_CREAD 0000200 +#define TARGET_PARENB 0000400 +#define TARGET_PARODD 0001000 +#define TARGET_HUPCL 0002000 +#define TARGET_CLOCAL 0004000 +#define TARGET_CBAUDEX 0010000 +#define TARGET_B57600 0010001 +#define TARGET_B115200 0010002 +#define TARGET_B230400 0010003 +#define TARGET_B460800 0010004 +#define TARGET_CIBAUD 002003600000 /* input baud rate (not used) */ +#define TARGET_CMSPAR 010000000000 /* mark or space (stick) parity */ +#define TARGET_CRTSCTS 020000000000 /* flow control */ + +/* c_lflag bits */ +#define TARGET_ISIG 0000001 +#define TARGET_ICANON 0000002 +#define TARGET_XCASE 0000004 +#define TARGET_ECHO 0000010 +#define TARGET_ECHOE 0000020 +#define TARGET_ECHOK 0000040 +#define TARGET_ECHONL 0000100 +#define TARGET_NOFLSH 0000200 +#define TARGET_TOSTOP 0000400 +#define TARGET_ECHOCTL 0001000 +#define TARGET_ECHOPRT 0002000 +#define TARGET_ECHOKE 0004000 +#define TARGET_FLUSHO 0010000 +#define TARGET_PENDIN 0040000 +#define TARGET_IEXTEN 0100000 + +/* c_cc character offsets */ +#define TARGET_VINTR 0 +#define TARGET_VQUIT 1 +#define TARGET_VERASE 2 +#define TARGET_VKILL 3 +#define TARGET_VEOF 4 +#define TARGET_VTIME 5 +#define TARGET_VMIN 6 +#define TARGET_VSWTC 7 +#define TARGET_VSTART 8 +#define TARGET_VSTOP 9 +#define TARGET_VSUSP 10 +#define TARGET_VEOL 11 +#define TARGET_VREPRINT 12 +#define TARGET_VDISCARD 13 +#define TARGET_VWERASE 14 +#define TARGET_VLNEXT 15 +#define TARGET_VEOL2 16 + +/* ioctls */ + +#define TARGET_TCGETS TARGET_IOR('T', 16, struct target_termios) +#define TARGET_TCSETS TARGET_IOW('T', 17, struct target_termios) +#define TARGET_TCSETSW TARGET_IOW('T', 18, struct target_termios) +#define TARGET_TCSETSF TARGET_IOW('T', 19, struct target_termios) +#define TARGET_TCGETA TARGET_IOR('T', 1, struct target_termios) +#define TARGET_TCSETA TARGET_IOW('T', 2, struct target_termios) +#define TARGET_TCSETAW TARGET_IOW('T', 3, struct target_termios) +#define TARGET_TCSETAF TARGET_IOW('T', 4, struct target_termios) +#define TARGET_TCSBRK TARGET_IO('T', 5) +#define TARGET_TCXONC TARGET_IO('T', 6) +#define TARGET_TCFLSH TARGET_IO('T', 7) + +#define TARGET_TIOCEXCL 0x540C +#define TARGET_TIOCNXCL 0x540D +#define TARGET_TIOCSCTTY 0x540E +#define TARGET_TIOCGPGRP TARGET_IOR('T', 30, int) +#define TARGET_TIOCSPGRP TARGET_IOW('T', 29, int) +#define TARGET_TIOCOUTQ 0x5411 +#define TARGET_TIOCSTI 0x5412 +#define TARGET_TIOCGWINSZ 0x5413 +#define TARGET_TIOCSWINSZ 0x5414 +#define TARGET_TIOCMGET 0x5415 +#define TARGET_TIOCMBIS 0x5416 +#define TARGET_TIOCMBIC 0x5417 +#define TARGET_TIOCMSET 0x5418 +#define TARGET_TIOCGSOFTCAR 0x5419 +#define TARGET_TIOCSSOFTCAR 0x541A +#define TARGET_FIONREAD 0x541B +#define TARGET_TIOCINQ TARGET_FIONREAD +#define TARGET_TIOCLINUX 0x541C +#define TARGET_TIOCCONS 0x541D +#define TARGET_TIOCGSERIAL 0x541E +#define TARGET_TIOCSSERIAL 0x541F +#define TARGET_TIOCPKT 0x5420 +#define TARGET_FIONBIO 0x5421 +#define TARGET_TIOCNOTTY 0x5422 +#define TARGET_TIOCSETD 0x5423 +#define TARGET_TIOCGETD 0x5424 +#define TARGET_TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */ +#define TARGET_TIOCTTYGSTRUCT 0x5426 /* For debugging only */ +#define TARGET_TIOCSBRK 0x5427 /* BSD compatibility */ +#define TARGET_TIOCCBRK 0x5428 /* BSD compatibility */ +#define TARGET_TIOCGSID TARGET_IOR('T', 20, int) +#define TARGET_TIOCGPTN TARGET_IOR('T', 0x30, unsigned int) + /* Get Pty Number (of pty-mux device) */ +#define TARGET_TIOCSPTLCK TARGET_IOW('T', 0x31, int) + /* Lock/unlock Pty */ + +#define TARGET_FIONCLEX 0x5450 /* these numbers need to be adjusted. */ +#define TARGET_FIOCLEX 0x5451 +#define TARGET_FIOASYNC 0x5452 +#define TARGET_TIOCSERCONFIG 0x5453 +#define TARGET_TIOCSERGWILD 0x5454 +#define TARGET_TIOCSERSWILD 0x5455 +#define TARGET_TIOCGLCKTRMIOS 0x5456 +#define TARGET_TIOCSLCKTRMIOS 0x5457 +#define TARGET_TIOCSERGSTRUCT 0x5458 /* For debugging only */ +#define TARGET_TIOCSERGETLSR 0x5459 /* Get line status register */ +#define TARGET_TIOCSERGETMULTI 0x545A /* Get multiport config */ +#define TARGET_TIOCSERSETMULTI 0x545B /* Set multiport config */ + +#define TARGET_TIOCMIWAIT 0x545C /* wait for a change on serial */ +#define TARGET_TIOCGICOUNT 0x545D +#define TARGET_FIOQSIZE 0x5460 +#define TARGET_TIOCSTART 0x5461 +#define TARGET_TIOCSTOP 0x5462 +#define TARGET_TIOCSLTC 0x5462 + +/* Used for packet mode */ +#define TARGET_TIOCPKT_DATA 0 +#define TARGET_TIOCPKT_FLUSHREAD 1 +#define TARGET_TIOCPKT_FLUSHWRITE 2 +#define TARGET_TIOCPKT_STOP 4 +#define TARGET_TIOCPKT_START 8 +#define TARGET_TIOCPKT_NOSTOP 16 +#define TARGET_TIOCPKT_DOSTOP 32 + +#define TARGET_TIOCSER_TEMT 0x01 /* Transmitter physically empty */ diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 1bad701481..2f6e85bd78 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -164,6 +164,9 @@ IOCTL(SIOCSRARP, IOC_W, MK_PTR(MK_STRUCT(STRUCT_arpreq))) IOCTL(SIOCGRARP, IOC_R, MK_PTR(MK_STRUCT(STRUCT_arpreq))) IOCTL(SIOCGIWNAME, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_char_ifreq))) + IOCTL(SIOCGPGRP, IOC_R, MK_PTR(TYPE_INT)) /* pid_t */ + IOCTL(SIOCGSTAMP, IOC_R, MK_PTR(MK_STRUCT(STRUCT_timeval))) + IOCTL(SIOCGSTAMPNS, IOC_R, MK_PTR(MK_STRUCT(STRUCT_timespec))) IOCTL(CDROMPAUSE, 0, TYPE_NULL) IOCTL(CDROMSTART, 0, TYPE_NULL) @@ -422,3 +425,8 @@ MK_PTR(MK_STRUCT(STRUCT_rtentry))) IOCTL_SPECIAL(SIOCDELRT, IOC_W, do_ioctl_rt, MK_PTR(MK_STRUCT(STRUCT_rtentry))) + +#ifdef TARGET_TIOCSTART + IOCTL_IGNORE(TIOCSTART) + IOCTL_IGNORE(TIOCSTOP) +#endif diff --git a/linux-user/main.c b/linux-user/main.c index 94a636f02a..db4eb682a2 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3512,6 +3512,169 @@ void cpu_loop(CPUTLGState *env) #endif +#ifdef TARGET_HPPA + +static abi_ulong hppa_lws(CPUHPPAState *env) +{ + uint32_t which = env->gr[20]; + abi_ulong addr = env->gr[26]; + abi_ulong old = env->gr[25]; + abi_ulong new = env->gr[24]; + abi_ulong size, ret; + + switch (which) { + default: + return -TARGET_ENOSYS; + + case 0: /* elf32 atomic 32bit cmpxchg */ + if ((addr & 3) || !access_ok(VERIFY_WRITE, addr, 4)) { + return -TARGET_EFAULT; + } + old = tswap32(old); + new = tswap32(new); + ret = atomic_cmpxchg((uint32_t *)g2h(addr), old, new); + ret = tswap32(ret); + break; + + case 2: /* elf32 atomic "new" cmpxchg */ + size = env->gr[23]; + if (size >= 4) { + return -TARGET_ENOSYS; + } + if (((addr | old | new) & ((1 << size) - 1)) + || !access_ok(VERIFY_WRITE, addr, 1 << size) + || !access_ok(VERIFY_READ, old, 1 << size) + || !access_ok(VERIFY_READ, new, 1 << size)) { + return -TARGET_EFAULT; + } + /* Note that below we use host-endian loads so that the cmpxchg + can be host-endian as well. */ + switch (size) { + case 0: + old = *(uint8_t *)g2h(old); + new = *(uint8_t *)g2h(new); + ret = atomic_cmpxchg((uint8_t *)g2h(addr), old, new); + ret = ret != old; + break; + case 1: + old = *(uint16_t *)g2h(old); + new = *(uint16_t *)g2h(new); + ret = atomic_cmpxchg((uint16_t *)g2h(addr), old, new); + ret = ret != old; + break; + case 2: + old = *(uint32_t *)g2h(old); + new = *(uint32_t *)g2h(new); + ret = atomic_cmpxchg((uint32_t *)g2h(addr), old, new); + ret = ret != old; + break; + case 3: + { + uint64_t o64, n64, r64; + o64 = *(uint64_t *)g2h(old); + n64 = *(uint64_t *)g2h(new); +#ifdef CONFIG_ATOMIC64 + r64 = atomic_cmpxchg__nocheck((uint64_t *)g2h(addr), o64, n64); + ret = r64 != o64; +#else + start_exclusive(); + r64 = *(uint64_t *)g2h(addr); + ret = 1; + if (r64 == o64) { + *(uint64_t *)g2h(addr) = n64; + ret = 0; + } + end_exclusive(); +#endif + } + break; + } + break; + } + + env->gr[28] = ret; + return 0; +} + +void cpu_loop(CPUHPPAState *env) +{ + CPUState *cs = CPU(hppa_env_get_cpu(env)); + target_siginfo_t info; + abi_ulong ret; + int trapnr; + + while (1) { + cpu_exec_start(cs); + trapnr = cpu_exec(cs); + cpu_exec_end(cs); + process_queued_cpu_work(cs); + + switch (trapnr) { + case EXCP_SYSCALL: + ret = do_syscall(env, env->gr[20], + env->gr[26], env->gr[25], + env->gr[24], env->gr[23], + env->gr[22], env->gr[21], 0, 0); + switch (ret) { + default: + env->gr[28] = ret; + /* We arrived here by faking the gateway page. Return. */ + env->iaoq_f = env->gr[31]; + env->iaoq_b = env->gr[31] + 4; + break; + case -TARGET_ERESTARTSYS: + case -TARGET_QEMU_ESIGRETURN: + break; + } + break; + case EXCP_SYSCALL_LWS: + env->gr[21] = hppa_lws(env); + /* We arrived here by faking the gateway page. Return. */ + env->iaoq_f = env->gr[31]; + env->iaoq_b = env->gr[31] + 4; + break; + case EXCP_SIGSEGV: + info.si_signo = TARGET_SIGSEGV; + info.si_errno = 0; + info.si_code = TARGET_SEGV_ACCERR; + info._sifields._sigfault._addr = env->ior; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_SIGILL: + info.si_signo = TARGET_SIGILL; + info.si_errno = 0; + info.si_code = TARGET_ILL_ILLOPN; + info._sifields._sigfault._addr = env->iaoq_f; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_SIGFPE: + info.si_signo = TARGET_SIGFPE; + info.si_errno = 0; + info.si_code = 0; + info._sifields._sigfault._addr = env->iaoq_f; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; + case EXCP_DEBUG: + trapnr = gdb_handlesig(cs, TARGET_SIGTRAP); + if (trapnr) { + info.si_signo = trapnr; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, trapnr, QEMU_SI_FAULT, &info); + } + break; + case EXCP_INTERRUPT: + /* just indicate that signals should be handled asap */ + break; + default: + g_assert_not_reached(); + } + process_pending_signals(env); + } +} + +#endif /* TARGET_HPPA */ + THREAD CPUState *thread_cpu; bool qemu_cpu_is_self(CPUState *cpu) @@ -4179,15 +4342,16 @@ int main(int argc, char **argv, char **envp) qemu_log("start_brk 0x" TARGET_ABI_FMT_lx "\n", info->start_brk); qemu_log("end_code 0x" TARGET_ABI_FMT_lx "\n", info->end_code); - qemu_log("start_code 0x" TARGET_ABI_FMT_lx "\n", - info->start_code); - qemu_log("start_data 0x" TARGET_ABI_FMT_lx "\n", - info->start_data); + qemu_log("start_code 0x" TARGET_ABI_FMT_lx "\n", info->start_code); + qemu_log("start_data 0x" TARGET_ABI_FMT_lx "\n", info->start_data); qemu_log("end_data 0x" TARGET_ABI_FMT_lx "\n", info->end_data); - qemu_log("start_stack 0x" TARGET_ABI_FMT_lx "\n", - info->start_stack); + qemu_log("start_stack 0x" TARGET_ABI_FMT_lx "\n", info->start_stack); qemu_log("brk 0x" TARGET_ABI_FMT_lx "\n", info->brk); qemu_log("entry 0x" TARGET_ABI_FMT_lx "\n", info->entry); + qemu_log("argv_start 0x" TARGET_ABI_FMT_lx "\n", info->arg_start); + qemu_log("env_start 0x" TARGET_ABI_FMT_lx "\n", + info->arg_end + (abi_ulong)sizeof(abi_ulong)); + qemu_log("auxv_start 0x" TARGET_ABI_FMT_lx "\n", info->saved_auxv); } target_set_brk(info->brk); @@ -4538,6 +4702,15 @@ int main(int argc, char **argv, char **envp) } env->pc = regs->pc; } +#elif defined(TARGET_HPPA) + { + int i; + for (i = 1; i < 32; i++) { + env->gr[i] = regs->gr[i]; + } + env->iaoq_f = regs->iaoq[0]; + env->iaoq_b = regs->iaoq[1]; + } #else #error unsupported target CPU #endif diff --git a/linux-user/mips/target_syscall.h b/linux-user/mips/target_syscall.h index 0b64b73714..2fca1c6bf9 100644 --- a/linux-user/mips/target_syscall.h +++ b/linux-user/mips/target_syscall.h @@ -221,6 +221,11 @@ struct target_pt_regs { #undef TARGET_ENOTRECOVERABLE #define TARGET_ENOTRECOVERABLE 166 /* State not recoverable */ +#undef TARGET_ERFKILL +#define TARGET_ERFKILL 167 +#undef TARGET_EHWPOISON +#define TARGET_EHWPOISON 168 + #undef TARGET_EDQUOT #define TARGET_EDQUOT 1133 /* Quota exceeded */ diff --git a/linux-user/mips64/target_syscall.h b/linux-user/mips64/target_syscall.h index 6692917e2e..078437d765 100644 --- a/linux-user/mips64/target_syscall.h +++ b/linux-user/mips64/target_syscall.h @@ -218,6 +218,11 @@ struct target_pt_regs { #undef TARGET_ENOTRECOVERABLE #define TARGET_ENOTRECOVERABLE 166 /* State not recoverable */ +#undef TARGET_ERFKILL +#define TARGET_ERFKILL 167 +#undef TARGET_EHWPOISON +#define TARGET_EHWPOISON 168 + #undef TARGET_EDQUOT #define TARGET_EDQUOT 1133 /* Quota exceeded */ diff --git a/linux-user/qemu.h b/linux-user/qemu.h index da73a01106..4edd7d0c08 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -48,6 +48,9 @@ struct image_info { abi_ulong auxv_len; abi_ulong arg_start; abi_ulong arg_end; + abi_ulong arg_strings; + abi_ulong env_strings; + abi_ulong file_string; uint32_t elf_flags; int personality; #ifdef CONFIG_USE_FDPIC diff --git a/linux-user/signal.c b/linux-user/signal.c index c750053edd..0a5bb4e26b 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -5888,6 +5888,195 @@ long do_rt_sigreturn(CPUTLGState *env) return -TARGET_QEMU_ESIGRETURN; } +#elif defined(TARGET_HPPA) + +struct target_sigcontext { + abi_ulong sc_flags; + abi_ulong sc_gr[32]; + uint64_t sc_fr[32]; + abi_ulong sc_iasq[2]; + abi_ulong sc_iaoq[2]; + abi_ulong sc_sar; +}; + +struct target_ucontext { + abi_uint tuc_flags; + abi_ulong tuc_link; + target_stack_t tuc_stack; + abi_uint pad[1]; + struct target_sigcontext tuc_mcontext; + target_sigset_t tuc_sigmask; +}; + +struct target_rt_sigframe { + abi_uint tramp[9]; + target_siginfo_t info; + struct target_ucontext uc; + /* hidden location of upper halves of pa2.0 64-bit gregs */ +}; + +static void setup_sigcontext(struct target_sigcontext *sc, CPUArchState *env) +{ + int flags = 0; + int i; + + /* ??? if on_sig_stack, flags |= 1 (PARISC_SC_FLAG_ONSTACK). */ + + if (env->iaoq_f < TARGET_PAGE_SIZE) { + /* In the gateway page, executing a syscall. */ + flags |= 2; /* PARISC_SC_FLAG_IN_SYSCALL */ + __put_user(env->gr[31], &sc->sc_iaoq[0]); + __put_user(env->gr[31] + 4, &sc->sc_iaoq[1]); + } else { + __put_user(env->iaoq_f, &sc->sc_iaoq[0]); + __put_user(env->iaoq_b, &sc->sc_iaoq[1]); + } + __put_user(0, &sc->sc_iasq[0]); + __put_user(0, &sc->sc_iasq[1]); + __put_user(flags, &sc->sc_flags); + + __put_user(cpu_hppa_get_psw(env), &sc->sc_gr[0]); + for (i = 1; i < 32; ++i) { + __put_user(env->gr[i], &sc->sc_gr[i]); + } + + __put_user((uint64_t)env->fr0_shadow << 32, &sc->sc_fr[0]); + for (i = 1; i < 32; ++i) { + __put_user(env->fr[i], &sc->sc_fr[i]); + } + + __put_user(env->sar, &sc->sc_sar); +} + +static void restore_sigcontext(CPUArchState *env, struct target_sigcontext *sc) +{ + target_ulong psw; + int i; + + __get_user(psw, &sc->sc_gr[0]); + cpu_hppa_put_psw(env, psw); + + for (i = 1; i < 32; ++i) { + __get_user(env->gr[i], &sc->sc_gr[i]); + } + for (i = 0; i < 32; ++i) { + __get_user(env->fr[i], &sc->sc_fr[i]); + } + cpu_hppa_loaded_fr0(env); + + __get_user(env->iaoq_f, &sc->sc_iaoq[0]); + __get_user(env->iaoq_b, &sc->sc_iaoq[1]); + __get_user(env->sar, &sc->sc_sar); +} + +/* No, this doesn't look right, but it's copied straight from the kernel. */ +#define PARISC_RT_SIGFRAME_SIZE32 \ + ((sizeof(struct target_rt_sigframe) + 48 + 64) & -64) + +static void setup_rt_frame(int sig, struct target_sigaction *ka, + target_siginfo_t *info, + target_sigset_t *set, CPUArchState *env) +{ + abi_ulong frame_addr, sp, haddr; + struct target_rt_sigframe *frame; + int i; + + sp = env->gr[30]; + if (ka->sa_flags & TARGET_SA_ONSTACK) { + if (sas_ss_flags(sp) == 0) { + sp = (target_sigaltstack_used.ss_sp + 0x7f) & ~0x3f; + } + } + frame_addr = QEMU_ALIGN_UP(sp, 64); + sp = frame_addr + PARISC_RT_SIGFRAME_SIZE32; + + trace_user_setup_rt_frame(env, frame_addr); + + if (!lock_user_struct(VERIFY_WRITE, frame, frame_addr, 0)) { + goto give_sigsegv; + } + + tswap_siginfo(&frame->info, info); + frame->uc.tuc_flags = 0; + frame->uc.tuc_link = 0; + + __put_user(target_sigaltstack_used.ss_sp, &frame->uc.tuc_stack.ss_sp); + __put_user(sas_ss_flags(get_sp_from_cpustate(env)), + &frame->uc.tuc_stack.ss_flags); + __put_user(target_sigaltstack_used.ss_size, + &frame->uc.tuc_stack.ss_size); + + for (i = 0; i < TARGET_NSIG_WORDS; i++) { + __put_user(set->sig[i], &frame->uc.tuc_sigmask.sig[i]); + } + + setup_sigcontext(&frame->uc.tuc_mcontext, env); + + __put_user(0x34190000, frame->tramp + 0); /* ldi 0,%r25 */ + __put_user(0x3414015a, frame->tramp + 1); /* ldi __NR_rt_sigreturn,%r20 */ + __put_user(0xe4008200, frame->tramp + 2); /* be,l 0x100(%sr2,%r0) */ + __put_user(0x08000240, frame->tramp + 3); /* nop */ + + unlock_user_struct(frame, frame_addr, 1); + + env->gr[2] = h2g(frame->tramp); + env->gr[30] = sp; + env->gr[26] = sig; + env->gr[25] = h2g(&frame->info); + env->gr[24] = h2g(&frame->uc); + + haddr = ka->_sa_handler; + if (haddr & 2) { + /* Function descriptor. */ + target_ulong *fdesc, dest; + + haddr &= -4; + if (!lock_user_struct(VERIFY_READ, fdesc, haddr, 1)) { + goto give_sigsegv; + } + __get_user(dest, fdesc); + __get_user(env->gr[19], fdesc + 1); + unlock_user_struct(fdesc, haddr, 1); + haddr = dest; + } + env->iaoq_f = haddr; + env->iaoq_b = haddr + 4;; + return; + + give_sigsegv: + force_sigsegv(sig); +} + +long do_rt_sigreturn(CPUArchState *env) +{ + abi_ulong frame_addr = env->gr[30] - PARISC_RT_SIGFRAME_SIZE32; + struct target_rt_sigframe *frame; + sigset_t set; + + trace_user_do_rt_sigreturn(env, frame_addr); + if (!lock_user_struct(VERIFY_READ, frame, frame_addr, 1)) { + goto badframe; + } + target_to_host_sigset(&set, &frame->uc.tuc_sigmask); + set_sigmask(&set); + + restore_sigcontext(env, &frame->uc.tuc_mcontext); + unlock_user_struct(frame, frame_addr, 0); + + if (do_sigaltstack(frame_addr + offsetof(struct target_rt_sigframe, + uc.tuc_stack), + 0, env->gr[30]) == -EFAULT) { + goto badframe; + } + + unlock_user_struct(frame, frame_addr, 0); + return -TARGET_QEMU_ESIGRETURN; + + badframe: + force_sig(TARGET_SIGSEGV); + return -TARGET_QEMU_ESIGRETURN; +} + #else static void setup_frame(int sig, struct target_sigaction *ka, @@ -5989,7 +6178,7 @@ static void handle_pending_signal(CPUArchState *cpu_env, int sig, /* prepare the stack frame of the virtual CPU */ #if defined(TARGET_ABI_MIPSN32) || defined(TARGET_ABI_MIPSN64) \ || defined(TARGET_OPENRISC) || defined(TARGET_TILEGX) \ - || defined(TARGET_PPC64) + || defined(TARGET_PPC64) || defined(TARGET_HPPA) /* These targets do not have traditional signals. */ setup_rt_frame(sig, sa, &k->info, &target_old_set, cpu_env); #else diff --git a/linux-user/socket.h b/linux-user/socket.h index 4dacae6127..7051cd2cf4 100644 --- a/linux-user/socket.h +++ b/linux-user/socket.h @@ -205,6 +205,8 @@ #define TARGET_SOCK_MAX (TARGET_SOCK_PACKET + 1) #define TARGET_SOCK_TYPE_MASK 0xf /* Covers up to TARGET_SOCK_MAX-1. */ +#elif defined(TARGET_HPPA) +#include <hppa/sockbits.h> #else #if defined(TARGET_SPARC) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 7b77503f94..11a311f9db 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -798,6 +798,12 @@ static uint16_t host_to_target_errno_table[ERRNO_TABLE_SIZE] = { #ifdef ENOMSG [ENOMSG] = TARGET_ENOMSG, #endif +#ifdef ERKFILL + [ERFKILL] = TARGET_ERFKILL, +#endif +#ifdef EHWPOISON + [EHWPOISON] = TARGET_EHWPOISON, +#endif }; static inline int host_to_target_errno(int err) @@ -5453,6 +5459,8 @@ static IOCTLEntry ioctl_entries[] = { { TARGET_ ## cmd, cmd, #cmd, access, 0, { __VA_ARGS__ } }, #define IOCTL_SPECIAL(cmd, access, dofn, ...) \ { TARGET_ ## cmd, cmd, #cmd, access, dofn, { __VA_ARGS__ } }, +#define IOCTL_IGNORE(cmd) \ + { TARGET_ ## cmd, 0, #cmd }, #include "ioctls.h" { 0, 0, }, }; @@ -5484,6 +5492,10 @@ static abi_long do_ioctl(int fd, int cmd, abi_long arg) #endif if (ie->do_ioctl) { return ie->do_ioctl(ie, buf_temp, fd, cmd, arg); + } else if (!ie->host_cmd) { + /* Some architectures define BSD ioctls in their headers + that are not implemented in Linux. */ + return -TARGET_ENOSYS; } switch(arg_type[0]) { diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 0b15466743..39848a8816 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -90,6 +90,15 @@ #define TARGET_IOC_READ 2U #define TARGET_IOC_WRITE 4U +#elif defined(TARGET_HPPA) + +#define TARGET_IOC_SIZEBITS 14 +#define TARGET_IOC_DIRBITS 2 + +#define TARGET_IOC_NONE 0U +#define TARGET_IOC_WRITE 2U +#define TARGET_IOC_READ 1U + #else #error unsupported CPU #endif @@ -417,7 +426,7 @@ int do_sigaction(int sig, const struct target_sigaction *act, || defined(TARGET_M68K) || defined(TARGET_ALPHA) || defined(TARGET_CRIS) \ || defined(TARGET_MICROBLAZE) || defined(TARGET_UNICORE32) \ || defined(TARGET_S390X) || defined(TARGET_OPENRISC) \ - || defined(TARGET_TILEGX) + || defined(TARGET_TILEGX) || defined(TARGET_HPPA) #if defined(TARGET_SPARC) #define TARGET_SA_NOCLDSTOP 8u @@ -587,6 +596,46 @@ int do_sigaction(int sig, const struct target_sigaction *act, #define TARGET_SIG_UNBLOCK 2 /* for unblocking signals */ #define TARGET_SIG_SETMASK 3 /* for setting the signal mask */ +#elif defined(TARGET_HPPA) + +#define TARGET_SIGHUP 1 +#define TARGET_SIGINT 2 +#define TARGET_SIGQUIT 3 +#define TARGET_SIGILL 4 +#define TARGET_SIGTRAP 5 +#define TARGET_SIGABRT 6 +#define TARGET_SIGIOT 6 +#define TARGET_SIGSTKFLT 7 +#define TARGET_SIGFPE 8 +#define TARGET_SIGKILL 9 +#define TARGET_SIGBUS 10 +#define TARGET_SIGSEGV 11 +#define TARGET_SIGXCPU 12 +#define TARGET_SIGPIPE 13 +#define TARGET_SIGALRM 14 +#define TARGET_SIGTERM 15 +#define TARGET_SIGUSR1 16 +#define TARGET_SIGUSR2 17 +#define TARGET_SIGCHLD 18 +#define TARGET_SIGPWR 19 +#define TARGET_SIGVTALRM 20 +#define TARGET_SIGPROF 21 +#define TARGET_SIGIO 22 +#define TARGET_SIGPOLL TARGET_SIGIO +#define TARGET_SIGWINCH 23 +#define TARGET_SIGSTOP 24 +#define TARGET_SIGTSTP 25 +#define TARGET_SIGCONT 26 +#define TARGET_SIGTTIN 27 +#define TARGET_SIGTTOU 28 +#define TARGET_SIGURG 29 +#define TARGET_SIGXFSZ 30 +#define TARGET_SIGSYS 31 + +#define TARGET_SIG_BLOCK 0 +#define TARGET_SIG_UNBLOCK 1 +#define TARGET_SIG_SETMASK 2 + #else /* OpenRISC Using the general signals */ @@ -930,9 +979,13 @@ struct target_pollfd { #if defined(TARGET_ALPHA) || defined(TARGET_MIPS) || defined(TARGET_SH4) #define TARGET_SIOCATMARK TARGET_IOR('s', 7, int) +#define TARGET_SIOCGPGRP TARGET_IOR('s', 9, pid_t) #else #define TARGET_SIOCATMARK 0x8905 +#define TARGET_SIOCGPGRP 0x8904 #endif +#define TARGET_SIOCGSTAMP 0x8906 /* Get stamp (timeval) */ +#define TARGET_SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */ /* Networking ioctls */ #define TARGET_SIOCADDRT 0x890B /* add routing table entry */ @@ -1275,6 +1328,16 @@ struct target_winsize { #define TARGET_MAP_NORESERVE 0x10000 /* no check for reservations */ #define TARGET_MAP_POPULATE 0x20000 /* pop (prefault) pagetables */ #define TARGET_MAP_NONBLOCK 0x40000 /* do not block on IO */ +#elif defined(TARGET_HPPA) +#define TARGET_MAP_ANONYMOUS 0x10 /* don't use a file */ +#define TARGET_MAP_FIXED 0x04 /* Interpret addr exactly */ +#define TARGET_MAP_GROWSDOWN 0x08000 /* stack-like segment */ +#define TARGET_MAP_DENYWRITE 0x00800 /* ETXTBSY */ +#define TARGET_MAP_EXECUTABLE 0x01000 /* mark it as an executable */ +#define TARGET_MAP_LOCKED 0x02000 /* lock the mapping */ +#define TARGET_MAP_NORESERVE 0x04000 /* no check for reservations */ +#define TARGET_MAP_POPULATE 0x10000 /* pop (prefault) pagetables */ +#define TARGET_MAP_NONBLOCK 0x20000 /* do not block on IO */ #else #define TARGET_MAP_FIXED 0x10 /* Interpret addr exactly */ #define TARGET_MAP_ANONYMOUS 0x20 /* don't use a file */ @@ -2025,6 +2088,62 @@ struct target_stat64 { unsigned int __unused5; }; +#elif defined(TARGET_HPPA) + +struct target_stat { + abi_uint st_dev; + abi_uint st_ino; + abi_ushort st_mode; + abi_ushort st_nlink; + abi_ushort _res1; + abi_ushort _res2; + abi_uint st_rdev; + abi_int st_size; + abi_int target_st_atime; + abi_uint target_st_atime_nsec; + abi_int target_st_mtime; + abi_uint target_st_mtime_nsec; + abi_int target_st_ctime; + abi_uint target_st_ctime_nsec; + abi_int st_blksize; + abi_int st_blocks; + abi_uint _unused1; + abi_uint _unused2; + abi_uint _unused3; + abi_uint _unused4; + abi_ushort _unused5; + abi_short st_fstype; + abi_uint st_realdev; + abi_ushort st_basemode; + abi_ushort _unused6; + abi_uint st_uid; + abi_uint st_gid; + abi_uint _unused7[3]; +}; + +#define TARGET_HAS_STRUCT_STAT64 +struct target_stat64 { + uint64_t st_dev; + abi_uint _pad1; + abi_uint _res1; + abi_uint st_mode; + abi_uint st_nlink; + abi_uint st_uid; + abi_uint st_gid; + uint64_t st_rdev; + abi_uint _pad2; + int64_t st_size; + abi_int st_blksize; + int64_t st_blocks; + abi_int target_st_atime; + abi_uint target_st_atime_nsec; + abi_int target_st_mtime; + abi_uint target_st_mtime_nsec; + abi_int target_st_ctime; + abi_uint target_st_ctime_nsec; + uint64_t st_ino; +}; + #else #error unsupported CPU #endif @@ -2195,6 +2314,12 @@ struct target_statfs64 { #define TARGET_F_SETLKW 7 #define TARGET_F_SETOWN 24 /* for sockets. */ #define TARGET_F_GETOWN 23 /* for sockets. */ +#elif defined(TARGET_HPPA) +#define TARGET_F_GETLK 5 +#define TARGET_F_SETLK 6 +#define TARGET_F_SETLKW 7 +#define TARGET_F_GETOWN 11 /* for sockets. */ +#define TARGET_F_SETOWN 12 /* for sockets. */ #else #define TARGET_F_GETLK 5 #define TARGET_F_SETLK 6 @@ -2217,13 +2342,22 @@ struct target_statfs64 { #endif +#if defined(TARGET_HPPA) +#define TARGET_F_SETSIG 13 /* for sockets. */ +#define TARGET_F_GETSIG 14 /* for sockets. */ +#else #define TARGET_F_SETSIG 10 /* for sockets. */ #define TARGET_F_GETSIG 11 /* for sockets. */ +#endif #if defined(TARGET_MIPS) #define TARGET_F_GETLK64 33 /* using 'struct flock64' */ #define TARGET_F_SETLK64 34 #define TARGET_F_SETLKW64 35 +#elif defined(TARGET_HPPA) +#define TARGET_F_GETLK64 8 /* using 'struct flock64' */ +#define TARGET_F_SETLK64 9 +#define TARGET_F_SETLKW64 10 #else #define TARGET_F_GETLK64 12 /* using 'struct flock64' */ #define TARGET_F_SETLK64 13 @@ -2254,6 +2388,20 @@ struct target_statfs64 { #define TARGET_O_CLOEXEC 010000000 #define TARGET___O_SYNC 020000000 #define TARGET_O_PATH 040000000 +#elif defined(TARGET_HPPA) +#define TARGET_O_NONBLOCK 000200004 /* HPUX has separate NDELAY & NONBLOCK */ +#define TARGET_O_APPEND 000000010 +#define TARGET_O_CREAT 000000400 /* not fcntl */ +#define TARGET_O_EXCL 000002000 /* not fcntl */ +#define TARGET_O_NOCTTY 000400000 /* not fcntl */ +#define TARGET_O_DSYNC 001000000 +#define TARGET_O_LARGEFILE 000004000 +#define TARGET_O_DIRECTORY 000010000 /* must be a directory */ +#define TARGET_O_NOFOLLOW 000000200 /* don't follow links */ +#define TARGET_O_NOATIME 004000000 +#define TARGET_O_CLOEXEC 010000000 +#define TARGET___O_SYNC 000100000 +#define TARGET_O_PATH 020000000 #elif defined(TARGET_ARM) || defined(TARGET_M68K) #define TARGET_O_DIRECTORY 040000 /* must be a directory */ #define TARGET_O_NOFOLLOW 0100000 /* don't follow links */ @@ -2375,8 +2523,8 @@ struct target_flock { struct target_flock64 { short l_type; short l_whence; -#if defined(TARGET_PPC) || defined(TARGET_X86_64) \ - || defined(TARGET_MIPS) || defined(TARGET_SPARC) \ +#if defined(TARGET_PPC) || defined(TARGET_X86_64) || defined(TARGET_MIPS) \ + || defined(TARGET_SPARC) || defined(TARGET_HPPA) \ || defined(TARGET_MICROBLAZE) || defined(TARGET_TILEGX) int __pad; #endif diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h index af79fbf1de..2b8c0c6df6 100644 --- a/linux-user/syscall_types.h +++ b/linux-user/syscall_types.h @@ -14,6 +14,12 @@ STRUCT(serial_icounter_struct, STRUCT(sockaddr, TYPE_SHORT, MK_ARRAY(TYPE_CHAR, 14)) +STRUCT(timeval, + MK_ARRAY(TYPE_LONG, 2)) + +STRUCT(timespec, + MK_ARRAY(TYPE_LONG, 2)) + STRUCT(rtentry, TYPE_ULONG, MK_STRUCT(STRUCT_sockaddr), MK_STRUCT(STRUCT_sockaddr), MK_STRUCT(STRUCT_sockaddr), TYPE_SHORT, TYPE_SHORT, TYPE_ULONG, TYPE_PTRVOID, TYPE_SHORT, TYPE_PTRVOID, diff --git a/target/hppa/Makefile.objs b/target/hppa/Makefile.objs new file mode 100644 index 0000000000..263446fa0b --- /dev/null +++ b/target/hppa/Makefile.objs @@ -0,0 +1 @@ +obj-y += translate.o helper.o cpu.o op_helper.o gdbstub.o diff --git a/target/hppa/cpu-qom.h b/target/hppa/cpu-qom.h new file mode 100644 index 0000000000..9084e4701d --- /dev/null +++ b/target/hppa/cpu-qom.h @@ -0,0 +1,52 @@ +/* + * QEMU HPPA CPU + * + * Copyright (c) 2016 Richard Henderson <rth@twiddle.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ +#ifndef QEMU_HPPA_CPU_QOM_H +#define QEMU_HPPA_CPU_QOM_H + +#include "qom/cpu.h" + +#define TYPE_HPPA_CPU "hppa-cpu" + +#define HPPA_CPU_CLASS(klass) \ + OBJECT_CLASS_CHECK(HPPACPUClass, (klass), TYPE_HPPA_CPU) +#define HPPA_CPU(obj) \ + OBJECT_CHECK(HPPACPU, (obj), TYPE_HPPA_CPU) +#define HPPA_CPU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(HPPACPUClass, (obj), TYPE_HPPA_CPU) + +/** + * HPPACPUClass: + * @parent_realize: The parent class' realize handler. + * @parent_reset: The parent class' reset handler. + * + * An HPPA CPU model. + */ +typedef struct HPPACPUClass { + /*< private >*/ + CPUClass parent_class; + /*< public >*/ + + DeviceRealize parent_realize; + void (*parent_reset)(CPUState *cpu); +} HPPACPUClass; + +typedef struct HPPACPU HPPACPU; + +#endif diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c new file mode 100644 index 0000000000..1d791d0f80 --- /dev/null +++ b/target/hppa/cpu.c @@ -0,0 +1,164 @@ +/* + * QEMU HPPA CPU + * + * Copyright (c) 2016 Richard Henderson <rth@twiddle.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/lgpl-2.1.html> + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "cpu.h" +#include "qemu-common.h" +#include "migration/vmstate.h" +#include "exec/exec-all.h" + + +static void hppa_cpu_set_pc(CPUState *cs, vaddr value) +{ + HPPACPU *cpu = HPPA_CPU(cs); + + cpu->env.iaoq_f = value; + cpu->env.iaoq_b = value + 4; +} + +static void hppa_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) +{ + HPPACPU *cpu = HPPA_CPU(cs); + + cpu->env.iaoq_f = tb->pc; + cpu->env.iaoq_b = tb->cs_base; + cpu->env.psw_n = tb->flags & 1; +} + +static void hppa_cpu_disas_set_info(CPUState *cs, disassemble_info *info) +{ + info->mach = bfd_mach_hppa20; + info->print_insn = print_insn_hppa; +} + +static void hppa_cpu_realizefn(DeviceState *dev, Error **errp) +{ + CPUState *cs = CPU(dev); + HPPACPUClass *acc = HPPA_CPU_GET_CLASS(dev); + Error *local_err = NULL; + + cpu_exec_realizefn(cs, &local_err); + if (local_err != NULL) { + error_propagate(errp, local_err); + return; + } + + qemu_init_vcpu(cs); + acc->parent_realize(dev, errp); +} + +/* Sort hppabetically by type name. */ +static gint hppa_cpu_list_compare(gconstpointer a, gconstpointer b) +{ + ObjectClass *class_a = (ObjectClass *)a; + ObjectClass *class_b = (ObjectClass *)b; + const char *name_a, *name_b; + + name_a = object_class_get_name(class_a); + name_b = object_class_get_name(class_b); + return strcmp(name_a, name_b); +} + +static void hppa_cpu_list_entry(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CPUListState *s = user_data; + + (*s->cpu_fprintf)(s->file, " %s\n", object_class_get_name(oc)); +} + +void hppa_cpu_list(FILE *f, fprintf_function cpu_fprintf) +{ + CPUListState s = { + .file = f, + .cpu_fprintf = cpu_fprintf, + }; + GSList *list; + + list = object_class_get_list(TYPE_HPPA_CPU, false); + list = g_slist_sort(list, hppa_cpu_list_compare); + (*cpu_fprintf)(f, "Available CPUs:\n"); + g_slist_foreach(list, hppa_cpu_list_entry, &s); + g_slist_free(list); +} + +static void hppa_cpu_initfn(Object *obj) +{ + CPUState *cs = CPU(obj); + HPPACPU *cpu = HPPA_CPU(obj); + CPUHPPAState *env = &cpu->env; + + cs->env_ptr = env; + cpu_hppa_loaded_fr0(env); + set_snan_bit_is_one(true, &env->fp_status); + + hppa_translate_init(); +} + +HPPACPU *cpu_hppa_init(const char *cpu_model) +{ + HPPACPU *cpu; + + cpu = HPPA_CPU(object_new(TYPE_HPPA_CPU)); + + object_property_set_bool(OBJECT(cpu), true, "realized", NULL); + + return cpu; +} + +static void hppa_cpu_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + CPUClass *cc = CPU_CLASS(oc); + HPPACPUClass *acc = HPPA_CPU_CLASS(oc); + + acc->parent_realize = dc->realize; + dc->realize = hppa_cpu_realizefn; + + cc->do_interrupt = hppa_cpu_do_interrupt; + cc->cpu_exec_interrupt = hppa_cpu_exec_interrupt; + cc->dump_state = hppa_cpu_dump_state; + cc->set_pc = hppa_cpu_set_pc; + cc->synchronize_from_tb = hppa_cpu_synchronize_from_tb; + cc->gdb_read_register = hppa_cpu_gdb_read_register; + cc->gdb_write_register = hppa_cpu_gdb_write_register; + cc->handle_mmu_fault = hppa_cpu_handle_mmu_fault; + cc->disas_set_info = hppa_cpu_disas_set_info; + + cc->gdb_num_core_regs = 128; +} + +static const TypeInfo hppa_cpu_type_info = { + .name = TYPE_HPPA_CPU, + .parent = TYPE_CPU, + .instance_size = sizeof(HPPACPU), + .instance_init = hppa_cpu_initfn, + .abstract = false, + .class_size = sizeof(HPPACPUClass), + .class_init = hppa_cpu_class_init, +}; + +static void hppa_cpu_register_types(void) +{ + type_register_static(&hppa_cpu_type_info); +} + +type_init(hppa_cpu_register_types) diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h new file mode 100644 index 0000000000..4cf4ac65e3 --- /dev/null +++ b/target/hppa/cpu.h @@ -0,0 +1,144 @@ +/* + * PA-RISC emulation cpu definitions for qemu. + * + * Copyright (c) 2016 Richard Henderson <rth@twiddle.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef HPPA_CPU_H +#define HPPA_CPU_H + +#include "qemu-common.h" +#include "cpu-qom.h" + +/* We only support hppa-linux-user at present, so 32-bit only. */ +#define TARGET_LONG_BITS 32 +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + +#define CPUArchState struct CPUHPPAState + +#include "exec/cpu-defs.h" +#include "fpu/softfloat.h" + +#define TARGET_PAGE_BITS 12 + +#define ALIGNED_ONLY +#define NB_MMU_MODES 1 +#define MMU_USER_IDX 0 +#define TARGET_INSN_START_EXTRA_WORDS 1 + +#define EXCP_SYSCALL 1 +#define EXCP_SYSCALL_LWS 2 +#define EXCP_SIGSEGV 3 +#define EXCP_SIGILL 4 +#define EXCP_SIGFPE 5 + +typedef struct CPUHPPAState CPUHPPAState; + +struct CPUHPPAState { + target_ulong gr[32]; + uint64_t fr[32]; + + target_ulong sar; + target_ulong cr26; + target_ulong cr27; + + target_ulong psw_n; /* boolean */ + target_long psw_v; /* in most significant bit */ + + /* Splitting the carry-borrow field into the MSB and "the rest", allows + * for "the rest" to be deleted when it is unused, but the MSB is in use. + * In addition, it's easier to compute carry-in for bit B+1 than it is to + * compute carry-out for bit B (3 vs 4 insns for addition, assuming the + * host has the appropriate add-with-carry insn to compute the msb). + * Therefore the carry bits are stored as: cb_msb : cb & 0x11111110. + */ + target_ulong psw_cb; /* in least significant bit of next nibble */ + target_ulong psw_cb_msb; /* boolean */ + + target_ulong iaoq_f; /* front */ + target_ulong iaoq_b; /* back, aka next instruction */ + + target_ulong ior; /* interrupt offset register */ + + uint32_t fr0_shadow; /* flags, c, ca/cq, rm, d, enables */ + float_status fp_status; + + /* Those resources are used only in QEMU core */ + CPU_COMMON +}; + +/** + * HPPACPU: + * @env: #CPUHPPAState + * + * An HPPA CPU. + */ +struct HPPACPU { + /*< private >*/ + CPUState parent_obj; + /*< public >*/ + + CPUHPPAState env; +}; + +static inline HPPACPU *hppa_env_get_cpu(CPUHPPAState *env) +{ + return container_of(env, HPPACPU, env); +} + +#define ENV_GET_CPU(e) CPU(hppa_env_get_cpu(e)) +#define ENV_OFFSET offsetof(HPPACPU, env) + +#include "exec/cpu-all.h" + +static inline int cpu_mmu_index(CPUHPPAState *env, bool ifetch) +{ + return 0; +} + +void hppa_translate_init(void); + +HPPACPU *cpu_hppa_init(const char *cpu_model); + +#define cpu_init(cpu_model) CPU(cpu_hppa_init(cpu_model)) + +void hppa_cpu_list(FILE *f, fprintf_function cpu_fprintf); + +static inline void cpu_get_tb_cpu_state(CPUHPPAState *env, target_ulong *pc, + target_ulong *cs_base, + uint32_t *pflags) +{ + *pc = env->iaoq_f; + *cs_base = env->iaoq_b; + *pflags = env->psw_n; +} + +target_ulong cpu_hppa_get_psw(CPUHPPAState *env); +void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong); +void cpu_hppa_loaded_fr0(CPUHPPAState *env); + +#define cpu_signal_handler cpu_hppa_signal_handler + +int cpu_hppa_signal_handler(int host_signum, void *pinfo, void *puc); +int hppa_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int rw, int midx); +int hppa_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); +int hppa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); +void hppa_cpu_do_interrupt(CPUState *cpu); +bool hppa_cpu_exec_interrupt(CPUState *cpu, int int_req); +void hppa_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function, int); + +#endif /* HPPA_CPU_H */ diff --git a/target/hppa/gdbstub.c b/target/hppa/gdbstub.c new file mode 100644 index 0000000000..413a5e12ad --- /dev/null +++ b/target/hppa/gdbstub.c @@ -0,0 +1,111 @@ +/* + * HPPA gdb server stub + * + * Copyright (c) 2016 Richard Henderson <rth@twiddle.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "cpu.h" +#include "exec/gdbstub.h" + +int hppa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + HPPACPU *cpu = HPPA_CPU(cs); + CPUHPPAState *env = &cpu->env; + target_ulong val; + + switch (n) { + case 0: + val = cpu_hppa_get_psw(env); + break; + case 1 ... 31: + val = env->gr[n]; + break; + case 32: + val = env->sar; + break; + case 33: + val = env->iaoq_f; + break; + case 35: + val = env->iaoq_b; + break; + case 59: + val = env->cr26; + break; + case 60: + val = env->cr27; + break; + case 64 ... 127: + val = extract64(env->fr[(n - 64) / 2], (n & 1 ? 0 : 32), 32); + break; + default: + if (n < 128) { + val = 0; + } else { + return 0; + } + break; + } + return gdb_get_regl(mem_buf, val); +} + +int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) +{ + HPPACPU *cpu = HPPA_CPU(cs); + CPUHPPAState *env = &cpu->env; + target_ulong val = ldtul_p(mem_buf); + + switch (n) { + case 0: + cpu_hppa_put_psw(env, val); + break; + case 1 ... 31: + env->gr[n] = val; + break; + case 32: + env->sar = val; + break; + case 33: + env->iaoq_f = val; + break; + case 35: + env->iaoq_b = val; + case 59: + env->cr26 = val; + break; + case 60: + env->cr27 = val; + break; + case 64: + env->fr[0] = deposit64(env->fr[0], 32, 32, val); + cpu_hppa_loaded_fr0(env); + break; + case 65 ... 127: + { + uint64_t *fr = &env->fr[(n - 64) / 2]; + *fr = deposit64(*fr, val, (n & 1 ? 0 : 32), 32); + } + break; + default: + if (n >= 128) { + return 0; + } + break; + } + return sizeof(target_ulong); +} diff --git a/target/hppa/helper.c b/target/hppa/helper.c new file mode 100644 index 0000000000..ba04a9a52b --- /dev/null +++ b/target/hppa/helper.c @@ -0,0 +1,137 @@ +/* + * HPPA emulation cpu helpers for qemu. + * + * Copyright (c) 2016 Richard Henderson <rth@twiddle.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" + +#include "cpu.h" +#include "exec/exec-all.h" +#include "fpu/softfloat.h" +#include "exec/helper-proto.h" + +target_ulong cpu_hppa_get_psw(CPUHPPAState *env) +{ + target_ulong psw; + + /* Fold carry bits down to 8 consecutive bits. */ + /* ??? Needs tweaking for hppa64. */ + /* .......b...c...d...e...f...g...h */ + psw = (env->psw_cb >> 4) & 0x01111111; + /* .......b..bc..cd..de..ef..fg..gh */ + psw |= psw >> 3; + /* .............bcd............efgh */ + psw |= (psw >> 6) & 0x000f000f; + /* .........................bcdefgh */ + psw |= (psw >> 12) & 0xf; + psw |= env->psw_cb_msb << 7; + psw <<= 8; + + psw |= env->psw_n << 21; + psw |= (env->psw_v < 0) << 17; + + return psw; +} + +void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong psw) +{ + target_ulong cb = 0; + + env->psw_n = (psw >> 21) & 1; + env->psw_v = -((psw >> 17) & 1); + env->psw_cb_msb = (psw >> 15) & 1; + + cb |= ((psw >> 14) & 1) << 28; + cb |= ((psw >> 13) & 1) << 24; + cb |= ((psw >> 12) & 1) << 20; + cb |= ((psw >> 11) & 1) << 16; + cb |= ((psw >> 10) & 1) << 12; + cb |= ((psw >> 9) & 1) << 8; + cb |= ((psw >> 8) & 1) << 4; + env->psw_cb = cb; +} + +int hppa_cpu_handle_mmu_fault(CPUState *cs, vaddr address, + int rw, int mmu_idx) +{ + HPPACPU *cpu = HPPA_CPU(cs); + + cs->exception_index = EXCP_SIGSEGV; + cpu->env.ior = address; + return 1; +} + +void hppa_cpu_do_interrupt(CPUState *cs) +{ + HPPACPU *cpu = HPPA_CPU(cs); + CPUHPPAState *env = &cpu->env; + int i = cs->exception_index; + + if (qemu_loglevel_mask(CPU_LOG_INT)) { + static int count; + const char *name = "<unknown>"; + + switch (i) { + case EXCP_SYSCALL: + name = "syscall"; + break; + case EXCP_SIGSEGV: + name = "sigsegv"; + break; + case EXCP_SIGILL: + name = "sigill"; + break; + case EXCP_SIGFPE: + name = "sigfpe"; + break; + } + qemu_log("INT %6d: %s ia_f=" TARGET_FMT_lx "\n", + ++count, name, env->iaoq_f); + } + cs->exception_index = -1; +} + +bool hppa_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + abort(); + return false; +} + +void hppa_cpu_dump_state(CPUState *cs, FILE *f, + fprintf_function cpu_fprintf, int flags) +{ + HPPACPU *cpu = HPPA_CPU(cs); + CPUHPPAState *env = &cpu->env; + int i; + + cpu_fprintf(f, "IA_F " TARGET_FMT_lx + " IA_B " TARGET_FMT_lx + " PSW " TARGET_FMT_lx + " [N:" TARGET_FMT_ld " V:%d" + " CB:" TARGET_FMT_lx "]\n ", + env->iaoq_f, env->iaoq_b, cpu_hppa_get_psw(env), + env->psw_n, env->psw_v < 0, + ((env->psw_cb >> 4) & 0x01111111) | (env->psw_cb_msb << 28)); + for (i = 1; i < 32; i++) { + cpu_fprintf(f, "GR%02d " TARGET_FMT_lx " ", i, env->gr[i]); + if ((i % 4) == 3) { + cpu_fprintf(f, "\n"); + } + } + + /* ??? FR */ +} diff --git a/target/hppa/helper.h b/target/hppa/helper.h new file mode 100644 index 0000000000..789f07fc0a --- /dev/null +++ b/target/hppa/helper.h @@ -0,0 +1,66 @@ +DEF_HELPER_2(excp, noreturn, env, int) +DEF_HELPER_FLAGS_2(tsv, TCG_CALL_NO_WG, void, env, tl) +DEF_HELPER_FLAGS_2(tcond, TCG_CALL_NO_WG, void, env, tl) + +DEF_HELPER_FLAGS_3(stby_b, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(stby_e, TCG_CALL_NO_WG, void, env, tl, tl) + +DEF_HELPER_FLAGS_1(probe_r, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(probe_w, TCG_CALL_NO_RWG_SE, tl, tl) + +DEF_HELPER_FLAGS_1(loaded_fr0, TCG_CALL_NO_RWG, void, env) + +DEF_HELPER_FLAGS_2(fsqrt_s, TCG_CALL_NO_RWG, f32, env, f32) +DEF_HELPER_FLAGS_2(frnd_s, TCG_CALL_NO_RWG, f32, env, f32) +DEF_HELPER_FLAGS_3(fadd_s, TCG_CALL_NO_RWG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fsub_s, TCG_CALL_NO_RWG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fmpy_s, TCG_CALL_NO_RWG, f32, env, f32, f32) +DEF_HELPER_FLAGS_3(fdiv_s, TCG_CALL_NO_RWG, f32, env, f32, f32) + +DEF_HELPER_FLAGS_2(fsqrt_d, TCG_CALL_NO_RWG, f64, env, f64) +DEF_HELPER_FLAGS_2(frnd_d, TCG_CALL_NO_RWG, f64, env, f64) +DEF_HELPER_FLAGS_3(fadd_d, TCG_CALL_NO_RWG, f64, env, f64, f64) +DEF_HELPER_FLAGS_3(fsub_d, TCG_CALL_NO_RWG, f64, env, f64, f64) +DEF_HELPER_FLAGS_3(fmpy_d, TCG_CALL_NO_RWG, f64, env, f64, f64) +DEF_HELPER_FLAGS_3(fdiv_d, TCG_CALL_NO_RWG, f64, env, f64, f64) + +DEF_HELPER_FLAGS_2(fcnv_s_d, TCG_CALL_NO_RWG, f64, env, f32) +DEF_HELPER_FLAGS_2(fcnv_d_s, TCG_CALL_NO_RWG, f32, env, f64) + +DEF_HELPER_FLAGS_2(fcnv_w_s, TCG_CALL_NO_RWG, f32, env, s32) +DEF_HELPER_FLAGS_2(fcnv_dw_s, TCG_CALL_NO_RWG, f32, env, s64) +DEF_HELPER_FLAGS_2(fcnv_w_d, TCG_CALL_NO_RWG, f64, env, s32) +DEF_HELPER_FLAGS_2(fcnv_dw_d, TCG_CALL_NO_RWG, f64, env, s64) + +DEF_HELPER_FLAGS_2(fcnv_s_w, TCG_CALL_NO_RWG, s32, env, f32) +DEF_HELPER_FLAGS_2(fcnv_d_w, TCG_CALL_NO_RWG, s32, env, f64) +DEF_HELPER_FLAGS_2(fcnv_s_dw, TCG_CALL_NO_RWG, s64, env, f32) +DEF_HELPER_FLAGS_2(fcnv_d_dw, TCG_CALL_NO_RWG, s64, env, f64) + +DEF_HELPER_FLAGS_2(fcnv_t_s_w, TCG_CALL_NO_RWG, s32, env, f32) +DEF_HELPER_FLAGS_2(fcnv_t_d_w, TCG_CALL_NO_RWG, s32, env, f64) +DEF_HELPER_FLAGS_2(fcnv_t_s_dw, TCG_CALL_NO_RWG, s64, env, f32) +DEF_HELPER_FLAGS_2(fcnv_t_d_dw, TCG_CALL_NO_RWG, s64, env, f64) + +DEF_HELPER_FLAGS_2(fcnv_uw_s, TCG_CALL_NO_RWG, f32, env, i32) +DEF_HELPER_FLAGS_2(fcnv_udw_s, TCG_CALL_NO_RWG, f32, env, i64) +DEF_HELPER_FLAGS_2(fcnv_uw_d, TCG_CALL_NO_RWG, f64, env, i32) +DEF_HELPER_FLAGS_2(fcnv_udw_d, TCG_CALL_NO_RWG, f64, env, i64) + +DEF_HELPER_FLAGS_2(fcnv_s_uw, TCG_CALL_NO_RWG, i32, env, f32) +DEF_HELPER_FLAGS_2(fcnv_d_uw, TCG_CALL_NO_RWG, i32, env, f64) +DEF_HELPER_FLAGS_2(fcnv_s_udw, TCG_CALL_NO_RWG, i64, env, f32) +DEF_HELPER_FLAGS_2(fcnv_d_udw, TCG_CALL_NO_RWG, i64, env, f64) + +DEF_HELPER_FLAGS_2(fcnv_t_s_uw, TCG_CALL_NO_RWG, i32, env, f32) +DEF_HELPER_FLAGS_2(fcnv_t_d_uw, TCG_CALL_NO_RWG, i32, env, f64) +DEF_HELPER_FLAGS_2(fcnv_t_s_udw, TCG_CALL_NO_RWG, i64, env, f32) +DEF_HELPER_FLAGS_2(fcnv_t_d_udw, TCG_CALL_NO_RWG, i64, env, f64) + +DEF_HELPER_FLAGS_5(fcmp_s, TCG_CALL_NO_RWG, void, env, f32, f32, i32, i32) +DEF_HELPER_FLAGS_5(fcmp_d, TCG_CALL_NO_RWG, void, env, f64, f64, i32, i32) + +DEF_HELPER_FLAGS_4(fmpyfadd_s, TCG_CALL_NO_RWG, i32, env, i32, i32, i32) +DEF_HELPER_FLAGS_4(fmpynfadd_s, TCG_CALL_NO_RWG, i32, env, i32, i32, i32) +DEF_HELPER_FLAGS_4(fmpyfadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) +DEF_HELPER_FLAGS_4(fmpynfadd_d, TCG_CALL_NO_RWG, i64, env, i64, i64, i64) diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c new file mode 100644 index 0000000000..c05c0d5572 --- /dev/null +++ b/target/hppa/op_helper.c @@ -0,0 +1,570 @@ +/* + * Helpers for HPPA instructions. + * + * Copyright (c) 2016 Richard Henderson <rth@twiddle.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" + +void QEMU_NORETURN HELPER(excp)(CPUHPPAState *env, int excp) +{ + HPPACPU *cpu = hppa_env_get_cpu(env); + CPUState *cs = CPU(cpu); + + cs->exception_index = excp; + cpu_loop_exit(cs); +} + +static void QEMU_NORETURN dynexcp(CPUHPPAState *env, int excp, uintptr_t ra) +{ + HPPACPU *cpu = hppa_env_get_cpu(env); + CPUState *cs = CPU(cpu); + + cs->exception_index = excp; + cpu_loop_exit_restore(cs, ra); +} + +void HELPER(tsv)(CPUHPPAState *env, target_ulong cond) +{ + if (unlikely((target_long)cond < 0)) { + dynexcp(env, EXCP_SIGFPE, GETPC()); + } +} + +void HELPER(tcond)(CPUHPPAState *env, target_ulong cond) +{ + if (unlikely(cond)) { + dynexcp(env, EXCP_SIGFPE, GETPC()); + } +} + +static void atomic_store_3(CPUHPPAState *env, target_ulong addr, uint32_t val, + uint32_t mask, uintptr_t ra) +{ + uint32_t old, new, cmp; + +#ifdef CONFIG_USER_ONLY + uint32_t *haddr = g2h(addr - 1); + old = *haddr; + while (1) { + new = (old & ~mask) | (val & mask); + cmp = atomic_cmpxchg(haddr, old, new); + if (cmp == old) { + return; + } + old = cmp; + } +#else +#error "Not implemented." +#endif +} + +void HELPER(stby_b)(CPUHPPAState *env, target_ulong addr, target_ulong val) +{ + uintptr_t ra = GETPC(); + + switch (addr & 3) { + case 3: + cpu_stb_data_ra(env, addr, val, ra); + break; + case 2: + cpu_stw_data_ra(env, addr, val, ra); + break; + case 1: + /* The 3 byte store must appear atomic. */ + if (parallel_cpus) { + atomic_store_3(env, addr, val, 0x00ffffffu, ra); + } else { + cpu_stb_data_ra(env, addr, val >> 16, ra); + cpu_stw_data_ra(env, addr + 1, val, ra); + } + break; + default: + cpu_stl_data_ra(env, addr, val, ra); + break; + } +} + +void HELPER(stby_e)(CPUHPPAState *env, target_ulong addr, target_ulong val) +{ + uintptr_t ra = GETPC(); + + switch (addr & 3) { + case 3: + /* The 3 byte store must appear atomic. */ + if (parallel_cpus) { + atomic_store_3(env, addr - 3, val, 0xffffff00u, ra); + } else { + cpu_stw_data_ra(env, addr - 3, val >> 16, ra); + cpu_stb_data_ra(env, addr - 1, val >> 8, ra); + } + break; + case 2: + cpu_stw_data_ra(env, addr - 2, val >> 16, ra); + break; + case 1: + cpu_stb_data_ra(env, addr - 1, val >> 24, ra); + break; + default: + /* Nothing is stored, but protection is checked and the + cacheline is marked dirty. */ +#ifndef CONFIG_USER_ONLY + probe_write(env, addr, cpu_mmu_index(env, 0), ra); +#endif + break; + } +} + +target_ulong HELPER(probe_r)(target_ulong addr) +{ + return page_check_range(addr, 1, PAGE_READ); +} + +target_ulong HELPER(probe_w)(target_ulong addr) +{ + return page_check_range(addr, 1, PAGE_WRITE); +} + +void HELPER(loaded_fr0)(CPUHPPAState *env) +{ + uint32_t shadow = env->fr[0] >> 32; + int rm, d; + + env->fr0_shadow = shadow; + + switch (extract32(shadow, 9, 2)) { + default: + rm = float_round_nearest_even; + break; + case 1: + rm = float_round_to_zero; + break; + case 2: + rm = float_round_up; + break; + case 3: + rm = float_round_down; + break; + } + set_float_rounding_mode(rm, &env->fp_status); + + d = extract32(shadow, 5, 1); + set_flush_to_zero(d, &env->fp_status); + set_flush_inputs_to_zero(d, &env->fp_status); +} + +void cpu_hppa_loaded_fr0(CPUHPPAState *env) +{ + helper_loaded_fr0(env); +} + +#define CONVERT_BIT(X, SRC, DST) \ + ((SRC) > (DST) \ + ? (X) / ((SRC) / (DST)) & (DST) \ + : ((X) & (SRC)) * ((DST) / (SRC))) + +static void update_fr0_op(CPUHPPAState *env, uintptr_t ra) +{ + uint32_t soft_exp = get_float_exception_flags(&env->fp_status); + uint32_t hard_exp = 0; + uint32_t shadow = env->fr0_shadow; + + if (likely(soft_exp == 0)) { + env->fr[0] = (uint64_t)shadow << 32; + return; + } + set_float_exception_flags(0, &env->fp_status); + + hard_exp |= CONVERT_BIT(soft_exp, float_flag_inexact, 1u << 0); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_underflow, 1u << 1); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, 1u << 2); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, 1u << 3); + hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, 1u << 4); + shadow |= hard_exp << (32 - 5); + env->fr0_shadow = shadow; + env->fr[0] = (uint64_t)shadow << 32; + + if (hard_exp & shadow) { + dynexcp(env, EXCP_SIGFPE, ra); + } +} + +float32 HELPER(fsqrt_s)(CPUHPPAState *env, float32 arg) +{ + float32 ret = float32_sqrt(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(frnd_s)(CPUHPPAState *env, float32 arg) +{ + float32 ret = float32_round_to_int(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fadd_s)(CPUHPPAState *env, float32 a, float32 b) +{ + float32 ret = float32_add(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fsub_s)(CPUHPPAState *env, float32 a, float32 b) +{ + float32 ret = float32_sub(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fmpy_s)(CPUHPPAState *env, float32 a, float32 b) +{ + float32 ret = float32_mul(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fdiv_s)(CPUHPPAState *env, float32 a, float32 b) +{ + float32 ret = float32_div(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fsqrt_d)(CPUHPPAState *env, float64 arg) +{ + float64 ret = float64_sqrt(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(frnd_d)(CPUHPPAState *env, float64 arg) +{ + float64 ret = float64_round_to_int(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fadd_d)(CPUHPPAState *env, float64 a, float64 b) +{ + float64 ret = float64_add(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fsub_d)(CPUHPPAState *env, float64 a, float64 b) +{ + float64 ret = float64_sub(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fmpy_d)(CPUHPPAState *env, float64 a, float64 b) +{ + float64 ret = float64_mul(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fdiv_d)(CPUHPPAState *env, float64 a, float64 b) +{ + float64 ret = float64_div(a, b, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fcnv_s_d)(CPUHPPAState *env, float32 arg) +{ + float64 ret = float32_to_float64(arg, &env->fp_status); + ret = float64_maybe_silence_nan(ret, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fcnv_d_s)(CPUHPPAState *env, float64 arg) +{ + float32 ret = float64_to_float32(arg, &env->fp_status); + ret = float32_maybe_silence_nan(ret, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fcnv_w_s)(CPUHPPAState *env, int32_t arg) +{ + float32 ret = int32_to_float32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fcnv_dw_s)(CPUHPPAState *env, int64_t arg) +{ + float32 ret = int64_to_float32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fcnv_w_d)(CPUHPPAState *env, int32_t arg) +{ + float64 ret = int32_to_float64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fcnv_dw_d)(CPUHPPAState *env, int64_t arg) +{ + float64 ret = int64_to_float64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int32_t HELPER(fcnv_s_w)(CPUHPPAState *env, float32 arg) +{ + int32_t ret = float32_to_int32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int32_t HELPER(fcnv_d_w)(CPUHPPAState *env, float64 arg) +{ + int32_t ret = float64_to_int32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int64_t HELPER(fcnv_s_dw)(CPUHPPAState *env, float32 arg) +{ + int64_t ret = float32_to_int64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int64_t HELPER(fcnv_d_dw)(CPUHPPAState *env, float64 arg) +{ + int64_t ret = float64_to_int64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int32_t HELPER(fcnv_t_s_w)(CPUHPPAState *env, float32 arg) +{ + int32_t ret = float32_to_int32_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int32_t HELPER(fcnv_t_d_w)(CPUHPPAState *env, float64 arg) +{ + int32_t ret = float64_to_int32_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int64_t HELPER(fcnv_t_s_dw)(CPUHPPAState *env, float32 arg) +{ + int64_t ret = float32_to_int64_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +int64_t HELPER(fcnv_t_d_dw)(CPUHPPAState *env, float64 arg) +{ + int64_t ret = float64_to_int64_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fcnv_uw_s)(CPUHPPAState *env, uint32_t arg) +{ + float32 ret = uint32_to_float32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fcnv_udw_s)(CPUHPPAState *env, uint64_t arg) +{ + float32 ret = uint64_to_float32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fcnv_uw_d)(CPUHPPAState *env, uint32_t arg) +{ + float64 ret = uint32_to_float64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fcnv_udw_d)(CPUHPPAState *env, uint64_t arg) +{ + float64 ret = uint64_to_float64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint32_t HELPER(fcnv_s_uw)(CPUHPPAState *env, float32 arg) +{ + uint32_t ret = float32_to_uint32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint32_t HELPER(fcnv_d_uw)(CPUHPPAState *env, float64 arg) +{ + uint32_t ret = float64_to_uint32(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint64_t HELPER(fcnv_s_udw)(CPUHPPAState *env, float32 arg) +{ + uint64_t ret = float32_to_uint64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint64_t HELPER(fcnv_d_udw)(CPUHPPAState *env, float64 arg) +{ + uint64_t ret = float64_to_uint64(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint32_t HELPER(fcnv_t_s_uw)(CPUHPPAState *env, float32 arg) +{ + uint32_t ret = float32_to_uint32_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint32_t HELPER(fcnv_t_d_uw)(CPUHPPAState *env, float64 arg) +{ + uint32_t ret = float64_to_uint32_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint64_t HELPER(fcnv_t_s_udw)(CPUHPPAState *env, float32 arg) +{ + uint64_t ret = float32_to_uint64_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +uint64_t HELPER(fcnv_t_d_udw)(CPUHPPAState *env, float64 arg) +{ + uint64_t ret = float64_to_uint64_round_to_zero(arg, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +static void update_fr0_cmp(CPUHPPAState *env, uint32_t y, uint32_t c, int r) +{ + uint32_t shadow = env->fr0_shadow; + + switch (r) { + case float_relation_greater: + c = extract32(c, 4, 1); + break; + case float_relation_less: + c = extract32(c, 3, 1); + break; + case float_relation_equal: + c = extract32(c, 2, 1); + break; + case float_relation_unordered: + c = extract32(c, 1, 1); + break; + default: + g_assert_not_reached(); + } + + if (y) { + /* targeted comparison */ + /* set fpsr[ca[y - 1]] to current compare */ + shadow = deposit32(shadow, 21 - (y - 1), 1, c); + } else { + /* queued comparison */ + /* shift cq right by one place */ + shadow = deposit32(shadow, 11, 10, extract32(shadow, 12, 10)); + /* move fpsr[c] to fpsr[cq[0]] */ + shadow = deposit32(shadow, 21, 1, extract32(shadow, 26, 1)); + /* set fpsr[c] to current compare */ + shadow = deposit32(shadow, 26, 1, c); + } + + env->fr0_shadow = shadow; + env->fr[0] = (uint64_t)shadow << 32; +} + +void HELPER(fcmp_s)(CPUHPPAState *env, float32 a, float32 b, + uint32_t y, uint32_t c) +{ + int r; + if (c & 1) { + r = float32_compare(a, b, &env->fp_status); + } else { + r = float32_compare_quiet(a, b, &env->fp_status); + } + update_fr0_op(env, GETPC()); + update_fr0_cmp(env, y, c, r); +} + +void HELPER(fcmp_d)(CPUHPPAState *env, float64 a, float64 b, + uint32_t y, uint32_t c) +{ + int r; + if (c & 1) { + r = float64_compare(a, b, &env->fp_status); + } else { + r = float64_compare_quiet(a, b, &env->fp_status); + } + update_fr0_op(env, GETPC()); + update_fr0_cmp(env, y, c, r); +} + +float32 HELPER(fmpyfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) +{ + float32 ret = float32_muladd(a, b, c, 0, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float32 HELPER(fmpynfadd_s)(CPUHPPAState *env, float32 a, float32 b, float32 c) +{ + float32 ret = float32_muladd(a, b, c, float_muladd_negate_product, + &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fmpyfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) +{ + float64 ret = float64_muladd(a, b, c, 0, &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} + +float64 HELPER(fmpynfadd_d)(CPUHPPAState *env, float64 a, float64 b, float64 c) +{ + float64 ret = float64_muladd(a, b, c, float_muladd_negate_product, + &env->fp_status); + update_fr0_op(env, GETPC()); + return ret; +} diff --git a/target/hppa/translate.c b/target/hppa/translate.c new file mode 100644 index 0000000000..4d243f7d3d --- /dev/null +++ b/target/hppa/translate.c @@ -0,0 +1,3946 @@ +/* + * HPPA emulation cpu translation for qemu. + * + * Copyright (c) 2016 Richard Henderson <rth@twiddle.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "disas/disas.h" +#include "qemu/host-utils.h" +#include "exec/exec-all.h" +#include "tcg-op.h" +#include "exec/cpu_ldst.h" + +#include "exec/helper-proto.h" +#include "exec/helper-gen.h" + +#include "trace-tcg.h" +#include "exec/log.h" + +typedef struct DisasCond { + TCGCond c; + TCGv a0, a1; + bool a0_is_n; + bool a1_is_0; +} DisasCond; + +typedef struct DisasContext { + struct TranslationBlock *tb; + CPUState *cs; + + target_ulong iaoq_f; + target_ulong iaoq_b; + target_ulong iaoq_n; + TCGv iaoq_n_var; + + int ntemps; + TCGv temps[8]; + + DisasCond null_cond; + TCGLabel *null_lab; + + bool singlestep_enabled; + bool psw_n_nonzero; +} DisasContext; + +/* Return values from translate_one, indicating the state of the TB. + Note that zero indicates that we are not exiting the TB. */ + +typedef enum { + NO_EXIT, + + /* We have emitted one or more goto_tb. No fixup required. */ + EXIT_GOTO_TB, + + /* We are not using a goto_tb (for whatever reason), but have updated + the iaq (for whatever reason), so don't do it again on exit. */ + EXIT_IAQ_N_UPDATED, + + /* We are exiting the TB, but have neither emitted a goto_tb, nor + updated the iaq for the next instruction to be executed. */ + EXIT_IAQ_N_STALE, + + /* We are ending the TB with a noreturn function call, e.g. longjmp. + No following code will be executed. */ + EXIT_NORETURN, +} ExitStatus; + +typedef struct DisasInsn { + uint32_t insn, mask; + ExitStatus (*trans)(DisasContext *ctx, uint32_t insn, + const struct DisasInsn *f); + union { + void (*f_ttt)(TCGv, TCGv, TCGv); + void (*f_weww)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32); + void (*f_dedd)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64); + void (*f_wew)(TCGv_i32, TCGv_env, TCGv_i32); + void (*f_ded)(TCGv_i64, TCGv_env, TCGv_i64); + void (*f_wed)(TCGv_i32, TCGv_env, TCGv_i64); + void (*f_dew)(TCGv_i64, TCGv_env, TCGv_i32); + }; +} DisasInsn; + +/* global register indexes */ +static TCGv_env cpu_env; +static TCGv cpu_gr[32]; +static TCGv cpu_iaoq_f; +static TCGv cpu_iaoq_b; +static TCGv cpu_sar; +static TCGv cpu_psw_n; +static TCGv cpu_psw_v; +static TCGv cpu_psw_cb; +static TCGv cpu_psw_cb_msb; +static TCGv cpu_cr26; +static TCGv cpu_cr27; + +#include "exec/gen-icount.h" + +void hppa_translate_init(void) +{ +#define DEF_VAR(V) { &cpu_##V, #V, offsetof(CPUHPPAState, V) } + + typedef struct { TCGv *var; const char *name; int ofs; } GlobalVar; + static const GlobalVar vars[] = { + DEF_VAR(sar), + DEF_VAR(cr26), + DEF_VAR(cr27), + DEF_VAR(psw_n), + DEF_VAR(psw_v), + DEF_VAR(psw_cb), + DEF_VAR(psw_cb_msb), + DEF_VAR(iaoq_f), + DEF_VAR(iaoq_b), + }; + +#undef DEF_VAR + + /* Use the symbolic register names that match the disassembler. */ + static const char gr_names[32][4] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31" + }; + + static bool done_init = 0; + int i; + + if (done_init) { + return; + } + done_init = 1; + + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + tcg_ctx.tcg_env = cpu_env; + + TCGV_UNUSED(cpu_gr[0]); + for (i = 1; i < 32; i++) { + cpu_gr[i] = tcg_global_mem_new(cpu_env, + offsetof(CPUHPPAState, gr[i]), + gr_names[i]); + } + + for (i = 0; i < ARRAY_SIZE(vars); ++i) { + const GlobalVar *v = &vars[i]; + *v->var = tcg_global_mem_new(cpu_env, v->ofs, v->name); + } +} + +static DisasCond cond_make_f(void) +{ + DisasCond r = { .c = TCG_COND_NEVER }; + TCGV_UNUSED(r.a0); + TCGV_UNUSED(r.a1); + return r; +} + +static DisasCond cond_make_n(void) +{ + DisasCond r = { .c = TCG_COND_NE, .a0_is_n = true, .a1_is_0 = true }; + r.a0 = cpu_psw_n; + TCGV_UNUSED(r.a1); + return r; +} + +static DisasCond cond_make_0(TCGCond c, TCGv a0) +{ + DisasCond r = { .c = c, .a1_is_0 = true }; + + assert (c != TCG_COND_NEVER && c != TCG_COND_ALWAYS); + r.a0 = tcg_temp_new(); + tcg_gen_mov_tl(r.a0, a0); + TCGV_UNUSED(r.a1); + + return r; +} + +static DisasCond cond_make(TCGCond c, TCGv a0, TCGv a1) +{ + DisasCond r = { .c = c }; + + assert (c != TCG_COND_NEVER && c != TCG_COND_ALWAYS); + r.a0 = tcg_temp_new(); + tcg_gen_mov_tl(r.a0, a0); + r.a1 = tcg_temp_new(); + tcg_gen_mov_tl(r.a1, a1); + + return r; +} + +static void cond_prep(DisasCond *cond) +{ + if (cond->a1_is_0) { + cond->a1_is_0 = false; + cond->a1 = tcg_const_tl(0); + } +} + +static void cond_free(DisasCond *cond) +{ + switch (cond->c) { + default: + if (!cond->a0_is_n) { + tcg_temp_free(cond->a0); + } + if (!cond->a1_is_0) { + tcg_temp_free(cond->a1); + } + cond->a0_is_n = false; + cond->a1_is_0 = false; + TCGV_UNUSED(cond->a0); + TCGV_UNUSED(cond->a1); + /* fallthru */ + case TCG_COND_ALWAYS: + cond->c = TCG_COND_NEVER; + break; + case TCG_COND_NEVER: + break; + } +} + +static TCGv get_temp(DisasContext *ctx) +{ + unsigned i = ctx->ntemps++; + g_assert(i < ARRAY_SIZE(ctx->temps)); + return ctx->temps[i] = tcg_temp_new(); +} + +static TCGv load_const(DisasContext *ctx, target_long v) +{ + TCGv t = get_temp(ctx); + tcg_gen_movi_tl(t, v); + return t; +} + +static TCGv load_gpr(DisasContext *ctx, unsigned reg) +{ + if (reg == 0) { + TCGv t = get_temp(ctx); + tcg_gen_movi_tl(t, 0); + return t; + } else { + return cpu_gr[reg]; + } +} + +static TCGv dest_gpr(DisasContext *ctx, unsigned reg) +{ + if (reg == 0 || ctx->null_cond.c != TCG_COND_NEVER) { + return get_temp(ctx); + } else { + return cpu_gr[reg]; + } +} + +static void save_or_nullify(DisasContext *ctx, TCGv dest, TCGv t) +{ + if (ctx->null_cond.c != TCG_COND_NEVER) { + cond_prep(&ctx->null_cond); + tcg_gen_movcond_tl(ctx->null_cond.c, dest, ctx->null_cond.a0, + ctx->null_cond.a1, dest, t); + } else { + tcg_gen_mov_tl(dest, t); + } +} + +static void save_gpr(DisasContext *ctx, unsigned reg, TCGv t) +{ + if (reg != 0) { + save_or_nullify(ctx, cpu_gr[reg], t); + } +} + +#ifdef HOST_WORDS_BIGENDIAN +# define HI_OFS 0 +# define LO_OFS 4 +#else +# define HI_OFS 4 +# define LO_OFS 0 +#endif + +static TCGv_i32 load_frw_i32(unsigned rt) +{ + TCGv_i32 ret = tcg_temp_new_i32(); + tcg_gen_ld_i32(ret, cpu_env, + offsetof(CPUHPPAState, fr[rt & 31]) + + (rt & 32 ? LO_OFS : HI_OFS)); + return ret; +} + +static TCGv_i32 load_frw0_i32(unsigned rt) +{ + if (rt == 0) { + return tcg_const_i32(0); + } else { + return load_frw_i32(rt); + } +} + +static TCGv_i64 load_frw0_i64(unsigned rt) +{ + if (rt == 0) { + return tcg_const_i64(0); + } else { + TCGv_i64 ret = tcg_temp_new_i64(); + tcg_gen_ld32u_i64(ret, cpu_env, + offsetof(CPUHPPAState, fr[rt & 31]) + + (rt & 32 ? LO_OFS : HI_OFS)); + return ret; + } +} + +static void save_frw_i32(unsigned rt, TCGv_i32 val) +{ + tcg_gen_st_i32(val, cpu_env, + offsetof(CPUHPPAState, fr[rt & 31]) + + (rt & 32 ? LO_OFS : HI_OFS)); +} + +#undef HI_OFS +#undef LO_OFS + +static TCGv_i64 load_frd(unsigned rt) +{ + TCGv_i64 ret = tcg_temp_new_i64(); + tcg_gen_ld_i64(ret, cpu_env, offsetof(CPUHPPAState, fr[rt])); + return ret; +} + +static TCGv_i64 load_frd0(unsigned rt) +{ + if (rt == 0) { + return tcg_const_i64(0); + } else { + return load_frd(rt); + } +} + +static void save_frd(unsigned rt, TCGv_i64 val) +{ + tcg_gen_st_i64(val, cpu_env, offsetof(CPUHPPAState, fr[rt])); +} + +/* Skip over the implementation of an insn that has been nullified. + Use this when the insn is too complex for a conditional move. */ +static void nullify_over(DisasContext *ctx) +{ + if (ctx->null_cond.c != TCG_COND_NEVER) { + /* The always condition should have been handled in the main loop. */ + assert(ctx->null_cond.c != TCG_COND_ALWAYS); + + ctx->null_lab = gen_new_label(); + cond_prep(&ctx->null_cond); + + /* If we're using PSW[N], copy it to a temp because... */ + if (ctx->null_cond.a0_is_n) { + ctx->null_cond.a0_is_n = false; + ctx->null_cond.a0 = tcg_temp_new(); + tcg_gen_mov_tl(ctx->null_cond.a0, cpu_psw_n); + } + /* ... we clear it before branching over the implementation, + so that (1) it's clear after nullifying this insn and + (2) if this insn nullifies the next, PSW[N] is valid. */ + if (ctx->psw_n_nonzero) { + ctx->psw_n_nonzero = false; + tcg_gen_movi_tl(cpu_psw_n, 0); + } + + tcg_gen_brcond_tl(ctx->null_cond.c, ctx->null_cond.a0, + ctx->null_cond.a1, ctx->null_lab); + cond_free(&ctx->null_cond); + } +} + +/* Save the current nullification state to PSW[N]. */ +static void nullify_save(DisasContext *ctx) +{ + if (ctx->null_cond.c == TCG_COND_NEVER) { + if (ctx->psw_n_nonzero) { + tcg_gen_movi_tl(cpu_psw_n, 0); + } + return; + } + if (!ctx->null_cond.a0_is_n) { + cond_prep(&ctx->null_cond); + tcg_gen_setcond_tl(ctx->null_cond.c, cpu_psw_n, + ctx->null_cond.a0, ctx->null_cond.a1); + ctx->psw_n_nonzero = true; + } + cond_free(&ctx->null_cond); +} + +/* Set a PSW[N] to X. The intention is that this is used immediately + before a goto_tb/exit_tb, so that there is no fallthru path to other + code within the TB. Therefore we do not update psw_n_nonzero. */ +static void nullify_set(DisasContext *ctx, bool x) +{ + if (ctx->psw_n_nonzero || x) { + tcg_gen_movi_tl(cpu_psw_n, x); + } +} + +/* Mark the end of an instruction that may have been nullified. + This is the pair to nullify_over. */ +static ExitStatus nullify_end(DisasContext *ctx, ExitStatus status) +{ + TCGLabel *null_lab = ctx->null_lab; + + if (likely(null_lab == NULL)) { + /* The current insn wasn't conditional or handled the condition + applied to it without a branch, so the (new) setting of + NULL_COND can be applied directly to the next insn. */ + return status; + } + ctx->null_lab = NULL; + + if (likely(ctx->null_cond.c == TCG_COND_NEVER)) { + /* The next instruction will be unconditional, + and NULL_COND already reflects that. */ + gen_set_label(null_lab); + } else { + /* The insn that we just executed is itself nullifying the next + instruction. Store the condition in the PSW[N] global. + We asserted PSW[N] = 0 in nullify_over, so that after the + label we have the proper value in place. */ + nullify_save(ctx); + gen_set_label(null_lab); + ctx->null_cond = cond_make_n(); + } + + assert(status != EXIT_GOTO_TB && status != EXIT_IAQ_N_UPDATED); + if (status == EXIT_NORETURN) { + status = NO_EXIT; + } + return status; +} + +static void copy_iaoq_entry(TCGv dest, target_ulong ival, TCGv vval) +{ + if (unlikely(ival == -1)) { + tcg_gen_mov_tl(dest, vval); + } else { + tcg_gen_movi_tl(dest, ival); + } +} + +static inline target_ulong iaoq_dest(DisasContext *ctx, target_long disp) +{ + return ctx->iaoq_f + disp + 8; +} + +static void gen_excp_1(int exception) +{ + TCGv_i32 t = tcg_const_i32(exception); + gen_helper_excp(cpu_env, t); + tcg_temp_free_i32(t); +} + +static ExitStatus gen_excp(DisasContext *ctx, int exception) +{ + copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f); + copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b); + nullify_save(ctx); + gen_excp_1(exception); + return EXIT_NORETURN; +} + +static ExitStatus gen_illegal(DisasContext *ctx) +{ + nullify_over(ctx); + return nullify_end(ctx, gen_excp(ctx, EXCP_SIGILL)); +} + +static bool use_goto_tb(DisasContext *ctx, target_ulong dest) +{ + /* Suppress goto_tb in the case of single-steping and IO. */ + if ((ctx->tb->cflags & CF_LAST_IO) || ctx->singlestep_enabled) { + return false; + } + return true; +} + +/* If the next insn is to be nullified, and it's on the same page, + and we're not attempting to set a breakpoint on it, then we can + totally skip the nullified insn. This avoids creating and + executing a TB that merely branches to the next TB. */ +static bool use_nullify_skip(DisasContext *ctx) +{ + return (((ctx->iaoq_b ^ ctx->iaoq_f) & TARGET_PAGE_MASK) == 0 + && !cpu_breakpoint_test(ctx->cs, ctx->iaoq_b, BP_ANY)); +} + +static void gen_goto_tb(DisasContext *ctx, int which, + target_ulong f, target_ulong b) +{ + if (f != -1 && b != -1 && use_goto_tb(ctx, f)) { + tcg_gen_goto_tb(which); + tcg_gen_movi_tl(cpu_iaoq_f, f); + tcg_gen_movi_tl(cpu_iaoq_b, b); + tcg_gen_exit_tb((uintptr_t)ctx->tb + which); + } else { + copy_iaoq_entry(cpu_iaoq_f, f, cpu_iaoq_b); + copy_iaoq_entry(cpu_iaoq_b, b, ctx->iaoq_n_var); + if (ctx->singlestep_enabled) { + gen_excp_1(EXCP_DEBUG); + } else { + tcg_gen_exit_tb(0); + } + } +} + +/* PA has a habit of taking the LSB of a field and using that as the sign, + with the rest of the field becoming the least significant bits. */ +static target_long low_sextract(uint32_t val, int pos, int len) +{ + target_ulong x = -(target_ulong)extract32(val, pos, 1); + x = (x << (len - 1)) | extract32(val, pos + 1, len - 1); + return x; +} + +static unsigned assemble_rt64(uint32_t insn) +{ + unsigned r1 = extract32(insn, 6, 1); + unsigned r0 = extract32(insn, 0, 5); + return r1 * 32 + r0; +} + +static unsigned assemble_ra64(uint32_t insn) +{ + unsigned r1 = extract32(insn, 7, 1); + unsigned r0 = extract32(insn, 21, 5); + return r1 * 32 + r0; +} + +static unsigned assemble_rb64(uint32_t insn) +{ + unsigned r1 = extract32(insn, 12, 1); + unsigned r0 = extract32(insn, 16, 5); + return r1 * 32 + r0; +} + +static unsigned assemble_rc64(uint32_t insn) +{ + unsigned r2 = extract32(insn, 8, 1); + unsigned r1 = extract32(insn, 13, 3); + unsigned r0 = extract32(insn, 9, 2); + return r2 * 32 + r1 * 4 + r0; +} + +static target_long assemble_12(uint32_t insn) +{ + target_ulong x = -(target_ulong)(insn & 1); + x = (x << 1) | extract32(insn, 2, 1); + x = (x << 10) | extract32(insn, 3, 10); + return x; +} + +static target_long assemble_16(uint32_t insn) +{ + /* Take the name from PA2.0, which produces a 16-bit number + only with wide mode; otherwise a 14-bit number. Since we don't + implement wide mode, this is always the 14-bit number. */ + return low_sextract(insn, 0, 14); +} + +static target_long assemble_16a(uint32_t insn) +{ + /* Take the name from PA2.0, which produces a 14-bit shifted number + only with wide mode; otherwise a 12-bit shifted number. Since we + don't implement wide mode, this is always the 12-bit number. */ + target_ulong x = -(target_ulong)(insn & 1); + x = (x << 11) | extract32(insn, 2, 11); + return x << 2; +} + +static target_long assemble_17(uint32_t insn) +{ + target_ulong x = -(target_ulong)(insn & 1); + x = (x << 5) | extract32(insn, 16, 5); + x = (x << 1) | extract32(insn, 2, 1); + x = (x << 10) | extract32(insn, 3, 10); + return x << 2; +} + +static target_long assemble_21(uint32_t insn) +{ + target_ulong x = -(target_ulong)(insn & 1); + x = (x << 11) | extract32(insn, 1, 11); + x = (x << 2) | extract32(insn, 14, 2); + x = (x << 5) | extract32(insn, 16, 5); + x = (x << 2) | extract32(insn, 12, 2); + return x << 11; +} + +static target_long assemble_22(uint32_t insn) +{ + target_ulong x = -(target_ulong)(insn & 1); + x = (x << 10) | extract32(insn, 16, 10); + x = (x << 1) | extract32(insn, 2, 1); + x = (x << 10) | extract32(insn, 3, 10); + return x << 2; +} + +/* The parisc documentation describes only the general interpretation of + the conditions, without describing their exact implementation. The + interpretations do not stand up well when considering ADD,C and SUB,B. + However, considering the Addition, Subtraction and Logical conditions + as a whole it would appear that these relations are similar to what + a traditional NZCV set of flags would produce. */ + +static DisasCond do_cond(unsigned cf, TCGv res, TCGv cb_msb, TCGv sv) +{ + DisasCond cond; + TCGv tmp; + + switch (cf >> 1) { + case 0: /* Never / TR */ + cond = cond_make_f(); + break; + case 1: /* = / <> (Z / !Z) */ + cond = cond_make_0(TCG_COND_EQ, res); + break; + case 2: /* < / >= (N / !N) */ + cond = cond_make_0(TCG_COND_LT, res); + break; + case 3: /* <= / > (N | Z / !N & !Z) */ + cond = cond_make_0(TCG_COND_LE, res); + break; + case 4: /* NUV / UV (!C / C) */ + cond = cond_make_0(TCG_COND_EQ, cb_msb); + break; + case 5: /* ZNV / VNZ (!C | Z / C & !Z) */ + tmp = tcg_temp_new(); + tcg_gen_neg_tl(tmp, cb_msb); + tcg_gen_and_tl(tmp, tmp, res); + cond = cond_make_0(TCG_COND_EQ, tmp); + tcg_temp_free(tmp); + break; + case 6: /* SV / NSV (V / !V) */ + cond = cond_make_0(TCG_COND_LT, sv); + break; + case 7: /* OD / EV */ + tmp = tcg_temp_new(); + tcg_gen_andi_tl(tmp, res, 1); + cond = cond_make_0(TCG_COND_NE, tmp); + tcg_temp_free(tmp); + break; + default: + g_assert_not_reached(); + } + if (cf & 1) { + cond.c = tcg_invert_cond(cond.c); + } + + return cond; +} + +/* Similar, but for the special case of subtraction without borrow, we + can use the inputs directly. This can allow other computation to be + deleted as unused. */ + +static DisasCond do_sub_cond(unsigned cf, TCGv res, TCGv in1, TCGv in2, TCGv sv) +{ + DisasCond cond; + + switch (cf >> 1) { + case 1: /* = / <> */ + cond = cond_make(TCG_COND_EQ, in1, in2); + break; + case 2: /* < / >= */ + cond = cond_make(TCG_COND_LT, in1, in2); + break; + case 3: /* <= / > */ + cond = cond_make(TCG_COND_LE, in1, in2); + break; + case 4: /* << / >>= */ + cond = cond_make(TCG_COND_LTU, in1, in2); + break; + case 5: /* <<= / >> */ + cond = cond_make(TCG_COND_LEU, in1, in2); + break; + default: + return do_cond(cf, res, sv, sv); + } + if (cf & 1) { + cond.c = tcg_invert_cond(cond.c); + } + + return cond; +} + +/* Similar, but for logicals, where the carry and overflow bits are not + computed, and use of them is undefined. */ + +static DisasCond do_log_cond(unsigned cf, TCGv res) +{ + switch (cf >> 1) { + case 4: case 5: case 6: + cf &= 1; + break; + } + return do_cond(cf, res, res, res); +} + +/* Similar, but for shift/extract/deposit conditions. */ + +static DisasCond do_sed_cond(unsigned orig, TCGv res) +{ + unsigned c, f; + + /* Convert the compressed condition codes to standard. + 0-2 are the same as logicals (nv,<,<=), while 3 is OD. + 4-7 are the reverse of 0-3. */ + c = orig & 3; + if (c == 3) { + c = 7; + } + f = (orig & 4) / 4; + + return do_log_cond(c * 2 + f, res); +} + +/* Similar, but for unit conditions. */ + +static DisasCond do_unit_cond(unsigned cf, TCGv res, TCGv in1, TCGv in2) +{ + DisasCond cond; + TCGv tmp, cb; + + TCGV_UNUSED(cb); + if (cf & 8) { + /* Since we want to test lots of carry-out bits all at once, do not + * do our normal thing and compute carry-in of bit B+1 since that + * leaves us with carry bits spread across two words. + */ + cb = tcg_temp_new(); + tmp = tcg_temp_new(); + tcg_gen_or_tl(cb, in1, in2); + tcg_gen_and_tl(tmp, in1, in2); + tcg_gen_andc_tl(cb, cb, res); + tcg_gen_or_tl(cb, cb, tmp); + tcg_temp_free(tmp); + } + + switch (cf >> 1) { + case 0: /* never / TR */ + case 1: /* undefined */ + case 5: /* undefined */ + cond = cond_make_f(); + break; + + case 2: /* SBZ / NBZ */ + /* See hasless(v,1) from + * https://graphics.stanford.edu/~seander/bithacks.html#ZeroInWord + */ + tmp = tcg_temp_new(); + tcg_gen_subi_tl(tmp, res, 0x01010101u); + tcg_gen_andc_tl(tmp, tmp, res); + tcg_gen_andi_tl(tmp, tmp, 0x80808080u); + cond = cond_make_0(TCG_COND_NE, tmp); + tcg_temp_free(tmp); + break; + + case 3: /* SHZ / NHZ */ + tmp = tcg_temp_new(); + tcg_gen_subi_tl(tmp, res, 0x00010001u); + tcg_gen_andc_tl(tmp, tmp, res); + tcg_gen_andi_tl(tmp, tmp, 0x80008000u); + cond = cond_make_0(TCG_COND_NE, tmp); + tcg_temp_free(tmp); + break; + + case 4: /* SDC / NDC */ + tcg_gen_andi_tl(cb, cb, 0x88888888u); + cond = cond_make_0(TCG_COND_NE, cb); + break; + + case 6: /* SBC / NBC */ + tcg_gen_andi_tl(cb, cb, 0x80808080u); + cond = cond_make_0(TCG_COND_NE, cb); + break; + + case 7: /* SHC / NHC */ + tcg_gen_andi_tl(cb, cb, 0x80008000u); + cond = cond_make_0(TCG_COND_NE, cb); + break; + + default: + g_assert_not_reached(); + } + if (cf & 8) { + tcg_temp_free(cb); + } + if (cf & 1) { + cond.c = tcg_invert_cond(cond.c); + } + + return cond; +} + +/* Compute signed overflow for addition. */ +static TCGv do_add_sv(DisasContext *ctx, TCGv res, TCGv in1, TCGv in2) +{ + TCGv sv = get_temp(ctx); + TCGv tmp = tcg_temp_new(); + + tcg_gen_xor_tl(sv, res, in1); + tcg_gen_xor_tl(tmp, in1, in2); + tcg_gen_andc_tl(sv, sv, tmp); + tcg_temp_free(tmp); + + return sv; +} + +/* Compute signed overflow for subtraction. */ +static TCGv do_sub_sv(DisasContext *ctx, TCGv res, TCGv in1, TCGv in2) +{ + TCGv sv = get_temp(ctx); + TCGv tmp = tcg_temp_new(); + + tcg_gen_xor_tl(sv, res, in1); + tcg_gen_xor_tl(tmp, in1, in2); + tcg_gen_and_tl(sv, sv, tmp); + tcg_temp_free(tmp); + + return sv; +} + +static ExitStatus do_add(DisasContext *ctx, unsigned rt, TCGv in1, TCGv in2, + unsigned shift, bool is_l, bool is_tsv, bool is_tc, + bool is_c, unsigned cf) +{ + TCGv dest, cb, cb_msb, sv, tmp; + unsigned c = cf >> 1; + DisasCond cond; + + dest = tcg_temp_new(); + TCGV_UNUSED(cb); + TCGV_UNUSED(cb_msb); + + if (shift) { + tmp = get_temp(ctx); + tcg_gen_shli_tl(tmp, in1, shift); + in1 = tmp; + } + + if (!is_l || c == 4 || c == 5) { + TCGv zero = tcg_const_tl(0); + cb_msb = get_temp(ctx); + tcg_gen_add2_tl(dest, cb_msb, in1, zero, in2, zero); + if (is_c) { + tcg_gen_add2_tl(dest, cb_msb, dest, cb_msb, cpu_psw_cb_msb, zero); + } + tcg_temp_free(zero); + if (!is_l) { + cb = get_temp(ctx); + tcg_gen_xor_tl(cb, in1, in2); + tcg_gen_xor_tl(cb, cb, dest); + } + } else { + tcg_gen_add_tl(dest, in1, in2); + if (is_c) { + tcg_gen_add_tl(dest, dest, cpu_psw_cb_msb); + } + } + + /* Compute signed overflow if required. */ + TCGV_UNUSED(sv); + if (is_tsv || c == 6) { + sv = do_add_sv(ctx, dest, in1, in2); + if (is_tsv) { + /* ??? Need to include overflow from shift. */ + gen_helper_tsv(cpu_env, sv); + } + } + + /* Emit any conditional trap before any writeback. */ + cond = do_cond(cf, dest, cb_msb, sv); + if (is_tc) { + cond_prep(&cond); + tmp = tcg_temp_new(); + tcg_gen_setcond_tl(cond.c, tmp, cond.a0, cond.a1); + gen_helper_tcond(cpu_env, tmp); + tcg_temp_free(tmp); + } + + /* Write back the result. */ + if (!is_l) { + save_or_nullify(ctx, cpu_psw_cb, cb); + save_or_nullify(ctx, cpu_psw_cb_msb, cb_msb); + } + save_gpr(ctx, rt, dest); + tcg_temp_free(dest); + + /* Install the new nullification. */ + cond_free(&ctx->null_cond); + ctx->null_cond = cond; + return NO_EXIT; +} + +static ExitStatus do_sub(DisasContext *ctx, unsigned rt, TCGv in1, TCGv in2, + bool is_tsv, bool is_b, bool is_tc, unsigned cf) +{ + TCGv dest, sv, cb, cb_msb, zero, tmp; + unsigned c = cf >> 1; + DisasCond cond; + + dest = tcg_temp_new(); + cb = tcg_temp_new(); + cb_msb = tcg_temp_new(); + + zero = tcg_const_tl(0); + if (is_b) { + /* DEST,C = IN1 + ~IN2 + C. */ + tcg_gen_not_tl(cb, in2); + tcg_gen_add2_tl(dest, cb_msb, in1, zero, cpu_psw_cb_msb, zero); + tcg_gen_add2_tl(dest, cb_msb, dest, cb_msb, cb, zero); + tcg_gen_xor_tl(cb, cb, in1); + tcg_gen_xor_tl(cb, cb, dest); + } else { + /* DEST,C = IN1 + ~IN2 + 1. We can produce the same result in fewer + operations by seeding the high word with 1 and subtracting. */ + tcg_gen_movi_tl(cb_msb, 1); + tcg_gen_sub2_tl(dest, cb_msb, in1, cb_msb, in2, zero); + tcg_gen_eqv_tl(cb, in1, in2); + tcg_gen_xor_tl(cb, cb, dest); + } + tcg_temp_free(zero); + + /* Compute signed overflow if required. */ + TCGV_UNUSED(sv); + if (is_tsv || c == 6) { + sv = do_sub_sv(ctx, dest, in1, in2); + if (is_tsv) { + gen_helper_tsv(cpu_env, sv); + } + } + + /* Compute the condition. We cannot use the special case for borrow. */ + if (!is_b) { + cond = do_sub_cond(cf, dest, in1, in2, sv); + } else { + cond = do_cond(cf, dest, cb_msb, sv); + } + + /* Emit any conditional trap before any writeback. */ + if (is_tc) { + cond_prep(&cond); + tmp = tcg_temp_new(); + tcg_gen_setcond_tl(cond.c, tmp, cond.a0, cond.a1); + gen_helper_tcond(cpu_env, tmp); + tcg_temp_free(tmp); + } + + /* Write back the result. */ + save_or_nullify(ctx, cpu_psw_cb, cb); + save_or_nullify(ctx, cpu_psw_cb_msb, cb_msb); + save_gpr(ctx, rt, dest); + tcg_temp_free(dest); + + /* Install the new nullification. */ + cond_free(&ctx->null_cond); + ctx->null_cond = cond; + return NO_EXIT; +} + +static ExitStatus do_cmpclr(DisasContext *ctx, unsigned rt, TCGv in1, + TCGv in2, unsigned cf) +{ + TCGv dest, sv; + DisasCond cond; + + dest = tcg_temp_new(); + tcg_gen_sub_tl(dest, in1, in2); + + /* Compute signed overflow if required. */ + TCGV_UNUSED(sv); + if ((cf >> 1) == 6) { + sv = do_sub_sv(ctx, dest, in1, in2); + } + + /* Form the condition for the compare. */ + cond = do_sub_cond(cf, dest, in1, in2, sv); + + /* Clear. */ + tcg_gen_movi_tl(dest, 0); + save_gpr(ctx, rt, dest); + tcg_temp_free(dest); + + /* Install the new nullification. */ + cond_free(&ctx->null_cond); + ctx->null_cond = cond; + return NO_EXIT; +} + +static ExitStatus do_log(DisasContext *ctx, unsigned rt, TCGv in1, TCGv in2, + unsigned cf, void (*fn)(TCGv, TCGv, TCGv)) +{ + TCGv dest = dest_gpr(ctx, rt); + + /* Perform the operation, and writeback. */ + fn(dest, in1, in2); + save_gpr(ctx, rt, dest); + + /* Install the new nullification. */ + cond_free(&ctx->null_cond); + if (cf) { + ctx->null_cond = do_log_cond(cf, dest); + } + return NO_EXIT; +} + +static ExitStatus do_unit(DisasContext *ctx, unsigned rt, TCGv in1, + TCGv in2, unsigned cf, bool is_tc, + void (*fn)(TCGv, TCGv, TCGv)) +{ + TCGv dest; + DisasCond cond; + + if (cf == 0) { + dest = dest_gpr(ctx, rt); + fn(dest, in1, in2); + save_gpr(ctx, rt, dest); + cond_free(&ctx->null_cond); + } else { + dest = tcg_temp_new(); + fn(dest, in1, in2); + + cond = do_unit_cond(cf, dest, in1, in2); + + if (is_tc) { + TCGv tmp = tcg_temp_new(); + cond_prep(&cond); + tcg_gen_setcond_tl(cond.c, tmp, cond.a0, cond.a1); + gen_helper_tcond(cpu_env, tmp); + tcg_temp_free(tmp); + } + save_gpr(ctx, rt, dest); + + cond_free(&ctx->null_cond); + ctx->null_cond = cond; + } + return NO_EXIT; +} + +/* Emit a memory load. The modify parameter should be + * < 0 for pre-modify, + * > 0 for post-modify, + * = 0 for no base register update. + */ +static void do_load_32(DisasContext *ctx, TCGv_i32 dest, unsigned rb, + unsigned rx, int scale, target_long disp, + int modify, TCGMemOp mop) +{ + TCGv addr, base; + + /* Caller uses nullify_over/nullify_end. */ + assert(ctx->null_cond.c == TCG_COND_NEVER); + + addr = tcg_temp_new(); + base = load_gpr(ctx, rb); + + /* Note that RX is mutually exclusive with DISP. */ + if (rx) { + tcg_gen_shli_tl(addr, cpu_gr[rx], scale); + tcg_gen_add_tl(addr, addr, base); + } else { + tcg_gen_addi_tl(addr, base, disp); + } + + if (modify == 0) { + tcg_gen_qemu_ld_i32(dest, addr, MMU_USER_IDX, mop); + } else { + tcg_gen_qemu_ld_i32(dest, (modify < 0 ? addr : base), + MMU_USER_IDX, mop); + save_gpr(ctx, rb, addr); + } + tcg_temp_free(addr); +} + +static void do_load_64(DisasContext *ctx, TCGv_i64 dest, unsigned rb, + unsigned rx, int scale, target_long disp, + int modify, TCGMemOp mop) +{ + TCGv addr, base; + + /* Caller uses nullify_over/nullify_end. */ + assert(ctx->null_cond.c == TCG_COND_NEVER); + + addr = tcg_temp_new(); + base = load_gpr(ctx, rb); + + /* Note that RX is mutually exclusive with DISP. */ + if (rx) { + tcg_gen_shli_tl(addr, cpu_gr[rx], scale); + tcg_gen_add_tl(addr, addr, base); + } else { + tcg_gen_addi_tl(addr, base, disp); + } + + if (modify == 0) { + tcg_gen_qemu_ld_i64(dest, addr, MMU_USER_IDX, mop); + } else { + tcg_gen_qemu_ld_i64(dest, (modify < 0 ? addr : base), + MMU_USER_IDX, mop); + save_gpr(ctx, rb, addr); + } + tcg_temp_free(addr); +} + +static void do_store_32(DisasContext *ctx, TCGv_i32 src, unsigned rb, + unsigned rx, int scale, target_long disp, + int modify, TCGMemOp mop) +{ + TCGv addr, base; + + /* Caller uses nullify_over/nullify_end. */ + assert(ctx->null_cond.c == TCG_COND_NEVER); + + addr = tcg_temp_new(); + base = load_gpr(ctx, rb); + + /* Note that RX is mutually exclusive with DISP. */ + if (rx) { + tcg_gen_shli_tl(addr, cpu_gr[rx], scale); + tcg_gen_add_tl(addr, addr, base); + } else { + tcg_gen_addi_tl(addr, base, disp); + } + + tcg_gen_qemu_st_i32(src, (modify <= 0 ? addr : base), MMU_USER_IDX, mop); + + if (modify != 0) { + save_gpr(ctx, rb, addr); + } + tcg_temp_free(addr); +} + +static void do_store_64(DisasContext *ctx, TCGv_i64 src, unsigned rb, + unsigned rx, int scale, target_long disp, + int modify, TCGMemOp mop) +{ + TCGv addr, base; + + /* Caller uses nullify_over/nullify_end. */ + assert(ctx->null_cond.c == TCG_COND_NEVER); + + addr = tcg_temp_new(); + base = load_gpr(ctx, rb); + + /* Note that RX is mutually exclusive with DISP. */ + if (rx) { + tcg_gen_shli_tl(addr, cpu_gr[rx], scale); + tcg_gen_add_tl(addr, addr, base); + } else { + tcg_gen_addi_tl(addr, base, disp); + } + + tcg_gen_qemu_st_i64(src, (modify <= 0 ? addr : base), MMU_USER_IDX, mop); + + if (modify != 0) { + save_gpr(ctx, rb, addr); + } + tcg_temp_free(addr); +} + +#if TARGET_LONG_BITS == 64 +#define do_load_tl do_load_64 +#define do_store_tl do_store_64 +#else +#define do_load_tl do_load_32 +#define do_store_tl do_store_32 +#endif + +static ExitStatus do_load(DisasContext *ctx, unsigned rt, unsigned rb, + unsigned rx, int scale, target_long disp, + int modify, TCGMemOp mop) +{ + TCGv dest; + + nullify_over(ctx); + + if (modify == 0) { + /* No base register update. */ + dest = dest_gpr(ctx, rt); + } else { + /* Make sure if RT == RB, we see the result of the load. */ + dest = get_temp(ctx); + } + do_load_tl(ctx, dest, rb, rx, scale, disp, modify, mop); + save_gpr(ctx, rt, dest); + + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus do_floadw(DisasContext *ctx, unsigned rt, unsigned rb, + unsigned rx, int scale, target_long disp, + int modify) +{ + TCGv_i32 tmp; + + nullify_over(ctx); + + tmp = tcg_temp_new_i32(); + do_load_32(ctx, tmp, rb, rx, scale, disp, modify, MO_TEUL); + save_frw_i32(rt, tmp); + tcg_temp_free_i32(tmp); + + if (rt == 0) { + gen_helper_loaded_fr0(cpu_env); + } + + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus do_floadd(DisasContext *ctx, unsigned rt, unsigned rb, + unsigned rx, int scale, target_long disp, + int modify) +{ + TCGv_i64 tmp; + + nullify_over(ctx); + + tmp = tcg_temp_new_i64(); + do_load_64(ctx, tmp, rb, rx, scale, disp, modify, MO_TEQ); + save_frd(rt, tmp); + tcg_temp_free_i64(tmp); + + if (rt == 0) { + gen_helper_loaded_fr0(cpu_env); + } + + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus do_store(DisasContext *ctx, unsigned rt, unsigned rb, + target_long disp, int modify, TCGMemOp mop) +{ + nullify_over(ctx); + do_store_tl(ctx, load_gpr(ctx, rt), rb, 0, 0, disp, modify, mop); + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus do_fstorew(DisasContext *ctx, unsigned rt, unsigned rb, + unsigned rx, int scale, target_long disp, + int modify) +{ + TCGv_i32 tmp; + + nullify_over(ctx); + + tmp = load_frw_i32(rt); + do_store_32(ctx, tmp, rb, rx, scale, disp, modify, MO_TEUL); + tcg_temp_free_i32(tmp); + + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus do_fstored(DisasContext *ctx, unsigned rt, unsigned rb, + unsigned rx, int scale, target_long disp, + int modify) +{ + TCGv_i64 tmp; + + nullify_over(ctx); + + tmp = load_frd(rt); + do_store_64(ctx, tmp, rb, rx, scale, disp, modify, MO_TEQ); + tcg_temp_free_i64(tmp); + + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus do_fop_wew(DisasContext *ctx, unsigned rt, unsigned ra, + void (*func)(TCGv_i32, TCGv_env, TCGv_i32)) +{ + TCGv_i32 tmp; + + nullify_over(ctx); + tmp = load_frw0_i32(ra); + + func(tmp, cpu_env, tmp); + + save_frw_i32(rt, tmp); + tcg_temp_free_i32(tmp); + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus do_fop_wed(DisasContext *ctx, unsigned rt, unsigned ra, + void (*func)(TCGv_i32, TCGv_env, TCGv_i64)) +{ + TCGv_i32 dst; + TCGv_i64 src; + + nullify_over(ctx); + src = load_frd(ra); + dst = tcg_temp_new_i32(); + + func(dst, cpu_env, src); + + tcg_temp_free_i64(src); + save_frw_i32(rt, dst); + tcg_temp_free_i32(dst); + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus do_fop_ded(DisasContext *ctx, unsigned rt, unsigned ra, + void (*func)(TCGv_i64, TCGv_env, TCGv_i64)) +{ + TCGv_i64 tmp; + + nullify_over(ctx); + tmp = load_frd0(ra); + + func(tmp, cpu_env, tmp); + + save_frd(rt, tmp); + tcg_temp_free_i64(tmp); + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus do_fop_dew(DisasContext *ctx, unsigned rt, unsigned ra, + void (*func)(TCGv_i64, TCGv_env, TCGv_i32)) +{ + TCGv_i32 src; + TCGv_i64 dst; + + nullify_over(ctx); + src = load_frw0_i32(ra); + dst = tcg_temp_new_i64(); + + func(dst, cpu_env, src); + + tcg_temp_free_i32(src); + save_frd(rt, dst); + tcg_temp_free_i64(dst); + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus do_fop_weww(DisasContext *ctx, unsigned rt, + unsigned ra, unsigned rb, + void (*func)(TCGv_i32, TCGv_env, + TCGv_i32, TCGv_i32)) +{ + TCGv_i32 a, b; + + nullify_over(ctx); + a = load_frw0_i32(ra); + b = load_frw0_i32(rb); + + func(a, cpu_env, a, b); + + tcg_temp_free_i32(b); + save_frw_i32(rt, a); + tcg_temp_free_i32(a); + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus do_fop_dedd(DisasContext *ctx, unsigned rt, + unsigned ra, unsigned rb, + void (*func)(TCGv_i64, TCGv_env, + TCGv_i64, TCGv_i64)) +{ + TCGv_i64 a, b; + + nullify_over(ctx); + a = load_frd0(ra); + b = load_frd0(rb); + + func(a, cpu_env, a, b); + + tcg_temp_free_i64(b); + save_frd(rt, a); + tcg_temp_free_i64(a); + return nullify_end(ctx, NO_EXIT); +} + +/* Emit an unconditional branch to a direct target, which may or may not + have already had nullification handled. */ +static ExitStatus do_dbranch(DisasContext *ctx, target_ulong dest, + unsigned link, bool is_n) +{ + if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { + if (link != 0) { + copy_iaoq_entry(cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); + } + ctx->iaoq_n = dest; + if (is_n) { + ctx->null_cond.c = TCG_COND_ALWAYS; + } + return NO_EXIT; + } else { + nullify_over(ctx); + + if (link != 0) { + copy_iaoq_entry(cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); + } + + if (is_n && use_nullify_skip(ctx)) { + nullify_set(ctx, 0); + gen_goto_tb(ctx, 0, dest, dest + 4); + } else { + nullify_set(ctx, is_n); + gen_goto_tb(ctx, 0, ctx->iaoq_b, dest); + } + + nullify_end(ctx, NO_EXIT); + + nullify_set(ctx, 0); + gen_goto_tb(ctx, 1, ctx->iaoq_b, ctx->iaoq_n); + return EXIT_GOTO_TB; + } +} + +/* Emit a conditional branch to a direct target. If the branch itself + is nullified, we should have already used nullify_over. */ +static ExitStatus do_cbranch(DisasContext *ctx, target_long disp, bool is_n, + DisasCond *cond) +{ + target_ulong dest = iaoq_dest(ctx, disp); + TCGLabel *taken = NULL; + TCGCond c = cond->c; + int which = 0; + bool n; + + assert(ctx->null_cond.c == TCG_COND_NEVER); + + /* Handle TRUE and NEVER as direct branches. */ + if (c == TCG_COND_ALWAYS) { + return do_dbranch(ctx, dest, 0, is_n && disp >= 0); + } + if (c == TCG_COND_NEVER) { + return do_dbranch(ctx, ctx->iaoq_n, 0, is_n && disp < 0); + } + + taken = gen_new_label(); + cond_prep(cond); + tcg_gen_brcond_tl(c, cond->a0, cond->a1, taken); + cond_free(cond); + + /* Not taken: Condition not satisfied; nullify on backward branches. */ + n = is_n && disp < 0; + if (n && use_nullify_skip(ctx)) { + nullify_set(ctx, 0); + gen_goto_tb(ctx, which++, ctx->iaoq_n, ctx->iaoq_n + 4); + } else { + if (!n && ctx->null_lab) { + gen_set_label(ctx->null_lab); + ctx->null_lab = NULL; + } + nullify_set(ctx, n); + gen_goto_tb(ctx, which++, ctx->iaoq_b, ctx->iaoq_n); + } + + gen_set_label(taken); + + /* Taken: Condition satisfied; nullify on forward branches. */ + n = is_n && disp >= 0; + if (n && use_nullify_skip(ctx)) { + nullify_set(ctx, 0); + gen_goto_tb(ctx, which++, dest, dest + 4); + } else { + nullify_set(ctx, n); + gen_goto_tb(ctx, which++, ctx->iaoq_b, dest); + } + + /* Not taken: the branch itself was nullified. */ + if (ctx->null_lab) { + gen_set_label(ctx->null_lab); + ctx->null_lab = NULL; + if (which < 2) { + nullify_set(ctx, 0); + gen_goto_tb(ctx, which, ctx->iaoq_b, ctx->iaoq_n); + return EXIT_GOTO_TB; + } else { + return EXIT_IAQ_N_STALE; + } + } else { + return EXIT_GOTO_TB; + } +} + +/* Emit an unconditional branch to an indirect target. This handles + nullification of the branch itself. */ +static ExitStatus do_ibranch(DisasContext *ctx, TCGv dest, + unsigned link, bool is_n) +{ + TCGv a0, a1, next, tmp; + TCGCond c; + + assert(ctx->null_lab == NULL); + + if (ctx->null_cond.c == TCG_COND_NEVER) { + if (link != 0) { + copy_iaoq_entry(cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); + } + next = get_temp(ctx); + tcg_gen_mov_tl(next, dest); + ctx->iaoq_n = -1; + ctx->iaoq_n_var = next; + if (is_n) { + ctx->null_cond.c = TCG_COND_ALWAYS; + } + } else if (is_n && use_nullify_skip(ctx)) { + /* The (conditional) branch, B, nullifies the next insn, N, + and we're allowed to skip execution N (no single-step or + tracepoint in effect). Since the exit_tb that we must use + for the indirect branch consumes no special resources, we + can (conditionally) skip B and continue execution. */ + /* The use_nullify_skip test implies we have a known control path. */ + tcg_debug_assert(ctx->iaoq_b != -1); + tcg_debug_assert(ctx->iaoq_n != -1); + + /* We do have to handle the non-local temporary, DEST, before + branching. Since IOAQ_F is not really live at this point, we + can simply store DEST optimistically. Similarly with IAOQ_B. */ + tcg_gen_mov_tl(cpu_iaoq_f, dest); + tcg_gen_addi_tl(cpu_iaoq_b, dest, 4); + + nullify_over(ctx); + if (link != 0) { + tcg_gen_movi_tl(cpu_gr[link], ctx->iaoq_n); + } + tcg_gen_exit_tb(0); + return nullify_end(ctx, NO_EXIT); + } else { + cond_prep(&ctx->null_cond); + c = ctx->null_cond.c; + a0 = ctx->null_cond.a0; + a1 = ctx->null_cond.a1; + + tmp = tcg_temp_new(); + next = get_temp(ctx); + + copy_iaoq_entry(tmp, ctx->iaoq_n, ctx->iaoq_n_var); + tcg_gen_movcond_tl(c, next, a0, a1, tmp, dest); + ctx->iaoq_n = -1; + ctx->iaoq_n_var = next; + + if (link != 0) { + tcg_gen_movcond_tl(c, cpu_gr[link], a0, a1, cpu_gr[link], tmp); + } + + if (is_n) { + /* The branch nullifies the next insn, which means the state of N + after the branch is the inverse of the state of N that applied + to the branch. */ + tcg_gen_setcond_tl(tcg_invert_cond(c), cpu_psw_n, a0, a1); + cond_free(&ctx->null_cond); + ctx->null_cond = cond_make_n(); + ctx->psw_n_nonzero = true; + } else { + cond_free(&ctx->null_cond); + } + } + + return NO_EXIT; +} + +/* On Linux, page zero is normally marked execute only + gateway. + Therefore normal read or write is supposed to fail, but specific + offsets have kernel code mapped to raise permissions to implement + system calls. Handling this via an explicit check here, rather + in than the "be disp(sr2,r0)" instruction that probably sent us + here, is the easiest way to handle the branch delay slot on the + aforementioned BE. */ +static ExitStatus do_page_zero(DisasContext *ctx) +{ + /* If by some means we get here with PSW[N]=1, that implies that + the B,GATE instruction would be skipped, and we'd fault on the + next insn within the privilaged page. */ + switch (ctx->null_cond.c) { + case TCG_COND_NEVER: + break; + case TCG_COND_ALWAYS: + tcg_gen_movi_tl(cpu_psw_n, 0); + goto do_sigill; + default: + /* Since this is always the first (and only) insn within the + TB, we should know the state of PSW[N] from TB->FLAGS. */ + g_assert_not_reached(); + } + + /* Check that we didn't arrive here via some means that allowed + non-sequential instruction execution. Normally the PSW[B] bit + detects this by disallowing the B,GATE instruction to execute + under such conditions. */ + if (ctx->iaoq_b != ctx->iaoq_f + 4) { + goto do_sigill; + } + + switch (ctx->iaoq_f) { + case 0x00: /* Null pointer call */ + gen_excp_1(EXCP_SIGSEGV); + return EXIT_NORETURN; + + case 0xb0: /* LWS */ + gen_excp_1(EXCP_SYSCALL_LWS); + return EXIT_NORETURN; + + case 0xe0: /* SET_THREAD_POINTER */ + tcg_gen_mov_tl(cpu_cr27, cpu_gr[26]); + tcg_gen_mov_tl(cpu_iaoq_f, cpu_gr[31]); + tcg_gen_addi_tl(cpu_iaoq_b, cpu_iaoq_f, 4); + return EXIT_IAQ_N_UPDATED; + + case 0x100: /* SYSCALL */ + gen_excp_1(EXCP_SYSCALL); + return EXIT_NORETURN; + + default: + do_sigill: + gen_excp_1(EXCP_SIGILL); + return EXIT_NORETURN; + } +} + +static ExitStatus trans_nop(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + cond_free(&ctx->null_cond); + return NO_EXIT; +} + +static ExitStatus trans_break(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + nullify_over(ctx); + return nullify_end(ctx, gen_excp(ctx, EXCP_DEBUG)); +} + +static ExitStatus trans_sync(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + /* No point in nullifying the memory barrier. */ + tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); + + cond_free(&ctx->null_cond); + return NO_EXIT; +} + +static ExitStatus trans_mfia(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + TCGv tmp = dest_gpr(ctx, rt); + tcg_gen_movi_tl(tmp, ctx->iaoq_f); + save_gpr(ctx, rt, tmp); + + cond_free(&ctx->null_cond); + return NO_EXIT; +} + +static ExitStatus trans_mfsp(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + TCGv tmp = dest_gpr(ctx, rt); + + /* ??? We don't implement space registers. */ + tcg_gen_movi_tl(tmp, 0); + save_gpr(ctx, rt, tmp); + + cond_free(&ctx->null_cond); + return NO_EXIT; +} + +static ExitStatus trans_mfctl(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + unsigned ctl = extract32(insn, 21, 5); + TCGv tmp; + + switch (ctl) { + case 11: /* SAR */ +#ifdef TARGET_HPPA64 + if (extract32(insn, 14, 1) == 0) { + /* MFSAR without ,W masks low 5 bits. */ + tmp = dest_gpr(ctx, rt); + tcg_gen_andi_tl(tmp, cpu_sar, 31); + save_gpr(ctx, rt, tmp); + break; + } +#endif + save_gpr(ctx, rt, cpu_sar); + break; + case 16: /* Interval Timer */ + tmp = dest_gpr(ctx, rt); + tcg_gen_movi_tl(tmp, 0); /* FIXME */ + save_gpr(ctx, rt, tmp); + break; + case 26: + save_gpr(ctx, rt, cpu_cr26); + break; + case 27: + save_gpr(ctx, rt, cpu_cr27); + break; + default: + /* All other control registers are privileged. */ + return gen_illegal(ctx); + } + + cond_free(&ctx->null_cond); + return NO_EXIT; +} + +static ExitStatus trans_mtctl(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rin = extract32(insn, 16, 5); + unsigned ctl = extract32(insn, 21, 5); + TCGv tmp; + + if (ctl == 11) { /* SAR */ + tmp = tcg_temp_new(); + tcg_gen_andi_tl(tmp, load_gpr(ctx, rin), TARGET_LONG_BITS - 1); + save_or_nullify(ctx, cpu_sar, tmp); + tcg_temp_free(tmp); + } else { + /* All other control registers are privileged or read-only. */ + return gen_illegal(ctx); + } + + cond_free(&ctx->null_cond); + return NO_EXIT; +} + +static ExitStatus trans_mtsarcm(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rin = extract32(insn, 16, 5); + TCGv tmp = tcg_temp_new(); + + tcg_gen_not_tl(tmp, load_gpr(ctx, rin)); + tcg_gen_andi_tl(tmp, tmp, TARGET_LONG_BITS - 1); + save_or_nullify(ctx, cpu_sar, tmp); + tcg_temp_free(tmp); + + cond_free(&ctx->null_cond); + return NO_EXIT; +} + +static ExitStatus trans_ldsid(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + TCGv dest = dest_gpr(ctx, rt); + + /* Since we don't implement space registers, this returns zero. */ + tcg_gen_movi_tl(dest, 0); + save_gpr(ctx, rt, dest); + + cond_free(&ctx->null_cond); + return NO_EXIT; +} + +static const DisasInsn table_system[] = { + { 0x00000000u, 0xfc001fe0u, trans_break }, + /* We don't implement space register, so MTSP is a nop. */ + { 0x00001820u, 0xffe01fffu, trans_nop }, + { 0x00001840u, 0xfc00ffffu, trans_mtctl }, + { 0x016018c0u, 0xffe0ffffu, trans_mtsarcm }, + { 0x000014a0u, 0xffffffe0u, trans_mfia }, + { 0x000004a0u, 0xffff1fe0u, trans_mfsp }, + { 0x000008a0u, 0xfc1fffe0u, trans_mfctl }, + { 0x00000400u, 0xffffffffu, trans_sync }, + { 0x000010a0u, 0xfc1f3fe0u, trans_ldsid }, +}; + +static ExitStatus trans_base_idx_mod(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rb = extract32(insn, 21, 5); + unsigned rx = extract32(insn, 16, 5); + TCGv dest = dest_gpr(ctx, rb); + TCGv src1 = load_gpr(ctx, rb); + TCGv src2 = load_gpr(ctx, rx); + + /* The only thing we need to do is the base register modification. */ + tcg_gen_add_tl(dest, src1, src2); + save_gpr(ctx, rb, dest); + + cond_free(&ctx->null_cond); + return NO_EXIT; +} + +static ExitStatus trans_probe(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + unsigned rb = extract32(insn, 21, 5); + unsigned is_write = extract32(insn, 6, 1); + TCGv dest; + + nullify_over(ctx); + + /* ??? Do something with priv level operand. */ + dest = dest_gpr(ctx, rt); + if (is_write) { + gen_helper_probe_w(dest, load_gpr(ctx, rb)); + } else { + gen_helper_probe_r(dest, load_gpr(ctx, rb)); + } + save_gpr(ctx, rt, dest); + return nullify_end(ctx, NO_EXIT); +} + +static const DisasInsn table_mem_mgmt[] = { + { 0x04003280u, 0xfc003fffu, trans_nop }, /* fdc, disp */ + { 0x04001280u, 0xfc003fffu, trans_nop }, /* fdc, index */ + { 0x040012a0u, 0xfc003fffu, trans_base_idx_mod }, /* fdc, index, base mod */ + { 0x040012c0u, 0xfc003fffu, trans_nop }, /* fdce */ + { 0x040012e0u, 0xfc003fffu, trans_base_idx_mod }, /* fdce, base mod */ + { 0x04000280u, 0xfc001fffu, trans_nop }, /* fic 0a */ + { 0x040002a0u, 0xfc001fffu, trans_base_idx_mod }, /* fic 0a, base mod */ + { 0x040013c0u, 0xfc003fffu, trans_nop }, /* fic 4f */ + { 0x040013e0u, 0xfc003fffu, trans_base_idx_mod }, /* fic 4f, base mod */ + { 0x040002c0u, 0xfc001fffu, trans_nop }, /* fice */ + { 0x040002e0u, 0xfc001fffu, trans_base_idx_mod }, /* fice, base mod */ + { 0x04002700u, 0xfc003fffu, trans_nop }, /* pdc */ + { 0x04002720u, 0xfc003fffu, trans_base_idx_mod }, /* pdc, base mod */ + { 0x04001180u, 0xfc003fa0u, trans_probe }, /* probe */ + { 0x04003180u, 0xfc003fa0u, trans_probe }, /* probei */ +}; + +static ExitStatus trans_add(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned r2 = extract32(insn, 21, 5); + unsigned r1 = extract32(insn, 16, 5); + unsigned cf = extract32(insn, 12, 4); + unsigned ext = extract32(insn, 8, 4); + unsigned shift = extract32(insn, 6, 2); + unsigned rt = extract32(insn, 0, 5); + TCGv tcg_r1, tcg_r2; + bool is_c = false; + bool is_l = false; + bool is_tc = false; + bool is_tsv = false; + ExitStatus ret; + + switch (ext) { + case 0x6: /* ADD, SHLADD */ + break; + case 0xa: /* ADD,L, SHLADD,L */ + is_l = true; + break; + case 0xe: /* ADD,TSV, SHLADD,TSV (1) */ + is_tsv = true; + break; + case 0x7: /* ADD,C */ + is_c = true; + break; + case 0xf: /* ADD,C,TSV */ + is_c = is_tsv = true; + break; + default: + return gen_illegal(ctx); + } + + if (cf) { + nullify_over(ctx); + } + tcg_r1 = load_gpr(ctx, r1); + tcg_r2 = load_gpr(ctx, r2); + ret = do_add(ctx, rt, tcg_r1, tcg_r2, shift, is_l, is_tsv, is_tc, is_c, cf); + return nullify_end(ctx, ret); +} + +static ExitStatus trans_sub(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned r2 = extract32(insn, 21, 5); + unsigned r1 = extract32(insn, 16, 5); + unsigned cf = extract32(insn, 12, 4); + unsigned ext = extract32(insn, 6, 6); + unsigned rt = extract32(insn, 0, 5); + TCGv tcg_r1, tcg_r2; + bool is_b = false; + bool is_tc = false; + bool is_tsv = false; + ExitStatus ret; + + switch (ext) { + case 0x10: /* SUB */ + break; + case 0x30: /* SUB,TSV */ + is_tsv = true; + break; + case 0x14: /* SUB,B */ + is_b = true; + break; + case 0x34: /* SUB,B,TSV */ + is_b = is_tsv = true; + break; + case 0x13: /* SUB,TC */ + is_tc = true; + break; + case 0x33: /* SUB,TSV,TC */ + is_tc = is_tsv = true; + break; + default: + return gen_illegal(ctx); + } + + if (cf) { + nullify_over(ctx); + } + tcg_r1 = load_gpr(ctx, r1); + tcg_r2 = load_gpr(ctx, r2); + ret = do_sub(ctx, rt, tcg_r1, tcg_r2, is_tsv, is_b, is_tc, cf); + return nullify_end(ctx, ret); +} + +static ExitStatus trans_log(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned r2 = extract32(insn, 21, 5); + unsigned r1 = extract32(insn, 16, 5); + unsigned cf = extract32(insn, 12, 4); + unsigned rt = extract32(insn, 0, 5); + TCGv tcg_r1, tcg_r2; + ExitStatus ret; + + if (cf) { + nullify_over(ctx); + } + tcg_r1 = load_gpr(ctx, r1); + tcg_r2 = load_gpr(ctx, r2); + ret = do_log(ctx, rt, tcg_r1, tcg_r2, cf, di->f_ttt); + return nullify_end(ctx, ret); +} + +/* OR r,0,t -> COPY (according to gas) */ +static ExitStatus trans_copy(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned r1 = extract32(insn, 16, 5); + unsigned rt = extract32(insn, 0, 5); + + if (r1 == 0) { + TCGv dest = dest_gpr(ctx, rt); + tcg_gen_movi_tl(dest, 0); + save_gpr(ctx, rt, dest); + } else { + save_gpr(ctx, rt, cpu_gr[r1]); + } + cond_free(&ctx->null_cond); + return NO_EXIT; +} + +static ExitStatus trans_cmpclr(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned r2 = extract32(insn, 21, 5); + unsigned r1 = extract32(insn, 16, 5); + unsigned cf = extract32(insn, 12, 4); + unsigned rt = extract32(insn, 0, 5); + TCGv tcg_r1, tcg_r2; + ExitStatus ret; + + if (cf) { + nullify_over(ctx); + } + tcg_r1 = load_gpr(ctx, r1); + tcg_r2 = load_gpr(ctx, r2); + ret = do_cmpclr(ctx, rt, tcg_r1, tcg_r2, cf); + return nullify_end(ctx, ret); +} + +static ExitStatus trans_uxor(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned r2 = extract32(insn, 21, 5); + unsigned r1 = extract32(insn, 16, 5); + unsigned cf = extract32(insn, 12, 4); + unsigned rt = extract32(insn, 0, 5); + TCGv tcg_r1, tcg_r2; + ExitStatus ret; + + if (cf) { + nullify_over(ctx); + } + tcg_r1 = load_gpr(ctx, r1); + tcg_r2 = load_gpr(ctx, r2); + ret = do_unit(ctx, rt, tcg_r1, tcg_r2, cf, false, tcg_gen_xor_tl); + return nullify_end(ctx, ret); +} + +static ExitStatus trans_uaddcm(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned r2 = extract32(insn, 21, 5); + unsigned r1 = extract32(insn, 16, 5); + unsigned cf = extract32(insn, 12, 4); + unsigned is_tc = extract32(insn, 6, 1); + unsigned rt = extract32(insn, 0, 5); + TCGv tcg_r1, tcg_r2, tmp; + ExitStatus ret; + + if (cf) { + nullify_over(ctx); + } + tcg_r1 = load_gpr(ctx, r1); + tcg_r2 = load_gpr(ctx, r2); + tmp = get_temp(ctx); + tcg_gen_not_tl(tmp, tcg_r2); + ret = do_unit(ctx, rt, tcg_r1, tmp, cf, is_tc, tcg_gen_add_tl); + return nullify_end(ctx, ret); +} + +static ExitStatus trans_dcor(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned r2 = extract32(insn, 21, 5); + unsigned cf = extract32(insn, 12, 4); + unsigned is_i = extract32(insn, 6, 1); + unsigned rt = extract32(insn, 0, 5); + TCGv tmp; + ExitStatus ret; + + nullify_over(ctx); + + tmp = get_temp(ctx); + tcg_gen_shri_tl(tmp, cpu_psw_cb, 3); + if (!is_i) { + tcg_gen_not_tl(tmp, tmp); + } + tcg_gen_andi_tl(tmp, tmp, 0x11111111); + tcg_gen_muli_tl(tmp, tmp, 6); + ret = do_unit(ctx, rt, tmp, load_gpr(ctx, r2), cf, false, + is_i ? tcg_gen_add_tl : tcg_gen_sub_tl); + + return nullify_end(ctx, ret); +} + +static ExitStatus trans_ds(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned r2 = extract32(insn, 21, 5); + unsigned r1 = extract32(insn, 16, 5); + unsigned cf = extract32(insn, 12, 4); + unsigned rt = extract32(insn, 0, 5); + TCGv dest, add1, add2, addc, zero, in1, in2; + + nullify_over(ctx); + + in1 = load_gpr(ctx, r1); + in2 = load_gpr(ctx, r2); + + add1 = tcg_temp_new(); + add2 = tcg_temp_new(); + addc = tcg_temp_new(); + dest = tcg_temp_new(); + zero = tcg_const_tl(0); + + /* Form R1 << 1 | PSW[CB]{8}. */ + tcg_gen_add_tl(add1, in1, in1); + tcg_gen_add_tl(add1, add1, cpu_psw_cb_msb); + + /* Add or subtract R2, depending on PSW[V]. Proper computation of + carry{8} requires that we subtract via + ~R2 + 1, as described in + the manual. By extracting and masking V, we can produce the + proper inputs to the addition without movcond. */ + tcg_gen_sari_tl(addc, cpu_psw_v, TARGET_LONG_BITS - 1); + tcg_gen_xor_tl(add2, in2, addc); + tcg_gen_andi_tl(addc, addc, 1); + /* ??? This is only correct for 32-bit. */ + tcg_gen_add2_i32(dest, cpu_psw_cb_msb, add1, zero, add2, zero); + tcg_gen_add2_i32(dest, cpu_psw_cb_msb, dest, cpu_psw_cb_msb, addc, zero); + + tcg_temp_free(addc); + tcg_temp_free(zero); + + /* Write back the result register. */ + save_gpr(ctx, rt, dest); + + /* Write back PSW[CB]. */ + tcg_gen_xor_tl(cpu_psw_cb, add1, add2); + tcg_gen_xor_tl(cpu_psw_cb, cpu_psw_cb, dest); + + /* Write back PSW[V] for the division step. */ + tcg_gen_neg_tl(cpu_psw_v, cpu_psw_cb_msb); + tcg_gen_xor_tl(cpu_psw_v, cpu_psw_v, in2); + + /* Install the new nullification. */ + if (cf) { + TCGv sv; + TCGV_UNUSED(sv); + if (cf >> 1 == 6) { + /* ??? The lshift is supposed to contribute to overflow. */ + sv = do_add_sv(ctx, dest, add1, add2); + } + ctx->null_cond = do_cond(cf, dest, cpu_psw_cb_msb, sv); + } + + tcg_temp_free(add1); + tcg_temp_free(add2); + tcg_temp_free(dest); + + return nullify_end(ctx, NO_EXIT); +} + +static const DisasInsn table_arith_log[] = { + { 0x08000240u, 0xfc00ffffu, trans_nop }, /* or x,y,0 */ + { 0x08000240u, 0xffe0ffe0u, trans_copy }, /* or x,0,t */ + { 0x08000000u, 0xfc000fe0u, trans_log, .f_ttt = tcg_gen_andc_tl }, + { 0x08000200u, 0xfc000fe0u, trans_log, .f_ttt = tcg_gen_and_tl }, + { 0x08000240u, 0xfc000fe0u, trans_log, .f_ttt = tcg_gen_or_tl }, + { 0x08000280u, 0xfc000fe0u, trans_log, .f_ttt = tcg_gen_xor_tl }, + { 0x08000880u, 0xfc000fe0u, trans_cmpclr }, + { 0x08000380u, 0xfc000fe0u, trans_uxor }, + { 0x08000980u, 0xfc000fa0u, trans_uaddcm }, + { 0x08000b80u, 0xfc1f0fa0u, trans_dcor }, + { 0x08000440u, 0xfc000fe0u, trans_ds }, + { 0x08000700u, 0xfc0007e0u, trans_add }, /* add */ + { 0x08000400u, 0xfc0006e0u, trans_sub }, /* sub; sub,b; sub,tsv */ + { 0x080004c0u, 0xfc0007e0u, trans_sub }, /* sub,tc; sub,tsv,tc */ + { 0x08000200u, 0xfc000320u, trans_add }, /* shladd */ +}; + +static ExitStatus trans_addi(DisasContext *ctx, uint32_t insn) +{ + target_long im = low_sextract(insn, 0, 11); + unsigned e1 = extract32(insn, 11, 1); + unsigned cf = extract32(insn, 12, 4); + unsigned rt = extract32(insn, 16, 5); + unsigned r2 = extract32(insn, 21, 5); + unsigned o1 = extract32(insn, 26, 1); + TCGv tcg_im, tcg_r2; + ExitStatus ret; + + if (cf) { + nullify_over(ctx); + } + + tcg_im = load_const(ctx, im); + tcg_r2 = load_gpr(ctx, r2); + ret = do_add(ctx, rt, tcg_im, tcg_r2, 0, false, e1, !o1, false, cf); + + return nullify_end(ctx, ret); +} + +static ExitStatus trans_subi(DisasContext *ctx, uint32_t insn) +{ + target_long im = low_sextract(insn, 0, 11); + unsigned e1 = extract32(insn, 11, 1); + unsigned cf = extract32(insn, 12, 4); + unsigned rt = extract32(insn, 16, 5); + unsigned r2 = extract32(insn, 21, 5); + TCGv tcg_im, tcg_r2; + ExitStatus ret; + + if (cf) { + nullify_over(ctx); + } + + tcg_im = load_const(ctx, im); + tcg_r2 = load_gpr(ctx, r2); + ret = do_sub(ctx, rt, tcg_im, tcg_r2, e1, false, false, cf); + + return nullify_end(ctx, ret); +} + +static ExitStatus trans_cmpiclr(DisasContext *ctx, uint32_t insn) +{ + target_long im = low_sextract(insn, 0, 11); + unsigned cf = extract32(insn, 12, 4); + unsigned rt = extract32(insn, 16, 5); + unsigned r2 = extract32(insn, 21, 5); + TCGv tcg_im, tcg_r2; + ExitStatus ret; + + if (cf) { + nullify_over(ctx); + } + + tcg_im = load_const(ctx, im); + tcg_r2 = load_gpr(ctx, r2); + ret = do_cmpclr(ctx, rt, tcg_im, tcg_r2, cf); + + return nullify_end(ctx, ret); +} + +static ExitStatus trans_ld_idx_i(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + unsigned m = extract32(insn, 5, 1); + unsigned sz = extract32(insn, 6, 2); + unsigned a = extract32(insn, 13, 1); + int disp = low_sextract(insn, 16, 5); + unsigned rb = extract32(insn, 21, 5); + int modify = (m ? (a ? -1 : 1) : 0); + TCGMemOp mop = MO_TE | sz; + + return do_load(ctx, rt, rb, 0, 0, disp, modify, mop); +} + +static ExitStatus trans_ld_idx_x(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + unsigned m = extract32(insn, 5, 1); + unsigned sz = extract32(insn, 6, 2); + unsigned u = extract32(insn, 13, 1); + unsigned rx = extract32(insn, 16, 5); + unsigned rb = extract32(insn, 21, 5); + TCGMemOp mop = MO_TE | sz; + + return do_load(ctx, rt, rb, rx, u ? sz : 0, 0, m, mop); +} + +static ExitStatus trans_st_idx_i(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + int disp = low_sextract(insn, 0, 5); + unsigned m = extract32(insn, 5, 1); + unsigned sz = extract32(insn, 6, 2); + unsigned a = extract32(insn, 13, 1); + unsigned rr = extract32(insn, 16, 5); + unsigned rb = extract32(insn, 21, 5); + int modify = (m ? (a ? -1 : 1) : 0); + TCGMemOp mop = MO_TE | sz; + + return do_store(ctx, rr, rb, disp, modify, mop); +} + +static ExitStatus trans_ldcw(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + unsigned m = extract32(insn, 5, 1); + unsigned i = extract32(insn, 12, 1); + unsigned au = extract32(insn, 13, 1); + unsigned rx = extract32(insn, 16, 5); + unsigned rb = extract32(insn, 21, 5); + TCGMemOp mop = MO_TEUL | MO_ALIGN_16; + TCGv zero, addr, base, dest; + int modify, disp = 0, scale = 0; + + nullify_over(ctx); + + /* ??? Share more code with do_load and do_load_{32,64}. */ + + if (i) { + modify = (m ? (au ? -1 : 1) : 0); + disp = low_sextract(rx, 0, 5); + rx = 0; + } else { + modify = m; + if (au) { + scale = mop & MO_SIZE; + } + } + if (modify) { + /* Base register modification. Make sure if RT == RB, we see + the result of the load. */ + dest = get_temp(ctx); + } else { + dest = dest_gpr(ctx, rt); + } + + addr = tcg_temp_new(); + base = load_gpr(ctx, rb); + if (rx) { + tcg_gen_shli_tl(addr, cpu_gr[rx], scale); + tcg_gen_add_tl(addr, addr, base); + } else { + tcg_gen_addi_tl(addr, base, disp); + } + + zero = tcg_const_tl(0); + tcg_gen_atomic_xchg_tl(dest, (modify <= 0 ? addr : base), + zero, MMU_USER_IDX, mop); + if (modify) { + save_gpr(ctx, rb, addr); + } + save_gpr(ctx, rt, dest); + + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus trans_stby(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + target_long disp = low_sextract(insn, 0, 5); + unsigned m = extract32(insn, 5, 1); + unsigned a = extract32(insn, 13, 1); + unsigned rt = extract32(insn, 16, 5); + unsigned rb = extract32(insn, 21, 5); + TCGv addr, val; + + nullify_over(ctx); + + addr = tcg_temp_new(); + if (m || disp == 0) { + tcg_gen_mov_tl(addr, load_gpr(ctx, rb)); + } else { + tcg_gen_addi_tl(addr, load_gpr(ctx, rb), disp); + } + val = load_gpr(ctx, rt); + + if (a) { + gen_helper_stby_e(cpu_env, addr, val); + } else { + gen_helper_stby_b(cpu_env, addr, val); + } + + if (m) { + tcg_gen_addi_tl(addr, addr, disp); + tcg_gen_andi_tl(addr, addr, ~3); + save_gpr(ctx, rb, addr); + } + tcg_temp_free(addr); + + return nullify_end(ctx, NO_EXIT); +} + +static const DisasInsn table_index_mem[] = { + { 0x0c001000u, 0xfc001300, trans_ld_idx_i }, /* LD[BHWD], im */ + { 0x0c000000u, 0xfc001300, trans_ld_idx_x }, /* LD[BHWD], rx */ + { 0x0c001200u, 0xfc001300, trans_st_idx_i }, /* ST[BHWD] */ + { 0x0c0001c0u, 0xfc0003c0, trans_ldcw }, + { 0x0c001300u, 0xfc0013c0, trans_stby }, +}; + +static ExitStatus trans_ldil(DisasContext *ctx, uint32_t insn) +{ + unsigned rt = extract32(insn, 21, 5); + target_long i = assemble_21(insn); + TCGv tcg_rt = dest_gpr(ctx, rt); + + tcg_gen_movi_tl(tcg_rt, i); + save_gpr(ctx, rt, tcg_rt); + cond_free(&ctx->null_cond); + + return NO_EXIT; +} + +static ExitStatus trans_addil(DisasContext *ctx, uint32_t insn) +{ + unsigned rt = extract32(insn, 21, 5); + target_long i = assemble_21(insn); + TCGv tcg_rt = load_gpr(ctx, rt); + TCGv tcg_r1 = dest_gpr(ctx, 1); + + tcg_gen_addi_tl(tcg_r1, tcg_rt, i); + save_gpr(ctx, 1, tcg_r1); + cond_free(&ctx->null_cond); + + return NO_EXIT; +} + +static ExitStatus trans_ldo(DisasContext *ctx, uint32_t insn) +{ + unsigned rb = extract32(insn, 21, 5); + unsigned rt = extract32(insn, 16, 5); + target_long i = assemble_16(insn); + TCGv tcg_rt = dest_gpr(ctx, rt); + + /* Special case rb == 0, for the LDI pseudo-op. + The COPY pseudo-op is handled for free within tcg_gen_addi_tl. */ + if (rb == 0) { + tcg_gen_movi_tl(tcg_rt, i); + } else { + tcg_gen_addi_tl(tcg_rt, cpu_gr[rb], i); + } + save_gpr(ctx, rt, tcg_rt); + cond_free(&ctx->null_cond); + + return NO_EXIT; +} + +static ExitStatus trans_load(DisasContext *ctx, uint32_t insn, + bool is_mod, TCGMemOp mop) +{ + unsigned rb = extract32(insn, 21, 5); + unsigned rt = extract32(insn, 16, 5); + target_long i = assemble_16(insn); + + return do_load(ctx, rt, rb, 0, 0, i, is_mod ? (i < 0 ? -1 : 1) : 0, mop); +} + +static ExitStatus trans_load_w(DisasContext *ctx, uint32_t insn) +{ + unsigned rb = extract32(insn, 21, 5); + unsigned rt = extract32(insn, 16, 5); + target_long i = assemble_16a(insn); + unsigned ext2 = extract32(insn, 1, 2); + + switch (ext2) { + case 0: + case 1: + /* FLDW without modification. */ + return do_floadw(ctx, ext2 * 32 + rt, rb, 0, 0, i, 0); + case 2: + /* LDW with modification. Note that the sign of I selects + post-dec vs pre-inc. */ + return do_load(ctx, rt, rb, 0, 0, i, (i < 0 ? 1 : -1), MO_TEUL); + default: + return gen_illegal(ctx); + } +} + +static ExitStatus trans_fload_mod(DisasContext *ctx, uint32_t insn) +{ + target_long i = assemble_16a(insn); + unsigned t1 = extract32(insn, 1, 1); + unsigned a = extract32(insn, 2, 1); + unsigned t0 = extract32(insn, 16, 5); + unsigned rb = extract32(insn, 21, 5); + + /* FLDW with modification. */ + return do_floadw(ctx, t1 * 32 + t0, rb, 0, 0, i, (a ? -1 : 1)); +} + +static ExitStatus trans_store(DisasContext *ctx, uint32_t insn, + bool is_mod, TCGMemOp mop) +{ + unsigned rb = extract32(insn, 21, 5); + unsigned rt = extract32(insn, 16, 5); + target_long i = assemble_16(insn); + + return do_store(ctx, rt, rb, i, is_mod ? (i < 0 ? -1 : 1) : 0, mop); +} + +static ExitStatus trans_store_w(DisasContext *ctx, uint32_t insn) +{ + unsigned rb = extract32(insn, 21, 5); + unsigned rt = extract32(insn, 16, 5); + target_long i = assemble_16a(insn); + unsigned ext2 = extract32(insn, 1, 2); + + switch (ext2) { + case 0: + case 1: + /* FSTW without modification. */ + return do_fstorew(ctx, ext2 * 32 + rt, rb, 0, 0, i, 0); + case 2: + /* LDW with modification. */ + return do_store(ctx, rt, rb, i, (i < 0 ? 1 : -1), MO_TEUL); + default: + return gen_illegal(ctx); + } +} + +static ExitStatus trans_fstore_mod(DisasContext *ctx, uint32_t insn) +{ + target_long i = assemble_16a(insn); + unsigned t1 = extract32(insn, 1, 1); + unsigned a = extract32(insn, 2, 1); + unsigned t0 = extract32(insn, 16, 5); + unsigned rb = extract32(insn, 21, 5); + + /* FSTW with modification. */ + return do_fstorew(ctx, t1 * 32 + t0, rb, 0, 0, i, (a ? -1 : 1)); +} + +static ExitStatus trans_copr_w(DisasContext *ctx, uint32_t insn) +{ + unsigned t0 = extract32(insn, 0, 5); + unsigned m = extract32(insn, 5, 1); + unsigned t1 = extract32(insn, 6, 1); + unsigned ext3 = extract32(insn, 7, 3); + /* unsigned cc = extract32(insn, 10, 2); */ + unsigned i = extract32(insn, 12, 1); + unsigned ua = extract32(insn, 13, 1); + unsigned rx = extract32(insn, 16, 5); + unsigned rb = extract32(insn, 21, 5); + unsigned rt = t1 * 32 + t0; + int modify = (m ? (ua ? -1 : 1) : 0); + int disp, scale; + + if (i == 0) { + scale = (ua ? 2 : 0); + disp = 0; + modify = m; + } else { + disp = low_sextract(rx, 0, 5); + scale = 0; + rx = 0; + modify = (m ? (ua ? -1 : 1) : 0); + } + + switch (ext3) { + case 0: /* FLDW */ + return do_floadw(ctx, rt, rb, rx, scale, disp, modify); + case 4: /* FSTW */ + return do_fstorew(ctx, rt, rb, rx, scale, disp, modify); + } + return gen_illegal(ctx); +} + +static ExitStatus trans_copr_dw(DisasContext *ctx, uint32_t insn) +{ + unsigned rt = extract32(insn, 0, 5); + unsigned m = extract32(insn, 5, 1); + unsigned ext4 = extract32(insn, 6, 4); + /* unsigned cc = extract32(insn, 10, 2); */ + unsigned i = extract32(insn, 12, 1); + unsigned ua = extract32(insn, 13, 1); + unsigned rx = extract32(insn, 16, 5); + unsigned rb = extract32(insn, 21, 5); + int modify = (m ? (ua ? -1 : 1) : 0); + int disp, scale; + + if (i == 0) { + scale = (ua ? 3 : 0); + disp = 0; + modify = m; + } else { + disp = low_sextract(rx, 0, 5); + scale = 0; + rx = 0; + modify = (m ? (ua ? -1 : 1) : 0); + } + + switch (ext4) { + case 0: /* FLDD */ + return do_floadd(ctx, rt, rb, rx, scale, disp, modify); + case 8: /* FSTD */ + return do_fstored(ctx, rt, rb, rx, scale, disp, modify); + default: + return gen_illegal(ctx); + } +} + +static ExitStatus trans_cmpb(DisasContext *ctx, uint32_t insn, + bool is_true, bool is_imm, bool is_dw) +{ + target_long disp = assemble_12(insn) * 4; + unsigned n = extract32(insn, 1, 1); + unsigned c = extract32(insn, 13, 3); + unsigned r = extract32(insn, 21, 5); + unsigned cf = c * 2 + !is_true; + TCGv dest, in1, in2, sv; + DisasCond cond; + + nullify_over(ctx); + + if (is_imm) { + in1 = load_const(ctx, low_sextract(insn, 16, 5)); + } else { + in1 = load_gpr(ctx, extract32(insn, 16, 5)); + } + in2 = load_gpr(ctx, r); + dest = get_temp(ctx); + + tcg_gen_sub_tl(dest, in1, in2); + + TCGV_UNUSED(sv); + if (c == 6) { + sv = do_sub_sv(ctx, dest, in1, in2); + } + + cond = do_sub_cond(cf, dest, in1, in2, sv); + return do_cbranch(ctx, disp, n, &cond); +} + +static ExitStatus trans_addb(DisasContext *ctx, uint32_t insn, + bool is_true, bool is_imm) +{ + target_long disp = assemble_12(insn) * 4; + unsigned n = extract32(insn, 1, 1); + unsigned c = extract32(insn, 13, 3); + unsigned r = extract32(insn, 21, 5); + unsigned cf = c * 2 + !is_true; + TCGv dest, in1, in2, sv, cb_msb; + DisasCond cond; + + nullify_over(ctx); + + if (is_imm) { + in1 = load_const(ctx, low_sextract(insn, 16, 5)); + } else { + in1 = load_gpr(ctx, extract32(insn, 16, 5)); + } + in2 = load_gpr(ctx, r); + dest = dest_gpr(ctx, r); + TCGV_UNUSED(sv); + TCGV_UNUSED(cb_msb); + + switch (c) { + default: + tcg_gen_add_tl(dest, in1, in2); + break; + case 4: case 5: + cb_msb = get_temp(ctx); + tcg_gen_movi_tl(cb_msb, 0); + tcg_gen_add2_tl(dest, cb_msb, in1, cb_msb, in2, cb_msb); + break; + case 6: + tcg_gen_add_tl(dest, in1, in2); + sv = do_add_sv(ctx, dest, in1, in2); + break; + } + + cond = do_cond(cf, dest, cb_msb, sv); + return do_cbranch(ctx, disp, n, &cond); +} + +static ExitStatus trans_bb(DisasContext *ctx, uint32_t insn) +{ + target_long disp = assemble_12(insn) * 4; + unsigned n = extract32(insn, 1, 1); + unsigned c = extract32(insn, 15, 1); + unsigned r = extract32(insn, 16, 5); + unsigned p = extract32(insn, 21, 5); + unsigned i = extract32(insn, 26, 1); + TCGv tmp, tcg_r; + DisasCond cond; + + nullify_over(ctx); + + tmp = tcg_temp_new(); + tcg_r = load_gpr(ctx, r); + if (i) { + tcg_gen_shli_tl(tmp, tcg_r, p); + } else { + tcg_gen_shl_tl(tmp, tcg_r, cpu_sar); + } + + cond = cond_make_0(c ? TCG_COND_GE : TCG_COND_LT, tmp); + tcg_temp_free(tmp); + return do_cbranch(ctx, disp, n, &cond); +} + +static ExitStatus trans_movb(DisasContext *ctx, uint32_t insn, bool is_imm) +{ + target_long disp = assemble_12(insn) * 4; + unsigned n = extract32(insn, 1, 1); + unsigned c = extract32(insn, 13, 3); + unsigned t = extract32(insn, 16, 5); + unsigned r = extract32(insn, 21, 5); + TCGv dest; + DisasCond cond; + + nullify_over(ctx); + + dest = dest_gpr(ctx, r); + if (is_imm) { + tcg_gen_movi_tl(dest, low_sextract(t, 0, 5)); + } else if (t == 0) { + tcg_gen_movi_tl(dest, 0); + } else { + tcg_gen_mov_tl(dest, cpu_gr[t]); + } + + cond = do_sed_cond(c, dest); + return do_cbranch(ctx, disp, n, &cond); +} + +static ExitStatus trans_shrpw_sar(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + unsigned c = extract32(insn, 13, 3); + unsigned r1 = extract32(insn, 16, 5); + unsigned r2 = extract32(insn, 21, 5); + TCGv dest; + + if (c) { + nullify_over(ctx); + } + + dest = dest_gpr(ctx, rt); + if (r1 == 0) { + tcg_gen_ext32u_tl(dest, load_gpr(ctx, r2)); + tcg_gen_shr_tl(dest, dest, cpu_sar); + } else if (r1 == r2) { + TCGv_i32 t32 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t32, load_gpr(ctx, r2)); + tcg_gen_rotr_i32(t32, t32, cpu_sar); + tcg_gen_extu_i32_tl(dest, t32); + tcg_temp_free_i32(t32); + } else { + TCGv_i64 t = tcg_temp_new_i64(); + TCGv_i64 s = tcg_temp_new_i64(); + + tcg_gen_concat_tl_i64(t, load_gpr(ctx, r2), load_gpr(ctx, r1)); + tcg_gen_extu_tl_i64(s, cpu_sar); + tcg_gen_shr_i64(t, t, s); + tcg_gen_trunc_i64_tl(dest, t); + + tcg_temp_free_i64(t); + tcg_temp_free_i64(s); + } + save_gpr(ctx, rt, dest); + + /* Install the new nullification. */ + cond_free(&ctx->null_cond); + if (c) { + ctx->null_cond = do_sed_cond(c, dest); + } + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus trans_shrpw_imm(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + unsigned cpos = extract32(insn, 5, 5); + unsigned c = extract32(insn, 13, 3); + unsigned r1 = extract32(insn, 16, 5); + unsigned r2 = extract32(insn, 21, 5); + unsigned sa = 31 - cpos; + TCGv dest, t2; + + if (c) { + nullify_over(ctx); + } + + dest = dest_gpr(ctx, rt); + t2 = load_gpr(ctx, r2); + if (r1 == r2) { + TCGv_i32 t32 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t32, t2); + tcg_gen_rotri_i32(t32, t32, sa); + tcg_gen_extu_i32_tl(dest, t32); + tcg_temp_free_i32(t32); + } else if (r1 == 0) { + tcg_gen_extract_tl(dest, t2, sa, 32 - sa); + } else { + TCGv t0 = tcg_temp_new(); + tcg_gen_extract_tl(t0, t2, sa, 32 - sa); + tcg_gen_deposit_tl(dest, t0, cpu_gr[r1], 32 - sa, sa); + tcg_temp_free(t0); + } + save_gpr(ctx, rt, dest); + + /* Install the new nullification. */ + cond_free(&ctx->null_cond); + if (c) { + ctx->null_cond = do_sed_cond(c, dest); + } + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus trans_extrw_sar(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned clen = extract32(insn, 0, 5); + unsigned is_se = extract32(insn, 10, 1); + unsigned c = extract32(insn, 13, 3); + unsigned rt = extract32(insn, 16, 5); + unsigned rr = extract32(insn, 21, 5); + unsigned len = 32 - clen; + TCGv dest, src, tmp; + + if (c) { + nullify_over(ctx); + } + + dest = dest_gpr(ctx, rt); + src = load_gpr(ctx, rr); + tmp = tcg_temp_new(); + + /* Recall that SAR is using big-endian bit numbering. */ + tcg_gen_xori_tl(tmp, cpu_sar, TARGET_LONG_BITS - 1); + if (is_se) { + tcg_gen_sar_tl(dest, src, tmp); + tcg_gen_sextract_tl(dest, dest, 0, len); + } else { + tcg_gen_shr_tl(dest, src, tmp); + tcg_gen_extract_tl(dest, dest, 0, len); + } + tcg_temp_free(tmp); + save_gpr(ctx, rt, dest); + + /* Install the new nullification. */ + cond_free(&ctx->null_cond); + if (c) { + ctx->null_cond = do_sed_cond(c, dest); + } + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus trans_extrw_imm(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned clen = extract32(insn, 0, 5); + unsigned pos = extract32(insn, 5, 5); + unsigned is_se = extract32(insn, 10, 1); + unsigned c = extract32(insn, 13, 3); + unsigned rt = extract32(insn, 16, 5); + unsigned rr = extract32(insn, 21, 5); + unsigned len = 32 - clen; + unsigned cpos = 31 - pos; + TCGv dest, src; + + if (c) { + nullify_over(ctx); + } + + dest = dest_gpr(ctx, rt); + src = load_gpr(ctx, rr); + if (is_se) { + tcg_gen_sextract_tl(dest, src, cpos, len); + } else { + tcg_gen_extract_tl(dest, src, cpos, len); + } + save_gpr(ctx, rt, dest); + + /* Install the new nullification. */ + cond_free(&ctx->null_cond); + if (c) { + ctx->null_cond = do_sed_cond(c, dest); + } + return nullify_end(ctx, NO_EXIT); +} + +static const DisasInsn table_sh_ex[] = { + { 0xd0000000u, 0xfc001fe0u, trans_shrpw_sar }, + { 0xd0000800u, 0xfc001c00u, trans_shrpw_imm }, + { 0xd0001000u, 0xfc001be0u, trans_extrw_sar }, + { 0xd0001800u, 0xfc001800u, trans_extrw_imm }, +}; + +static ExitStatus trans_depw_imm_c(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned clen = extract32(insn, 0, 5); + unsigned cpos = extract32(insn, 5, 5); + unsigned nz = extract32(insn, 10, 1); + unsigned c = extract32(insn, 13, 3); + target_long val = low_sextract(insn, 16, 5); + unsigned rt = extract32(insn, 21, 5); + unsigned len = 32 - clen; + target_long mask0, mask1; + TCGv dest; + + if (c) { + nullify_over(ctx); + } + if (cpos + len > 32) { + len = 32 - cpos; + } + + dest = dest_gpr(ctx, rt); + mask0 = deposit64(0, cpos, len, val); + mask1 = deposit64(-1, cpos, len, val); + + if (nz) { + TCGv src = load_gpr(ctx, rt); + if (mask1 != -1) { + tcg_gen_andi_tl(dest, src, mask1); + src = dest; + } + tcg_gen_ori_tl(dest, src, mask0); + } else { + tcg_gen_movi_tl(dest, mask0); + } + save_gpr(ctx, rt, dest); + + /* Install the new nullification. */ + cond_free(&ctx->null_cond); + if (c) { + ctx->null_cond = do_sed_cond(c, dest); + } + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus trans_depw_imm(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned clen = extract32(insn, 0, 5); + unsigned cpos = extract32(insn, 5, 5); + unsigned nz = extract32(insn, 10, 1); + unsigned c = extract32(insn, 13, 3); + unsigned rr = extract32(insn, 16, 5); + unsigned rt = extract32(insn, 21, 5); + unsigned rs = nz ? rt : 0; + unsigned len = 32 - clen; + TCGv dest, val; + + if (c) { + nullify_over(ctx); + } + if (cpos + len > 32) { + len = 32 - cpos; + } + + dest = dest_gpr(ctx, rt); + val = load_gpr(ctx, rr); + if (rs == 0) { + tcg_gen_deposit_z_tl(dest, val, cpos, len); + } else { + tcg_gen_deposit_tl(dest, cpu_gr[rs], val, cpos, len); + } + save_gpr(ctx, rt, dest); + + /* Install the new nullification. */ + cond_free(&ctx->null_cond); + if (c) { + ctx->null_cond = do_sed_cond(c, dest); + } + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus trans_depw_sar(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned clen = extract32(insn, 0, 5); + unsigned nz = extract32(insn, 10, 1); + unsigned i = extract32(insn, 12, 1); + unsigned c = extract32(insn, 13, 3); + unsigned rt = extract32(insn, 21, 5); + unsigned rs = nz ? rt : 0; + unsigned len = 32 - clen; + TCGv val, mask, tmp, shift, dest; + unsigned msb = 1U << (len - 1); + + if (c) { + nullify_over(ctx); + } + + if (i) { + val = load_const(ctx, low_sextract(insn, 16, 5)); + } else { + val = load_gpr(ctx, extract32(insn, 16, 5)); + } + dest = dest_gpr(ctx, rt); + shift = tcg_temp_new(); + tmp = tcg_temp_new(); + + /* Convert big-endian bit numbering in SAR to left-shift. */ + tcg_gen_xori_tl(shift, cpu_sar, TARGET_LONG_BITS - 1); + + mask = tcg_const_tl(msb + (msb - 1)); + tcg_gen_and_tl(tmp, val, mask); + if (rs) { + tcg_gen_shl_tl(mask, mask, shift); + tcg_gen_shl_tl(tmp, tmp, shift); + tcg_gen_andc_tl(dest, cpu_gr[rs], mask); + tcg_gen_or_tl(dest, dest, tmp); + } else { + tcg_gen_shl_tl(dest, tmp, shift); + } + tcg_temp_free(shift); + tcg_temp_free(mask); + tcg_temp_free(tmp); + save_gpr(ctx, rt, dest); + + /* Install the new nullification. */ + cond_free(&ctx->null_cond); + if (c) { + ctx->null_cond = do_sed_cond(c, dest); + } + return nullify_end(ctx, NO_EXIT); +} + +static const DisasInsn table_depw[] = { + { 0xd4000000u, 0xfc000be0u, trans_depw_sar }, + { 0xd4000800u, 0xfc001800u, trans_depw_imm }, + { 0xd4001800u, 0xfc001800u, trans_depw_imm_c }, +}; + +static ExitStatus trans_be(DisasContext *ctx, uint32_t insn, bool is_l) +{ + unsigned n = extract32(insn, 1, 1); + unsigned b = extract32(insn, 21, 5); + target_long disp = assemble_17(insn); + + /* unsigned s = low_uextract(insn, 13, 3); */ + /* ??? It seems like there should be a good way of using + "be disp(sr2, r0)", the canonical gateway entry mechanism + to our advantage. But that appears to be inconvenient to + manage along side branch delay slots. Therefore we handle + entry into the gateway page via absolute address. */ + + /* Since we don't implement spaces, just branch. Do notice the special + case of "be disp(*,r0)" using a direct branch to disp, so that we can + goto_tb to the TB containing the syscall. */ + if (b == 0) { + return do_dbranch(ctx, disp, is_l ? 31 : 0, n); + } else { + TCGv tmp = get_temp(ctx); + tcg_gen_addi_tl(tmp, load_gpr(ctx, b), disp); + return do_ibranch(ctx, tmp, is_l ? 31 : 0, n); + } +} + +static ExitStatus trans_bl(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned n = extract32(insn, 1, 1); + unsigned link = extract32(insn, 21, 5); + target_long disp = assemble_17(insn); + + return do_dbranch(ctx, iaoq_dest(ctx, disp), link, n); +} + +static ExitStatus trans_bl_long(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned n = extract32(insn, 1, 1); + target_long disp = assemble_22(insn); + + return do_dbranch(ctx, iaoq_dest(ctx, disp), 2, n); +} + +static ExitStatus trans_blr(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned n = extract32(insn, 1, 1); + unsigned rx = extract32(insn, 16, 5); + unsigned link = extract32(insn, 21, 5); + TCGv tmp = get_temp(ctx); + + tcg_gen_shli_tl(tmp, load_gpr(ctx, rx), 3); + tcg_gen_addi_tl(tmp, tmp, ctx->iaoq_f + 8); + return do_ibranch(ctx, tmp, link, n); +} + +static ExitStatus trans_bv(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned n = extract32(insn, 1, 1); + unsigned rx = extract32(insn, 16, 5); + unsigned rb = extract32(insn, 21, 5); + TCGv dest; + + if (rx == 0) { + dest = load_gpr(ctx, rb); + } else { + dest = get_temp(ctx); + tcg_gen_shli_tl(dest, load_gpr(ctx, rx), 3); + tcg_gen_add_tl(dest, dest, load_gpr(ctx, rb)); + } + return do_ibranch(ctx, dest, 0, n); +} + +static ExitStatus trans_bve(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned n = extract32(insn, 1, 1); + unsigned rb = extract32(insn, 21, 5); + unsigned link = extract32(insn, 13, 1) ? 2 : 0; + + return do_ibranch(ctx, load_gpr(ctx, rb), link, n); +} + +static const DisasInsn table_branch[] = { + { 0xe8000000u, 0xfc006000u, trans_bl }, /* B,L and B,L,PUSH */ + { 0xe800a000u, 0xfc00e000u, trans_bl_long }, + { 0xe8004000u, 0xfc00fffdu, trans_blr }, + { 0xe800c000u, 0xfc00fffdu, trans_bv }, + { 0xe800d000u, 0xfc00dffcu, trans_bve }, +}; + +static ExitStatus trans_fop_wew_0c(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + unsigned ra = extract32(insn, 21, 5); + return do_fop_wew(ctx, rt, ra, di->f_wew); +} + +static ExitStatus trans_fop_wew_0e(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = assemble_rt64(insn); + unsigned ra = assemble_ra64(insn); + return do_fop_wew(ctx, rt, ra, di->f_wew); +} + +static ExitStatus trans_fop_ded(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + unsigned ra = extract32(insn, 21, 5); + return do_fop_ded(ctx, rt, ra, di->f_ded); +} + +static ExitStatus trans_fop_wed_0c(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + unsigned ra = extract32(insn, 21, 5); + return do_fop_wed(ctx, rt, ra, di->f_wed); +} + +static ExitStatus trans_fop_wed_0e(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = assemble_rt64(insn); + unsigned ra = extract32(insn, 21, 5); + return do_fop_wed(ctx, rt, ra, di->f_wed); +} + +static ExitStatus trans_fop_dew_0c(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + unsigned ra = extract32(insn, 21, 5); + return do_fop_dew(ctx, rt, ra, di->f_dew); +} + +static ExitStatus trans_fop_dew_0e(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + unsigned ra = assemble_ra64(insn); + return do_fop_dew(ctx, rt, ra, di->f_dew); +} + +static ExitStatus trans_fop_weww_0c(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + unsigned rb = extract32(insn, 16, 5); + unsigned ra = extract32(insn, 21, 5); + return do_fop_weww(ctx, rt, ra, rb, di->f_weww); +} + +static ExitStatus trans_fop_weww_0e(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = assemble_rt64(insn); + unsigned rb = assemble_rb64(insn); + unsigned ra = assemble_ra64(insn); + return do_fop_weww(ctx, rt, ra, rb, di->f_weww); +} + +static ExitStatus trans_fop_dedd(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + unsigned rb = extract32(insn, 16, 5); + unsigned ra = extract32(insn, 21, 5); + return do_fop_dedd(ctx, rt, ra, rb, di->f_dedd); +} + +static void gen_fcpy_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) +{ + tcg_gen_mov_i32(dst, src); +} + +static void gen_fcpy_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src) +{ + tcg_gen_mov_i64(dst, src); +} + +static void gen_fabs_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) +{ + tcg_gen_andi_i32(dst, src, INT32_MAX); +} + +static void gen_fabs_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src) +{ + tcg_gen_andi_i64(dst, src, INT64_MAX); +} + +static void gen_fneg_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) +{ + tcg_gen_xori_i32(dst, src, INT32_MIN); +} + +static void gen_fneg_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src) +{ + tcg_gen_xori_i64(dst, src, INT64_MIN); +} + +static void gen_fnegabs_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) +{ + tcg_gen_ori_i32(dst, src, INT32_MIN); +} + +static void gen_fnegabs_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src) +{ + tcg_gen_ori_i64(dst, src, INT64_MIN); +} + +static ExitStatus do_fcmp_s(DisasContext *ctx, unsigned ra, unsigned rb, + unsigned y, unsigned c) +{ + TCGv_i32 ta, tb, tc, ty; + + nullify_over(ctx); + + ta = load_frw0_i32(ra); + tb = load_frw0_i32(rb); + ty = tcg_const_i32(y); + tc = tcg_const_i32(c); + + gen_helper_fcmp_s(cpu_env, ta, tb, ty, tc); + + tcg_temp_free_i32(ta); + tcg_temp_free_i32(tb); + tcg_temp_free_i32(ty); + tcg_temp_free_i32(tc); + + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus trans_fcmp_s_0c(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned c = extract32(insn, 0, 5); + unsigned y = extract32(insn, 13, 3); + unsigned rb = extract32(insn, 16, 5); + unsigned ra = extract32(insn, 21, 5); + return do_fcmp_s(ctx, ra, rb, y, c); +} + +static ExitStatus trans_fcmp_s_0e(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned c = extract32(insn, 0, 5); + unsigned y = extract32(insn, 13, 3); + unsigned rb = assemble_rb64(insn); + unsigned ra = assemble_ra64(insn); + return do_fcmp_s(ctx, ra, rb, y, c); +} + +static ExitStatus trans_fcmp_d(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned c = extract32(insn, 0, 5); + unsigned y = extract32(insn, 13, 3); + unsigned rb = extract32(insn, 16, 5); + unsigned ra = extract32(insn, 21, 5); + TCGv_i64 ta, tb; + TCGv_i32 tc, ty; + + nullify_over(ctx); + + ta = load_frd0(ra); + tb = load_frd0(rb); + ty = tcg_const_i32(y); + tc = tcg_const_i32(c); + + gen_helper_fcmp_d(cpu_env, ta, tb, ty, tc); + + tcg_temp_free_i64(ta); + tcg_temp_free_i64(tb); + tcg_temp_free_i32(ty); + tcg_temp_free_i32(tc); + + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus trans_ftest_t(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned y = extract32(insn, 13, 3); + unsigned cbit = (y ^ 1) - 1; + TCGv t; + + nullify_over(ctx); + + t = tcg_temp_new(); + tcg_gen_ld32u_tl(t, cpu_env, offsetof(CPUHPPAState, fr0_shadow)); + tcg_gen_extract_tl(t, t, 21 - cbit, 1); + ctx->null_cond = cond_make_0(TCG_COND_NE, t); + tcg_temp_free(t); + + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus trans_ftest_q(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned c = extract32(insn, 0, 5); + int mask; + bool inv = false; + TCGv t; + + nullify_over(ctx); + + t = tcg_temp_new(); + tcg_gen_ld32u_tl(t, cpu_env, offsetof(CPUHPPAState, fr0_shadow)); + + switch (c) { + case 0: /* simple */ + tcg_gen_andi_tl(t, t, 0x4000000); + ctx->null_cond = cond_make_0(TCG_COND_NE, t); + goto done; + case 2: /* rej */ + inv = true; + /* fallthru */ + case 1: /* acc */ + mask = 0x43ff800; + break; + case 6: /* rej8 */ + inv = true; + /* fallthru */ + case 5: /* acc8 */ + mask = 0x43f8000; + break; + case 9: /* acc6 */ + mask = 0x43e0000; + break; + case 13: /* acc4 */ + mask = 0x4380000; + break; + case 17: /* acc2 */ + mask = 0x4200000; + break; + default: + return gen_illegal(ctx); + } + if (inv) { + TCGv c = load_const(ctx, mask); + tcg_gen_or_tl(t, t, c); + ctx->null_cond = cond_make(TCG_COND_EQ, t, c); + } else { + tcg_gen_andi_tl(t, t, mask); + ctx->null_cond = cond_make_0(TCG_COND_EQ, t); + } + done: + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus trans_xmpyu(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + unsigned rb = assemble_rb64(insn); + unsigned ra = assemble_ra64(insn); + TCGv_i64 a, b; + + nullify_over(ctx); + + a = load_frw0_i64(ra); + b = load_frw0_i64(rb); + tcg_gen_mul_i64(a, a, b); + save_frd(rt, a); + tcg_temp_free_i64(a); + tcg_temp_free_i64(b); + + return nullify_end(ctx, NO_EXIT); +} + +#define FOP_DED trans_fop_ded, .f_ded +#define FOP_DEDD trans_fop_dedd, .f_dedd + +#define FOP_WEW trans_fop_wew_0c, .f_wew +#define FOP_DEW trans_fop_dew_0c, .f_dew +#define FOP_WED trans_fop_wed_0c, .f_wed +#define FOP_WEWW trans_fop_weww_0c, .f_weww + +static const DisasInsn table_float_0c[] = { + /* floating point class zero */ + { 0x30004000, 0xfc1fffe0, FOP_WEW = gen_fcpy_s }, + { 0x30006000, 0xfc1fffe0, FOP_WEW = gen_fabs_s }, + { 0x30008000, 0xfc1fffe0, FOP_WEW = gen_helper_fsqrt_s }, + { 0x3000a000, 0xfc1fffe0, FOP_WEW = gen_helper_frnd_s }, + { 0x3000c000, 0xfc1fffe0, FOP_WEW = gen_fneg_s }, + { 0x3000e000, 0xfc1fffe0, FOP_WEW = gen_fnegabs_s }, + + { 0x30004800, 0xfc1fffe0, FOP_DED = gen_fcpy_d }, + { 0x30006800, 0xfc1fffe0, FOP_DED = gen_fabs_d }, + { 0x30008800, 0xfc1fffe0, FOP_DED = gen_helper_fsqrt_d }, + { 0x3000a800, 0xfc1fffe0, FOP_DED = gen_helper_frnd_d }, + { 0x3000c800, 0xfc1fffe0, FOP_DED = gen_fneg_d }, + { 0x3000e800, 0xfc1fffe0, FOP_DED = gen_fnegabs_d }, + + /* floating point class three */ + { 0x30000600, 0xfc00ffe0, FOP_WEWW = gen_helper_fadd_s }, + { 0x30002600, 0xfc00ffe0, FOP_WEWW = gen_helper_fsub_s }, + { 0x30004600, 0xfc00ffe0, FOP_WEWW = gen_helper_fmpy_s }, + { 0x30006600, 0xfc00ffe0, FOP_WEWW = gen_helper_fdiv_s }, + + { 0x30000e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fadd_d }, + { 0x30002e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fsub_d }, + { 0x30004e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fmpy_d }, + { 0x30006e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fdiv_d }, + + /* floating point class one */ + /* float/float */ + { 0x30000a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_d_s }, + { 0x30002200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_s_d }, + /* int/float */ + { 0x30008200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_w_s }, + { 0x30008a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_dw_s }, + { 0x3000a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_w_d }, + { 0x3000aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_dw_d }, + /* float/int */ + { 0x30010200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_s_w }, + { 0x30010a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_d_w }, + { 0x30012200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_s_dw }, + { 0x30012a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_dw }, + /* float/int truncate */ + { 0x30018200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_t_s_w }, + { 0x30018a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_t_d_w }, + { 0x3001a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_t_s_dw }, + { 0x3001aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_dw }, + /* uint/float */ + { 0x30028200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_uw_s }, + { 0x30028a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_udw_s }, + { 0x3002a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_uw_d }, + { 0x3002aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_udw_d }, + /* float/uint */ + { 0x30030200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_s_uw }, + { 0x30030a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_d_uw }, + { 0x30032200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_s_udw }, + { 0x30032a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_udw }, + /* float/uint truncate */ + { 0x30038200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_t_s_uw }, + { 0x30038a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_t_d_uw }, + { 0x3003a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_t_s_udw }, + { 0x3003aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_udw }, + + /* floating point class two */ + { 0x30000400, 0xfc001fe0, trans_fcmp_s_0c }, + { 0x30000c00, 0xfc001fe0, trans_fcmp_d }, + { 0x30002420, 0xffffffe0, trans_ftest_q }, + { 0x30000420, 0xffff1fff, trans_ftest_t }, + + /* FID. Note that ra == rt == 0, which via fcpy puts 0 into fr0. + This is machine/revision == 0, which is reserved for simulator. */ + { 0x30000000, 0xffffffff, FOP_WEW = gen_fcpy_s }, +}; + +#undef FOP_WEW +#undef FOP_DEW +#undef FOP_WED +#undef FOP_WEWW +#define FOP_WEW trans_fop_wew_0e, .f_wew +#define FOP_DEW trans_fop_dew_0e, .f_dew +#define FOP_WED trans_fop_wed_0e, .f_wed +#define FOP_WEWW trans_fop_weww_0e, .f_weww + +static const DisasInsn table_float_0e[] = { + /* floating point class zero */ + { 0x38004000, 0xfc1fff20, FOP_WEW = gen_fcpy_s }, + { 0x38006000, 0xfc1fff20, FOP_WEW = gen_fabs_s }, + { 0x38008000, 0xfc1fff20, FOP_WEW = gen_helper_fsqrt_s }, + { 0x3800a000, 0xfc1fff20, FOP_WEW = gen_helper_frnd_s }, + { 0x3800c000, 0xfc1fff20, FOP_WEW = gen_fneg_s }, + { 0x3800e000, 0xfc1fff20, FOP_WEW = gen_fnegabs_s }, + + { 0x38004800, 0xfc1fffe0, FOP_DED = gen_fcpy_d }, + { 0x38006800, 0xfc1fffe0, FOP_DED = gen_fabs_d }, + { 0x38008800, 0xfc1fffe0, FOP_DED = gen_helper_fsqrt_d }, + { 0x3800a800, 0xfc1fffe0, FOP_DED = gen_helper_frnd_d }, + { 0x3800c800, 0xfc1fffe0, FOP_DED = gen_fneg_d }, + { 0x3800e800, 0xfc1fffe0, FOP_DED = gen_fnegabs_d }, + + /* floating point class three */ + { 0x38000600, 0xfc00ef20, FOP_WEWW = gen_helper_fadd_s }, + { 0x38002600, 0xfc00ef20, FOP_WEWW = gen_helper_fsub_s }, + { 0x38004600, 0xfc00ef20, FOP_WEWW = gen_helper_fmpy_s }, + { 0x38006600, 0xfc00ef20, FOP_WEWW = gen_helper_fdiv_s }, + + { 0x38000e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fadd_d }, + { 0x38002e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fsub_d }, + { 0x38004e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fmpy_d }, + { 0x38006e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fdiv_d }, + + { 0x38004700, 0xfc00ef60, trans_xmpyu }, + + /* floating point class one */ + /* float/float */ + { 0x38000a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_s }, + { 0x38002200, 0xfc1fffc0, FOP_DEW = gen_helper_fcnv_s_d }, + /* int/float */ + { 0x38008200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_w_s }, + { 0x38008a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_dw_s }, + { 0x3800a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_w_d }, + { 0x3800aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_dw_d }, + /* float/int */ + { 0x38010200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_s_w }, + { 0x38010a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_w }, + { 0x38012200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_s_dw }, + { 0x38012a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_dw }, + /* float/int truncate */ + { 0x38018200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_t_s_w }, + { 0x38018a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_t_d_w }, + { 0x3801a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_t_s_dw }, + { 0x3801aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_dw }, + /* uint/float */ + { 0x38028200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_uw_s }, + { 0x38028a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_udw_s }, + { 0x3802a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_uw_d }, + { 0x3802aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_udw_d }, + /* float/uint */ + { 0x38030200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_s_uw }, + { 0x38030a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_uw }, + { 0x38032200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_s_udw }, + { 0x38032a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_udw }, + /* float/uint truncate */ + { 0x38038200, 0xfc1ffe60, FOP_WEW = gen_helper_fcnv_t_s_uw }, + { 0x38038a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_t_d_uw }, + { 0x3803a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_t_s_udw }, + { 0x3803aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_udw }, + + /* floating point class two */ + { 0x38000400, 0xfc000f60, trans_fcmp_s_0e }, + { 0x38000c00, 0xfc001fe0, trans_fcmp_d }, +}; + +#undef FOP_WEW +#undef FOP_DEW +#undef FOP_WED +#undef FOP_WEWW +#undef FOP_DED +#undef FOP_DEDD + +/* Convert the fmpyadd single-precision register encodings to standard. */ +static inline int fmpyadd_s_reg(unsigned r) +{ + return (r & 16) * 2 + 16 + (r & 15); +} + +static ExitStatus trans_fmpyadd(DisasContext *ctx, uint32_t insn, bool is_sub) +{ + unsigned tm = extract32(insn, 0, 5); + unsigned f = extract32(insn, 5, 1); + unsigned ra = extract32(insn, 6, 5); + unsigned ta = extract32(insn, 11, 5); + unsigned rm2 = extract32(insn, 16, 5); + unsigned rm1 = extract32(insn, 21, 5); + + nullify_over(ctx); + + /* Independent multiply & add/sub, with undefined behaviour + if outputs overlap inputs. */ + if (f == 0) { + tm = fmpyadd_s_reg(tm); + ra = fmpyadd_s_reg(ra); + ta = fmpyadd_s_reg(ta); + rm2 = fmpyadd_s_reg(rm2); + rm1 = fmpyadd_s_reg(rm1); + do_fop_weww(ctx, tm, rm1, rm2, gen_helper_fmpy_s); + do_fop_weww(ctx, ta, ta, ra, + is_sub ? gen_helper_fsub_s : gen_helper_fadd_s); + } else { + do_fop_dedd(ctx, tm, rm1, rm2, gen_helper_fmpy_d); + do_fop_dedd(ctx, ta, ta, ra, + is_sub ? gen_helper_fsub_d : gen_helper_fadd_d); + } + + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus trans_fmpyfadd_s(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = assemble_rt64(insn); + unsigned neg = extract32(insn, 5, 1); + unsigned rm1 = assemble_ra64(insn); + unsigned rm2 = assemble_rb64(insn); + unsigned ra3 = assemble_rc64(insn); + TCGv_i32 a, b, c; + + nullify_over(ctx); + a = load_frw0_i32(rm1); + b = load_frw0_i32(rm2); + c = load_frw0_i32(ra3); + + if (neg) { + gen_helper_fmpynfadd_s(a, cpu_env, a, b, c); + } else { + gen_helper_fmpyfadd_s(a, cpu_env, a, b, c); + } + + tcg_temp_free_i32(b); + tcg_temp_free_i32(c); + save_frw_i32(rt, a); + tcg_temp_free_i32(a); + return nullify_end(ctx, NO_EXIT); +} + +static ExitStatus trans_fmpyfadd_d(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + unsigned rt = extract32(insn, 0, 5); + unsigned neg = extract32(insn, 5, 1); + unsigned rm1 = extract32(insn, 21, 5); + unsigned rm2 = extract32(insn, 16, 5); + unsigned ra3 = assemble_rc64(insn); + TCGv_i64 a, b, c; + + nullify_over(ctx); + a = load_frd0(rm1); + b = load_frd0(rm2); + c = load_frd0(ra3); + + if (neg) { + gen_helper_fmpynfadd_d(a, cpu_env, a, b, c); + } else { + gen_helper_fmpyfadd_d(a, cpu_env, a, b, c); + } + + tcg_temp_free_i64(b); + tcg_temp_free_i64(c); + save_frd(rt, a); + tcg_temp_free_i64(a); + return nullify_end(ctx, NO_EXIT); +} + +static const DisasInsn table_fp_fused[] = { + { 0xb8000000u, 0xfc000800u, trans_fmpyfadd_s }, + { 0xb8000800u, 0xfc0019c0u, trans_fmpyfadd_d } +}; + +static ExitStatus translate_table_int(DisasContext *ctx, uint32_t insn, + const DisasInsn table[], size_t n) +{ + size_t i; + for (i = 0; i < n; ++i) { + if ((insn & table[i].mask) == table[i].insn) { + return table[i].trans(ctx, insn, &table[i]); + } + } + return gen_illegal(ctx); +} + +#define translate_table(ctx, insn, table) \ + translate_table_int(ctx, insn, table, ARRAY_SIZE(table)) + +static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) +{ + uint32_t opc = extract32(insn, 26, 6); + + switch (opc) { + case 0x00: /* system op */ + return translate_table(ctx, insn, table_system); + case 0x01: + return translate_table(ctx, insn, table_mem_mgmt); + case 0x02: + return translate_table(ctx, insn, table_arith_log); + case 0x03: + return translate_table(ctx, insn, table_index_mem); + case 0x06: + return trans_fmpyadd(ctx, insn, false); + case 0x08: + return trans_ldil(ctx, insn); + case 0x09: + return trans_copr_w(ctx, insn); + case 0x0A: + return trans_addil(ctx, insn); + case 0x0B: + return trans_copr_dw(ctx, insn); + case 0x0C: + return translate_table(ctx, insn, table_float_0c); + case 0x0D: + return trans_ldo(ctx, insn); + case 0x0E: + return translate_table(ctx, insn, table_float_0e); + + case 0x10: + return trans_load(ctx, insn, false, MO_UB); + case 0x11: + return trans_load(ctx, insn, false, MO_TEUW); + case 0x12: + return trans_load(ctx, insn, false, MO_TEUL); + case 0x13: + return trans_load(ctx, insn, true, MO_TEUL); + case 0x16: + return trans_fload_mod(ctx, insn); + case 0x17: + return trans_load_w(ctx, insn); + case 0x18: + return trans_store(ctx, insn, false, MO_UB); + case 0x19: + return trans_store(ctx, insn, false, MO_TEUW); + case 0x1A: + return trans_store(ctx, insn, false, MO_TEUL); + case 0x1B: + return trans_store(ctx, insn, true, MO_TEUL); + case 0x1E: + return trans_fstore_mod(ctx, insn); + case 0x1F: + return trans_store_w(ctx, insn); + + case 0x20: + return trans_cmpb(ctx, insn, true, false, false); + case 0x21: + return trans_cmpb(ctx, insn, true, true, false); + case 0x22: + return trans_cmpb(ctx, insn, false, false, false); + case 0x23: + return trans_cmpb(ctx, insn, false, true, false); + case 0x24: + return trans_cmpiclr(ctx, insn); + case 0x25: + return trans_subi(ctx, insn); + case 0x26: + return trans_fmpyadd(ctx, insn, true); + case 0x27: + return trans_cmpb(ctx, insn, true, false, true); + case 0x28: + return trans_addb(ctx, insn, true, false); + case 0x29: + return trans_addb(ctx, insn, true, true); + case 0x2A: + return trans_addb(ctx, insn, false, false); + case 0x2B: + return trans_addb(ctx, insn, false, true); + case 0x2C: + case 0x2D: + return trans_addi(ctx, insn); + case 0x2E: + return translate_table(ctx, insn, table_fp_fused); + case 0x2F: + return trans_cmpb(ctx, insn, false, false, true); + + case 0x30: + case 0x31: + return trans_bb(ctx, insn); + case 0x32: + return trans_movb(ctx, insn, false); + case 0x33: + return trans_movb(ctx, insn, true); + case 0x34: + return translate_table(ctx, insn, table_sh_ex); + case 0x35: + return translate_table(ctx, insn, table_depw); + case 0x38: + return trans_be(ctx, insn, false); + case 0x39: + return trans_be(ctx, insn, true); + case 0x3A: + return translate_table(ctx, insn, table_branch); + + case 0x04: /* spopn */ + case 0x05: /* diag */ + case 0x0F: /* product specific */ + break; + + case 0x07: /* unassigned */ + case 0x15: /* unassigned */ + case 0x1D: /* unassigned */ + case 0x37: /* unassigned */ + case 0x3F: /* unassigned */ + default: + break; + } + return gen_illegal(ctx); +} + +void gen_intermediate_code(CPUHPPAState *env, struct TranslationBlock *tb) +{ + HPPACPU *cpu = hppa_env_get_cpu(env); + CPUState *cs = CPU(cpu); + DisasContext ctx; + ExitStatus ret; + int num_insns, max_insns, i; + + ctx.tb = tb; + ctx.cs = cs; + ctx.iaoq_f = tb->pc; + ctx.iaoq_b = tb->cs_base; + ctx.singlestep_enabled = cs->singlestep_enabled; + + ctx.ntemps = 0; + for (i = 0; i < ARRAY_SIZE(ctx.temps); ++i) { + TCGV_UNUSED(ctx.temps[i]); + } + + /* Compute the maximum number of insns to execute, as bounded by + (1) icount, (2) single-stepping, (3) branch delay slots, or + (4) the number of insns remaining on the current page. */ + max_insns = tb->cflags & CF_COUNT_MASK; + if (max_insns == 0) { + max_insns = CF_COUNT_MASK; + } + if (ctx.singlestep_enabled || singlestep) { + max_insns = 1; + } else if (max_insns > TCG_MAX_INSNS) { + max_insns = TCG_MAX_INSNS; + } + + num_insns = 0; + gen_tb_start(tb); + + /* Seed the nullification status from PSW[N], as shown in TB->FLAGS. */ + ctx.null_cond = cond_make_f(); + ctx.psw_n_nonzero = false; + if (tb->flags & 1) { + ctx.null_cond.c = TCG_COND_ALWAYS; + ctx.psw_n_nonzero = true; + } + ctx.null_lab = NULL; + + do { + tcg_gen_insn_start(ctx.iaoq_f, ctx.iaoq_b); + num_insns++; + + if (unlikely(cpu_breakpoint_test(cs, ctx.iaoq_f, BP_ANY))) { + ret = gen_excp(&ctx, EXCP_DEBUG); + break; + } + if (num_insns == max_insns && (tb->cflags & CF_LAST_IO)) { + gen_io_start(); + } + + if (ctx.iaoq_f < TARGET_PAGE_SIZE) { + ret = do_page_zero(&ctx); + assert(ret != NO_EXIT); + } else { + /* Always fetch the insn, even if nullified, so that we check + the page permissions for execute. */ + uint32_t insn = cpu_ldl_code(env, ctx.iaoq_f); + + /* Set up the IA queue for the next insn. + This will be overwritten by a branch. */ + if (ctx.iaoq_b == -1) { + ctx.iaoq_n = -1; + ctx.iaoq_n_var = get_temp(&ctx); + tcg_gen_addi_tl(ctx.iaoq_n_var, cpu_iaoq_b, 4); + } else { + ctx.iaoq_n = ctx.iaoq_b + 4; + TCGV_UNUSED(ctx.iaoq_n_var); + } + + if (unlikely(ctx.null_cond.c == TCG_COND_ALWAYS)) { + ctx.null_cond.c = TCG_COND_NEVER; + ret = NO_EXIT; + } else { + ret = translate_one(&ctx, insn); + assert(ctx.null_lab == NULL); + } + } + + for (i = 0; i < ctx.ntemps; ++i) { + tcg_temp_free(ctx.temps[i]); + TCGV_UNUSED(ctx.temps[i]); + } + ctx.ntemps = 0; + + /* If we see non-linear instructions, exhaust instruction count, + or run out of buffer space, stop generation. */ + /* ??? The non-linear instruction restriction is purely due to + the debugging dump. Otherwise we *could* follow unconditional + branches within the same page. */ + if (ret == NO_EXIT + && (ctx.iaoq_b != ctx.iaoq_f + 4 + || num_insns >= max_insns + || tcg_op_buf_full())) { + if (ctx.null_cond.c == TCG_COND_NEVER + || ctx.null_cond.c == TCG_COND_ALWAYS) { + nullify_set(&ctx, ctx.null_cond.c == TCG_COND_ALWAYS); + gen_goto_tb(&ctx, 0, ctx.iaoq_b, ctx.iaoq_n); + ret = EXIT_GOTO_TB; + } else { + ret = EXIT_IAQ_N_STALE; + } + } + + ctx.iaoq_f = ctx.iaoq_b; + ctx.iaoq_b = ctx.iaoq_n; + if (ret == EXIT_NORETURN + || ret == EXIT_GOTO_TB + || ret == EXIT_IAQ_N_UPDATED) { + break; + } + if (ctx.iaoq_f == -1) { + tcg_gen_mov_tl(cpu_iaoq_f, cpu_iaoq_b); + copy_iaoq_entry(cpu_iaoq_b, ctx.iaoq_n, ctx.iaoq_n_var); + nullify_save(&ctx); + ret = EXIT_IAQ_N_UPDATED; + break; + } + if (ctx.iaoq_b == -1) { + tcg_gen_mov_tl(cpu_iaoq_b, ctx.iaoq_n_var); + } + } while (ret == NO_EXIT); + + if (tb->cflags & CF_LAST_IO) { + gen_io_end(); + } + + switch (ret) { + case EXIT_GOTO_TB: + case EXIT_NORETURN: + break; + case EXIT_IAQ_N_STALE: + copy_iaoq_entry(cpu_iaoq_f, ctx.iaoq_f, cpu_iaoq_f); + copy_iaoq_entry(cpu_iaoq_b, ctx.iaoq_b, cpu_iaoq_b); + nullify_save(&ctx); + /* FALLTHRU */ + case EXIT_IAQ_N_UPDATED: + if (ctx.singlestep_enabled) { + gen_excp_1(EXCP_DEBUG); + } else { + tcg_gen_exit_tb(0); + } + break; + default: + abort(); + } + + gen_tb_end(tb, num_insns); + + tb->size = num_insns * 4; + tb->icount = num_insns; + +#ifdef DEBUG_DISAS + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) + && qemu_log_in_addr_range(tb->pc)) { + qemu_log_lock(); + switch (tb->pc) { + case 0x00: + qemu_log("IN:\n0x00000000: (null)\n\n"); + break; + case 0xb0: + qemu_log("IN:\n0x000000b0: light-weight-syscall\n\n"); + break; + case 0xe0: + qemu_log("IN:\n0x000000e0: set-thread-pointer-syscall\n\n"); + break; + case 0x100: + qemu_log("IN:\n0x00000100: syscall\n\n"); + break; + default: + qemu_log("IN: %s\n", lookup_symbol(tb->pc)); + log_target_disas(cs, tb->pc, tb->size, 1); + qemu_log("\n"); + break; + } + qemu_log_unlock(); + } +#endif +} + +void restore_state_to_opc(CPUHPPAState *env, TranslationBlock *tb, + target_ulong *data) +{ + env->iaoq_f = data[0]; + if (data[1] != -1) { + env->iaoq_b = data[1]; + } + /* Since we were executing the instruction at IAOQ_F, and took some + sort of action that provoked the cpu_restore_state, we can infer + that the instruction was not nullified. */ + env->psw_n = 0; +} |