From 429b31a205bd3362a5eed22ca9b1a4c1b92f29f5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 29 Sep 2016 10:55:53 -0700 Subject: [PATCH 01/25] Revert "Remove remainders of HPPA backend" This reverts commit d41f3c3cc7a5fb9de144cc4022da14a9ff010671. Signed-off-by: Richard Henderson --- configure | 5 + disas.c | 2 + disas/Makefile.objs | 1 + disas/hppa.c | 2832 +++++++++++++++++++++++++++++++++++++ linux-user/syscall_defs.h | 4 +- 5 files changed, 2842 insertions(+), 2 deletions(-) create mode 100644 disas/hppa.c diff --git a/configure b/configure index 17d52cdd74..494c9cca3f 100755 --- a/configure +++ b/configure @@ -510,6 +510,8 @@ elif check_define __arm__ ; then cpu="arm" elif check_define __aarch64__ ; then cpu="aarch64" +elif check_define __hppa__ ; then + cpu="hppa" else cpu=$(uname -m) fi @@ -6107,6 +6109,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/disas.c b/disas.c index 67f116a19b..05a7a1260a 100644 --- a/disas.c +++ b/disas.c @@ -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 . */ + +#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 . */ + +#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 . */ + +#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/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 0b15466743..7607df8cce 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2375,8 +2375,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 From 7c4ee5bcc82e643836a5f32db85887d2475288f7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 09:38:11 -0800 Subject: [PATCH 02/25] linux-user: Support stack-grows-up in elfload.c HPPA is a (the) stack-grows-up target, and supporting that requires rearranging how we compute addresses while laying out the initial program stack. In addition, hppa32 requires 64-byte stack alignment so parameterize that as well. Signed-off-by: Richard Henderson --- linux-user/elfload.c | 235 +++++++++++++++++++++++++++++++------------ linux-user/main.c | 13 +-- linux-user/qemu.h | 3 + 3 files changed, 180 insertions(+), 71 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 547053c27a..5cea39d172 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1231,6 +1231,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 +1382,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); - } - len = strlen(tmp) + 1; - tmp += len; + 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; - 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); - memcpy_fromfs(scratch + offset, tmp, bytes_to_copy); - - if (offset == 0) { - memcpy_to_target(p, scratch, top - p); - top = p; - offset = TARGET_PAGE_SIZE; + if (offset == 0) { + memcpy_to_target(p, scratch, top - p); + top = p; + offset = TARGET_PAGE_SIZE; + } } } - } - if (offset) { - memcpy_to_target(p, scratch + offset, top - p); + if (p != top) { + memcpy_to_target(p, scratch + offset, top - p); + } + } 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 (p != top) { + memcpy_to_target(top, scratch, p - top); + } } return p; @@ -1447,11 +1488,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 +1574,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 +1603,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 +1631,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 +1654,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 +1699,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 +1709,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 +2302,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/main.c b/linux-user/main.c index 94a636f02a..aae29bd94d 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -4179,15 +4179,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); 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 From 8cb762fe013a4368fc7aa2eba27c44033ec310e0 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Tue, 6 Dec 2016 16:24:03 +0100 Subject: [PATCH 03/25] linux-user: Handle TIOCSTART and TIOCSTOP Some architectures (ppc, alpha, sparc, parisc, sh and xtensa) define the BSD TIOCSTART and TIOCSTOP ioctls in their kernel headers to provide compatibility to other operating systems. Those ioctls are not implemented in Linux, nevertheless, bash will use this ioctl if it's available on those architectures. To avoid false warnings, add code to simply ignore those ioctls. Signed-off-by: Helge Deller Message-Id: <20161206152403.GA6651@ls3530> Signed-off-by: Richard Henderson --- linux-user/ioctls.h | 5 +++++ linux-user/syscall.c | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 1bad701481..eaf6fe61b0 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -422,3 +422,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/syscall.c b/linux-user/syscall.c index 7b77503f94..acb004f035 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -5453,6 +5453,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 +5486,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]) { From 405b49150425a3570de4eab99870498d14712b11 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 7 Dec 2016 11:31:46 -0800 Subject: [PATCH 04/25] linux-user: Add SIOCGPGRP, SIOCGSTAMP, SIOCGSTAMPNS Mirror syscall_defs.h for the element type of struct timeval and struct timespec, even though that's not 100% accurate for each guest. Signed-off-by: Helge Deller [rth: Changed the MK_ARRAY types as per above; added ioctl.h entries.] Signed-off-by: Richard Henderson --- linux-user/ioctls.h | 3 +++ linux-user/syscall_defs.h | 4 ++++ linux-user/syscall_types.h | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index eaf6fe61b0..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) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 7607df8cce..55ae367d05 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -930,9 +930,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 */ 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, From fe8ed7d5794f6cecc69bb31ab1291e2356d31bcd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 5 Dec 2016 13:08:12 -0800 Subject: [PATCH 05/25] linux-user: Handle ERFKILL and EHWPOISON With definitions for generic, alpha and mips taken from 4.9-rc2. Signed-off-by: Richard Henderson --- linux-user/alpha/target_syscall.h | 2 ++ linux-user/errno_defs.h | 3 +++ linux-user/mips/target_syscall.h | 5 +++++ linux-user/mips64/target_syscall.h | 5 +++++ linux-user/syscall.c | 6 ++++++ 5 files changed, 21 insertions(+) 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/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/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/syscall.c b/linux-user/syscall.c index acb004f035..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) From b5c375e2faf0e4958c567e8fe5990ccdfd35c2bd Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 7 Dec 2016 11:32:58 -0800 Subject: [PATCH 06/25] linux-user: Add some hppa ioctls Add F_GETLK, F_SETLK, F_SETLKW, F_GETOWN, F_SETOWN, F_SETSIG, F_GETSIG, F_GETLK64, F_SETLK64 and F_SETLKW64 for hppa. Signed-off-by: Helge Deller Signed-off-by: Richard Henderson --- linux-user/syscall_defs.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 55ae367d05..957b737e2f 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2199,6 +2199,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 @@ -2221,13 +2227,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 From 8b4c32ae43949068550525feef95444143cf9dcd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 09:49:25 -0800 Subject: [PATCH 07/25] linux-user: Add HPPA socket.h definitions Split this out into a "cpu/sockbits.h" file now, like we ought to do for all of the other targets. Signed-off-by: Richard Henderson --- linux-user/hppa/sockbits.h | 97 ++++++++++++++++++++++++++++++++++++++ linux-user/socket.h | 2 + 2 files changed, 99 insertions(+) create mode 100644 linux-user/hppa/sockbits.h 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/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 #else #if defined(TARGET_SPARC) From 8ee78dece0d2de9f6c7d83e7358013b8293db209 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 09:53:28 -0800 Subject: [PATCH 08/25] linux-user: Add HPPA syscall numbers Signed-off-by: Richard Henderson --- linux-user/hppa/syscall_nr.h | 353 +++++++++++++++++++++++++++++++++++ 1 file changed, 353 insertions(+) create mode 100644 linux-user/hppa/syscall_nr.h 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 From 9cad44ac6cb339d4a7ceec9f5ca771f7e3031d81 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 09:54:13 -0800 Subject: [PATCH 09/25] linux-user: Add HPPA termbits.h Signed-off-by: Richard Henderson --- linux-user/hppa/termbits.h | 219 +++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 linux-user/hppa/termbits.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 */ From b9490f5a9417f6aba1a131bf568581e86a7e7b2a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 09:55:12 -0800 Subject: [PATCH 10/25] linux-user: Add HPPA target_syscall.h Which is primarily a re-definition of errno numbers. Signed-off-by: Richard Henderson --- linux-user/hppa/target_syscall.h | 237 +++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) create mode 100644 linux-user/hppa/target_syscall.h 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 */ From a10d1e50d9f964c049ffd5af1cc30180be2989d1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 09:56:41 -0800 Subject: [PATCH 11/25] linux-user: Add HPPA definitions to syscall_defs.h --- linux-user/syscall_defs.h | 131 +++++++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 1 deletion(-) diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 957b737e2f..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 */ @@ -1279,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 */ @@ -2029,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 @@ -2273,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 */ From 22991d19ab62bc48c2ef825b1278f2920005f9b4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 09:57:04 -0800 Subject: [PATCH 12/25] linux-user: Add HPPA target_structs.h Signed-off-by: Richard Henderson --- linux-user/hppa/target_structs.h | 54 ++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 linux-user/hppa/target_structs.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 . + */ +#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 From 925ccf32364026f17764c454d4c5ecec3c4310ba Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 09:58:14 -0800 Subject: [PATCH 13/25] linux-user: Add HPPA target_signal.h and target_cpu.h The cpu.h structure that these manipulate hasn't been defined yet, but we haven't enabled compilation yet either. Signed-off-by: Richard Henderson --- linux-user/hppa/target_cpu.h | 35 +++++++++++++++++++++++++++++++++ linux-user/hppa/target_signal.h | 29 +++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 linux-user/hppa/target_cpu.h create mode 100644 linux-user/hppa/target_signal.h 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 . + */ +#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 */ From 1659e38e1d40b8d6b39d8a9c664a2db2df3c6865 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 09:59:01 -0800 Subject: [PATCH 14/25] linux-user: Add HPPA signal handling Signed-off-by: Richard Henderson --- linux-user/signal.c | 191 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 190 insertions(+), 1 deletion(-) 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 From 7c248bcda1d1b3081b1bdb3a10352bb549dff2c5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 10:01:00 -0800 Subject: [PATCH 15/25] linux-user: Add HPPA startup and main loop Including support for the atomic memory op syscalls. Signed-off-by: Richard Henderson --- linux-user/elfload.c | 24 ++++++ linux-user/main.c | 172 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 5cea39d172..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 diff --git a/linux-user/main.c b/linux-user/main.c index aae29bd94d..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) @@ -4539,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 From 005fa38d86257d471ac461c066a5409a9f5ebb02 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 10:46:40 -0800 Subject: [PATCH 16/25] target-hppa: Add softfloat specializations Like the original MIPS, HPPA has the MSB of an SNaN set. However, it has different rules for silencing an SNaN: (1) msb is cleared and (2) msb-1 must be set if the fraction is now zero, and (implementation defined) may be set always. I haven't checked real hardware but chose the set always alternative because it's easy and within spec. Signed-off-by: Richard Henderson --- fpu/softfloat-specialize.h | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) 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); From 61766fe9e2d37ac4928119eabfe2250bd8f43b11 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 11:26:14 -0800 Subject: [PATCH 17/25] target-hppa: Add framework and enable compilation This is just about the minimum required to enable compilation without actually executing any instructions. This contains the HPPACPU structure and the required callbacks, the gdbstub, the basic translation loop, and a translate_one function that always results in an illegal instruction. Signed-off-by: Richard Henderson --- MAINTAINERS | 6 + configure | 6 +- default-configs/hppa-linux-user.mak | 1 + target/hppa/Makefile.objs | 1 + target/hppa/cpu-qom.h | 52 ++++ target/hppa/cpu.c | 164 +++++++++++ target/hppa/cpu.h | 144 ++++++++++ target/hppa/gdbstub.c | 111 +++++++ target/hppa/helper.c | 137 +++++++++ target/hppa/helper.h | 3 + target/hppa/op_helper.c | 65 +++++ target/hppa/translate.c | 429 ++++++++++++++++++++++++++++ 12 files changed, 1116 insertions(+), 3 deletions(-) create mode 100644 default-configs/hppa-linux-user.mak create mode 100644 target/hppa/Makefile.objs create mode 100644 target/hppa/cpu-qom.h create mode 100644 target/hppa/cpu.c create mode 100644 target/hppa/cpu.h create mode 100644 target/hppa/gdbstub.c create mode 100644 target/hppa/helper.c create mode 100644 target/hppa/helper.h create mode 100644 target/hppa/op_helper.c create mode 100644 target/hppa/translate.c diff --git a/MAINTAINERS b/MAINTAINERS index b5ebfab4c3..44c42fb764 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 +S: Maintained +F: target/hppa/ +F: disas/hppa.c + LM32 M: Michael Walle S: Maintained diff --git a/configure b/configure index 494c9cca3f..90f01e8a35 100755 --- a/configure +++ b/configure @@ -510,8 +510,6 @@ elif check_define __arm__ ; then cpu="arm" elif check_define __aarch64__ ; then cpu="aarch64" -elif check_define __hppa__ ; then - cpu="hppa" else cpu=$(uname -m) fi @@ -5847,7 +5845,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 @@ -5910,6 +5908,8 @@ case "$target_name" in ;; cris) ;; + hppa) + ;; lm32) ;; m68k) 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 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 + * + * 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 + * + */ +#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 + * + * 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 + * + */ + +#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 + * + * 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 . + */ + +#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 + * + * 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 . + */ + +#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 + * + * 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 . + */ + +#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 = ""; + + 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..9c94dacb3e --- /dev/null +++ b/target/hppa/helper.h @@ -0,0 +1,3 @@ +DEF_HELPER_2(excp, noreturn, env, int) + +DEF_HELPER_FLAGS_1(loaded_fr0, TCG_CALL_NO_RWG, void, env) diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c new file mode 100644 index 0000000000..4dd0119424 --- /dev/null +++ b/target/hppa/op_helper.c @@ -0,0 +1,65 @@ +/* + * Helpers for HPPA instructions. + * + * 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 . + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/helper-proto.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); +} + +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); +} diff --git a/target/hppa/translate.c b/target/hppa/translate.c new file mode 100644 index 0000000000..8d61853193 --- /dev/null +++ b/target/hppa/translate.c @@ -0,0 +1,429 @@ +/* + * HPPA emulation cpu translation for qemu. + * + * 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 . + */ + +#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); +} 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 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) { + return get_temp(ctx); + } else { + return cpu_gr[reg]; + } +} + +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); + gen_excp_1(exception); + return EXIT_NORETURN; +} + +static ExitStatus gen_illegal(DisasContext *ctx) +{ + return 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; +} + +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); + } + } +} + +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) { + 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); + + 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(); + } + + { + /* 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); + } + + ret = translate_one(&ctx, insn); + } + + 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())) { + 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); + 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); + /* 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(); + qemu_log("IN: %s\n", lookup_symbol(tb->pc)); + log_target_disas(cs, tb->pc, tb->size, 1); + qemu_log("\n"); + 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; +} From 129e9cc3a1792b66bd189838d58f7a2944602a82 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 11:58:17 -0800 Subject: [PATCH 18/25] target-hppa: Add nullification framework The HPPA cpu has a unique form of predicated execution in which almost any instruction can set the PSW[N] (or "nullify") bit, which suppresses execution (and even decoding) of the following instruction. Execution of a nullified insn clears the PSW[N] bit. This adds a generic framework for branching over nullified insns, or for sufficiently simple insns, transforming the writeback of the result to a conditional move. In the process, we want to be able to represent PSW[N] as a TCG condition, which implies management of the related tcg temps. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 228 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 224 insertions(+), 4 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 8d61853193..22dfb73cb4 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -151,6 +151,78 @@ void hppa_translate_init(void) } } +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++; @@ -178,13 +250,125 @@ static TCGv load_gpr(DisasContext *ctx, unsigned reg) static TCGv dest_gpr(DisasContext *ctx, unsigned reg) { - if (reg == 0) { + 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); + } +} + +/* 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)) { @@ -210,13 +394,15 @@ 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) { - return gen_excp(ctx, EXCP_SIGILL); + nullify_over(ctx); + return nullify_end(ctx, gen_excp(ctx, EXCP_SIGILL)); } static bool use_goto_tb(DisasContext *ctx, target_ulong dest) @@ -228,6 +414,16 @@ static bool use_goto_tb(DisasContext *ctx, target_ulong dest) 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) { @@ -308,6 +504,15 @@ void gen_intermediate_code(CPUHPPAState *env, struct TranslationBlock *tb) 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++; @@ -336,7 +541,13 @@ void gen_intermediate_code(CPUHPPAState *env, struct TranslationBlock *tb) TCGV_UNUSED(ctx.iaoq_n_var); } - ret = translate_one(&ctx, insn); + 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) { @@ -354,7 +565,14 @@ void gen_intermediate_code(CPUHPPAState *env, struct TranslationBlock *tb) && (ctx.iaoq_b != ctx.iaoq_f + 4 || num_insns >= max_insns || tcg_op_buf_full())) { - ret = EXIT_IAQ_N_STALE; + 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; @@ -367,6 +585,7 @@ void gen_intermediate_code(CPUHPPAState *env, struct TranslationBlock *tb) 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; } @@ -386,6 +605,7 @@ void gen_intermediate_code(CPUHPPAState *env, struct TranslationBlock *tb) 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) { From b2167459ae46bcaa07a84fc7e9df1743000be81f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 13:37:23 -0800 Subject: [PATCH 19/25] target-hppa: Implement basic arithmetic Signed-off-by: Richard Henderson --- target/hppa/helper.h | 2 + target/hppa/op_helper.c | 23 ++ target/hppa/translate.c | 882 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 907 insertions(+) diff --git a/target/hppa/helper.h b/target/hppa/helper.h index 9c94dacb3e..ecff17c0f3 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -1,3 +1,5 @@ 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_1(loaded_fr0, TCG_CALL_NO_RWG, void, env) diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index 4dd0119424..f36ce74163 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -31,6 +31,29 @@ void QEMU_NORETURN HELPER(excp)(CPUHPPAState *env, int 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()); + } +} + void HELPER(loaded_fr0)(CPUHPPAState *env) { uint32_t shadow = env->fr[0] >> 32; diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 22dfb73cb4..2ad651c126 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -83,6 +83,9 @@ 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); + }; } DisasInsn; /* global register indexes */ @@ -443,6 +446,870 @@ static void gen_goto_tb(DisasContext *ctx, int which, } } +/* 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 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_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; +} + +/* 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 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; +} + +static ExitStatus trans_nop(DisasContext *ctx, uint32_t insn, + const DisasInsn *di) +{ + cond_free(&ctx->null_cond); + return NO_EXIT; +} + +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_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 translate_table_int(DisasContext *ctx, uint32_t insn, const DisasInsn table[], size_t n) { @@ -463,6 +1330,21 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) uint32_t opc = extract32(insn, 26, 6); switch (opc) { + case 0x02: + return translate_table(ctx, insn, table_arith_log); + case 0x08: + return trans_ldil(ctx, insn); + case 0x0A: + return trans_addil(ctx, insn); + case 0x0D: + return trans_ldo(ctx, insn); + case 0x24: + return trans_cmpiclr(ctx, insn); + case 0x25: + return trans_subi(ctx, insn); + case 0x2C: + case 0x2D: + return trans_addi(ctx, insn); default: break; } From 98cd9ca7cc560414ad4a79e7e9aee568d10f985e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 13:50:28 -0800 Subject: [PATCH 20/25] target-hppa: Implement branches Signed-off-by: Richard Henderson --- target/hppa/translate.c | 477 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 477 insertions(+) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 2ad651c126..1d0976f756 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -455,6 +455,14 @@ static target_long low_sextract(uint32_t val, int pos, int len) return x; } +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 @@ -463,6 +471,15 @@ static target_long assemble_16(uint32_t insn) return low_sextract(insn, 0, 14); } +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); @@ -473,6 +490,15 @@ static target_long assemble_21(uint32_t insn) 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. @@ -574,6 +600,24 @@ static DisasCond do_log_cond(unsigned cf, TCGv res) 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) @@ -897,6 +941,188 @@ static ExitStatus do_unit(DisasContext *ctx, unsigned rt, TCGv in1, return 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; +} + static ExitStatus trans_nop(DisasContext *ctx, uint32_t insn, const DisasInsn *di) { @@ -1310,6 +1536,224 @@ static ExitStatus trans_ldo(DisasContext *ctx, uint32_t insn) return NO_EXIT; } +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_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 translate_table_int(DisasContext *ctx, uint32_t insn, const DisasInsn table[], size_t n) { @@ -1338,13 +1782,46 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) return trans_addil(ctx, insn); case 0x0D: return trans_ldo(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 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 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 0x38: + return trans_be(ctx, insn, false); + case 0x39: + return trans_be(ctx, insn, true); + case 0x3A: + return translate_table(ctx, insn, table_branch); default: break; } From 7ad439df5616fd1854a58ef7bf87c7a092cd34ce Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 13:55:46 -0800 Subject: [PATCH 21/25] target-hppa: Implement linux-user gateway page For linux, page 0 is mapped as an execute-only gateway. A gateway page is a special bit in the page table that allows a B,GATE insn within that page to raise processor permissions. This is how system calls are implemented for HPPA. Rather than actually map anything here, or handle permissions at all, implement the semantics of the actual linux syscall entry points. Signed-off-by: Richard Henderson --- target/hppa/translate.c | 85 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 81 insertions(+), 4 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 1d0976f756..14fe4bbd2c 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1123,6 +1123,64 @@ static ExitStatus do_ibranch(DisasContext *ctx, TCGv dest, 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) { @@ -1884,7 +1942,10 @@ void gen_intermediate_code(CPUHPPAState *env, struct TranslationBlock *tb) 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); @@ -1986,9 +2047,25 @@ void gen_intermediate_code(CPUHPPAState *env, struct TranslationBlock *tb) if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM) && qemu_log_in_addr_range(tb->pc)) { qemu_log_lock(); - qemu_log("IN: %s\n", lookup_symbol(tb->pc)); - log_target_disas(cs, tb->pc, tb->size, 1); - qemu_log("\n"); + 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 From 0b1347d259460a633e07e6bdae312992862238ba Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 14:04:13 -0800 Subject: [PATCH 22/25] target-hppa: Implement shifts and deposits Signed-off-by: Richard Henderson --- target/hppa/translate.c | 309 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 309 insertions(+) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 14fe4bbd2c..093a65e8b1 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1719,6 +1719,311 @@ static ExitStatus trans_movb(DisasContext *ctx, uint32_t insn, bool is_imm) 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); @@ -1874,6 +2179,10 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) 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: From 96d6407f36346aa6ea706905fc179811f49b6569 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 14:54:51 -0800 Subject: [PATCH 23/25] target-hppa: Implement loads and stores Signed-off-by: Richard Henderson --- target/hppa/helper.h | 3 + target/hppa/op_helper.c | 78 +++++ target/hppa/translate.c | 618 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 699 insertions(+) diff --git a/target/hppa/helper.h b/target/hppa/helper.h index ecff17c0f3..88db719818 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -2,4 +2,7 @@ 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(loaded_fr0, TCG_CALL_NO_RWG, void, env) diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index f36ce74163..0aa5fb99d4 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -21,6 +21,7 @@ #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) { @@ -54,6 +55,83 @@ void HELPER(tcond)(CPUHPPAState *env, target_ulong cond) } } +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; + } +} + void HELPER(loaded_fr0)(CPUHPPAState *env) { uint32_t shadow = env->fr[0] >> 32; diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 093a65e8b1..1973777597 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -278,6 +278,45 @@ static void save_gpr(DisasContext *ctx, unsigned reg, TCGv 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 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 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) @@ -471,6 +510,16 @@ static target_long assemble_16(uint32_t insn) 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); @@ -941,6 +990,234 @@ static ExitStatus do_unit(DisasContext *ctx, unsigned rt, TCGv in1, 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); +} + /* 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, @@ -1547,6 +1824,149 @@ static ExitStatus trans_cmpiclr(DisasContext *ctx, uint32_t insn) 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); @@ -1594,6 +2014,160 @@ static ExitStatus trans_ldo(DisasContext *ctx, uint32_t insn) 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) { @@ -2139,12 +2713,44 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) switch (opc) { case 0x02: return translate_table(ctx, insn, table_arith_log); + case 0x03: + return translate_table(ctx, insn, table_index_mem); 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 0x0D: return trans_ldo(ctx, insn); + + 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: @@ -2172,6 +2778,7 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) return trans_addi(ctx, insn); case 0x2F: return trans_cmpb(ctx, insn, false, false, true); + case 0x30: case 0x31: return trans_bb(ctx, insn); @@ -2189,6 +2796,17 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) 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; } From 98a9cb792c8c177d5d81c2c4a08e740deeb207fd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 14:59:03 -0800 Subject: [PATCH 24/25] target-hppa: Implement system and memory-management insns Signed-off-by: Richard Henderson --- target/hppa/helper.h | 3 + target/hppa/op_helper.c | 10 ++ target/hppa/translate.c | 206 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 219 insertions(+) diff --git a/target/hppa/helper.h b/target/hppa/helper.h index 88db719818..d51cf6d33f 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -5,4 +5,7 @@ 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) diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index 0aa5fb99d4..670e600bcd 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -132,6 +132,16 @@ void HELPER(stby_e)(CPUHPPAState *env, target_ulong addr, target_ulong val) } } +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; diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 1973777597..cfdb9ee761 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1465,6 +1465,208 @@ static ExitStatus trans_nop(DisasContext *ctx, uint32_t insn, 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) { @@ -2711,6 +2913,10 @@ 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: From ebe9383caefd56d519e965a5d87bca29f0aeffe3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 15 Dec 2016 15:04:19 -0800 Subject: [PATCH 25/25] target-hppa: Implement floating-point insns Signed-off-by: Richard Henderson --- target/hppa/helper.h | 55 +++ target/hppa/op_helper.c | 394 ++++++++++++++++++++++ target/hppa/translate.c | 728 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1177 insertions(+) diff --git a/target/hppa/helper.h b/target/hppa/helper.h index d51cf6d33f..789f07fc0a 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -9,3 +9,58 @@ 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 index 670e600bcd..c05c0d5572 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -174,3 +174,397 @@ 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 index cfdb9ee761..4d243f7d3d 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -85,6 +85,12 @@ typedef struct DisasInsn { 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; @@ -295,6 +301,28 @@ static TCGv_i32 load_frw_i32(unsigned rt) 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, @@ -312,6 +340,15 @@ static TCGv_i64 load_frd(unsigned 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])); @@ -494,6 +531,35 @@ static target_long low_sextract(uint32_t val, int pos, int len) 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); @@ -1218,6 +1284,110 @@ static ExitStatus do_fstored(DisasContext *ctx, unsigned rt, unsigned rb, 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, @@ -2893,6 +3063,554 @@ static const DisasInsn table_branch[] = { { 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) { @@ -2921,6 +3639,8 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) 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: @@ -2929,8 +3649,12 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) 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); @@ -2969,6 +3693,8 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) 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: @@ -2982,6 +3708,8 @@ static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) 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);