| /* Target dependent code for CRIS, for GDB, the GNU debugger. |
| Copyright 2001, 2002, 2003 Free Software Foundation, Inc. |
| Contributed by Axis Communications AB. |
| Written by Hendrik Ruijter, Stefan Andersson, and Orjan Friberg. |
| |
| This file is part of GDB. |
| |
| This program is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 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, write to the Free Software |
| Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ |
| |
| #include "defs.h" |
| #include "frame.h" |
| #include "symtab.h" |
| #include "inferior.h" |
| #include "gdbtypes.h" |
| #include "gdbcore.h" |
| #include "gdbcmd.h" |
| #include "target.h" |
| #include "value.h" |
| #include "opcode/cris.h" |
| #include "arch-utils.h" |
| #include "regcache.h" |
| |
| /* To get entry_point_address. */ |
| #include "symfile.h" |
| |
| #include "solib.h" /* Support for shared libraries. */ |
| #include "solib-svr4.h" /* For struct link_map_offsets. */ |
| #include "gdb_string.h" |
| |
| |
| enum cris_num_regs |
| { |
| /* There are no floating point registers. Used in gdbserver low-linux.c. */ |
| NUM_FREGS = 0, |
| |
| /* There are 16 general registers. */ |
| NUM_GENREGS = 16, |
| |
| /* There are 16 special registers. */ |
| NUM_SPECREGS = 16 |
| }; |
| |
| /* Register numbers of various important registers. |
| DEPRECATED_FP_REGNUM Contains address of executing stack frame. |
| STR_REGNUM Contains the address of structure return values. |
| RET_REGNUM Contains the return value when shorter than or equal to 32 bits |
| ARG1_REGNUM Contains the first parameter to a function. |
| ARG2_REGNUM Contains the second parameter to a function. |
| ARG3_REGNUM Contains the third parameter to a function. |
| ARG4_REGNUM Contains the fourth parameter to a function. Rest on stack. |
| SP_REGNUM Contains address of top of stack. |
| PC_REGNUM Contains address of next instruction. |
| SRP_REGNUM Subroutine return pointer register. |
| BRP_REGNUM Breakpoint return pointer register. */ |
| |
| /* DEPRECATED_FP_REGNUM = 8, SP_REGNUM = 14, and PC_REGNUM = 15 have |
| been incorporated into the multi-arch framework. */ |
| |
| enum cris_regnums |
| { |
| /* Enums with respect to the general registers, valid for all |
| CRIS versions. */ |
| STR_REGNUM = 9, |
| RET_REGNUM = 10, |
| ARG1_REGNUM = 10, |
| ARG2_REGNUM = 11, |
| ARG3_REGNUM = 12, |
| ARG4_REGNUM = 13, |
| |
| /* Enums with respect to the special registers, some of which may not be |
| applicable to all CRIS versions. */ |
| P0_REGNUM = 16, |
| VR_REGNUM = 17, |
| P2_REGNUM = 18, |
| P3_REGNUM = 19, |
| P4_REGNUM = 20, |
| CCR_REGNUM = 21, |
| MOF_REGNUM = 23, |
| P8_REGNUM = 24, |
| IBR_REGNUM = 25, |
| IRP_REGNUM = 26, |
| SRP_REGNUM = 27, |
| BAR_REGNUM = 28, |
| DCCR_REGNUM = 29, |
| BRP_REGNUM = 30, |
| USP_REGNUM = 31 |
| }; |
| |
| extern const struct cris_spec_reg cris_spec_regs[]; |
| |
| /* CRIS version, set via the user command 'set cris-version'. Affects |
| register names and sizes.*/ |
| static int usr_cmd_cris_version; |
| |
| /* Indicates whether to trust the above variable. */ |
| static int usr_cmd_cris_version_valid = 0; |
| |
| /* CRIS mode, set via the user command 'set cris-mode'. Affects availability |
| of some registers. */ |
| static const char *usr_cmd_cris_mode; |
| |
| /* Indicates whether to trust the above variable. */ |
| static int usr_cmd_cris_mode_valid = 0; |
| |
| static const char CRIS_MODE_USER[] = "CRIS_MODE_USER"; |
| static const char CRIS_MODE_SUPERVISOR[] = "CRIS_MODE_SUPERVISOR"; |
| static const char *cris_mode_enums[] = |
| { |
| CRIS_MODE_USER, |
| CRIS_MODE_SUPERVISOR, |
| 0 |
| }; |
| |
| /* CRIS ABI, set via the user command 'set cris-abi'. |
| There are two flavours: |
| 1. Original ABI with 32-bit doubles, where arguments <= 4 bytes are |
| passed by value. |
| 2. New ABI with 64-bit doubles, where arguments <= 8 bytes are passed by |
| value. */ |
| static const char *usr_cmd_cris_abi; |
| |
| /* Indicates whether to trust the above variable. */ |
| static int usr_cmd_cris_abi_valid = 0; |
| |
| /* These variables are strings instead of enums to make them usable as |
| parameters to add_set_enum_cmd. */ |
| static const char CRIS_ABI_ORIGINAL[] = "CRIS_ABI_ORIGINAL"; |
| static const char CRIS_ABI_V2[] = "CRIS_ABI_V2"; |
| static const char CRIS_ABI_SYMBOL[] = ".$CRIS_ABI_V2"; |
| static const char *cris_abi_enums[] = |
| { |
| CRIS_ABI_ORIGINAL, |
| CRIS_ABI_V2, |
| 0 |
| }; |
| |
| /* CRIS architecture specific information. */ |
| struct gdbarch_tdep |
| { |
| int cris_version; |
| const char *cris_mode; |
| const char *cris_abi; |
| }; |
| |
| /* Functions for accessing target dependent data. */ |
| |
| static int |
| cris_version (void) |
| { |
| return (gdbarch_tdep (current_gdbarch)->cris_version); |
| } |
| |
| static const char * |
| cris_mode (void) |
| { |
| return (gdbarch_tdep (current_gdbarch)->cris_mode); |
| } |
| |
| static const char * |
| cris_abi (void) |
| { |
| return (gdbarch_tdep (current_gdbarch)->cris_abi); |
| } |
| |
| /* For saving call-clobbered contents in R9 when returning structs. */ |
| static CORE_ADDR struct_return_address; |
| |
| struct frame_extra_info |
| { |
| CORE_ADDR return_pc; |
| int leaf_function; |
| }; |
| |
| /* The instruction environment needed to find single-step breakpoints. */ |
| typedef |
| struct instruction_environment |
| { |
| unsigned long reg[NUM_GENREGS]; |
| unsigned long preg[NUM_SPECREGS]; |
| unsigned long branch_break_address; |
| unsigned long delay_slot_pc; |
| unsigned long prefix_value; |
| int branch_found; |
| int prefix_found; |
| int invalid; |
| int slot_needed; |
| int delay_slot_pc_active; |
| int xflag_found; |
| int disable_interrupt; |
| } inst_env_type; |
| |
| /* Save old breakpoints in order to restore the state before a single_step. |
| At most, two breakpoints will have to be remembered. */ |
| typedef |
| char binsn_quantum[BREAKPOINT_MAX]; |
| static binsn_quantum break_mem[2]; |
| static CORE_ADDR next_pc = 0; |
| static CORE_ADDR branch_target_address = 0; |
| static unsigned char branch_break_inserted = 0; |
| |
| /* Machine-dependencies in CRIS for opcodes. */ |
| |
| /* Instruction sizes. */ |
| enum cris_instruction_sizes |
| { |
| INST_BYTE_SIZE = 0, |
| INST_WORD_SIZE = 1, |
| INST_DWORD_SIZE = 2 |
| }; |
| |
| /* Addressing modes. */ |
| enum cris_addressing_modes |
| { |
| REGISTER_MODE = 1, |
| INDIRECT_MODE = 2, |
| AUTOINC_MODE = 3 |
| }; |
| |
| /* Prefix addressing modes. */ |
| enum cris_prefix_addressing_modes |
| { |
| PREFIX_INDEX_MODE = 2, |
| PREFIX_ASSIGN_MODE = 3, |
| |
| /* Handle immediate byte offset addressing mode prefix format. */ |
| PREFIX_OFFSET_MODE = 2 |
| }; |
| |
| /* Masks for opcodes. */ |
| enum cris_opcode_masks |
| { |
| BRANCH_SIGNED_SHORT_OFFSET_MASK = 0x1, |
| SIGNED_EXTEND_BIT_MASK = 0x2, |
| SIGNED_BYTE_MASK = 0x80, |
| SIGNED_BYTE_EXTEND_MASK = 0xFFFFFF00, |
| SIGNED_WORD_MASK = 0x8000, |
| SIGNED_WORD_EXTEND_MASK = 0xFFFF0000, |
| SIGNED_DWORD_MASK = 0x80000000, |
| SIGNED_QUICK_VALUE_MASK = 0x20, |
| SIGNED_QUICK_VALUE_EXTEND_MASK = 0xFFFFFFC0 |
| }; |
| |
| /* Functions for opcodes. The general form of the ETRAX 16-bit instruction: |
| Bit 15 - 12 Operand2 |
| 11 - 10 Mode |
| 9 - 6 Opcode |
| 5 - 4 Size |
| 3 - 0 Operand1 */ |
| |
| static int |
| cris_get_operand2 (unsigned short insn) |
| { |
| return ((insn & 0xF000) >> 12); |
| } |
| |
| static int |
| cris_get_mode (unsigned short insn) |
| { |
| return ((insn & 0x0C00) >> 10); |
| } |
| |
| static int |
| cris_get_opcode (unsigned short insn) |
| { |
| return ((insn & 0x03C0) >> 6); |
| } |
| |
| static int |
| cris_get_size (unsigned short insn) |
| { |
| return ((insn & 0x0030) >> 4); |
| } |
| |
| static int |
| cris_get_operand1 (unsigned short insn) |
| { |
| return (insn & 0x000F); |
| } |
| |
| /* Additional functions in order to handle opcodes. */ |
| |
| static int |
| cris_get_wide_opcode (unsigned short insn) |
| { |
| return ((insn & 0x03E0) >> 5); |
| } |
| |
| static int |
| cris_get_short_size (unsigned short insn) |
| { |
| return ((insn & 0x0010) >> 4); |
| } |
| |
| static int |
| cris_get_quick_value (unsigned short insn) |
| { |
| return (insn & 0x003F); |
| } |
| |
| static int |
| cris_get_bdap_quick_offset (unsigned short insn) |
| { |
| return (insn & 0x00FF); |
| } |
| |
| static int |
| cris_get_branch_short_offset (unsigned short insn) |
| { |
| return (insn & 0x00FF); |
| } |
| |
| static int |
| cris_get_asr_shift_steps (unsigned long value) |
| { |
| return (value & 0x3F); |
| } |
| |
| static int |
| cris_get_asr_quick_shift_steps (unsigned short insn) |
| { |
| return (insn & 0x1F); |
| } |
| |
| static int |
| cris_get_clear_size (unsigned short insn) |
| { |
| return ((insn) & 0xC000); |
| } |
| |
| static int |
| cris_is_signed_extend_bit_on (unsigned short insn) |
| { |
| return (((insn) & 0x20) == 0x20); |
| } |
| |
| static int |
| cris_is_xflag_bit_on (unsigned short insn) |
| { |
| return (((insn) & 0x1000) == 0x1000); |
| } |
| |
| static void |
| cris_set_size_to_dword (unsigned short *insn) |
| { |
| *insn &= 0xFFCF; |
| *insn |= 0x20; |
| } |
| |
| static signed char |
| cris_get_signed_offset (unsigned short insn) |
| { |
| return ((signed char) (insn & 0x00FF)); |
| } |
| |
| /* Calls an op function given the op-type, working on the insn and the |
| inst_env. */ |
| static void cris_gdb_func (enum cris_op_type, unsigned short, inst_env_type *); |
| |
| static CORE_ADDR cris_skip_prologue_main (CORE_ADDR pc, int frameless_p); |
| |
| static struct gdbarch *cris_gdbarch_init (struct gdbarch_info, |
| struct gdbarch_list *); |
| |
| static int cris_delayed_get_disassembler (bfd_vma, disassemble_info *); |
| |
| static void cris_dump_tdep (struct gdbarch *, struct ui_file *); |
| |
| static void cris_version_update (char *ignore_args, int from_tty, |
| struct cmd_list_element *c); |
| |
| static void cris_mode_update (char *ignore_args, int from_tty, |
| struct cmd_list_element *c); |
| |
| static void cris_abi_update (char *ignore_args, int from_tty, |
| struct cmd_list_element *c); |
| |
| static CORE_ADDR bfd_lookup_symbol (bfd *, const char *); |
| |
| /* Frames information. The definition of the struct frame_info is |
| |
| CORE_ADDR frame |
| CORE_ADDR pc |
| enum frame_type type; |
| CORE_ADDR return_pc |
| int leaf_function |
| |
| If the compilation option -fno-omit-frame-pointer is present the |
| variable frame will be set to the content of R8 which is the frame |
| pointer register. |
| |
| The variable pc contains the address where execution is performed |
| in the present frame. The innermost frame contains the current content |
| of the register PC. All other frames contain the content of the |
| register PC in the next frame. |
| |
| The variable `type' indicates the frame's type: normal, SIGTRAMP |
| (associated with a signal handler), dummy (associated with a dummy |
| frame). |
| |
| The variable return_pc contains the address where execution should be |
| resumed when the present frame has finished, the return address. |
| |
| The variable leaf_function is 1 if the return address is in the register |
| SRP, and 0 if it is on the stack. |
| |
| Prologue instructions C-code. |
| The prologue may consist of (-fno-omit-frame-pointer) |
| 1) 2) |
| push srp |
| push r8 push r8 |
| move.d sp,r8 move.d sp,r8 |
| subq X,sp subq X,sp |
| movem rY,[sp] movem rY,[sp] |
| move.S rZ,[r8-U] move.S rZ,[r8-U] |
| |
| where 1 is a non-terminal function, and 2 is a leaf-function. |
| |
| Note that this assumption is extremely brittle, and will break at the |
| slightest change in GCC's prologue. |
| |
| If local variables are declared or register contents are saved on stack |
| the subq-instruction will be present with X as the number of bytes |
| needed for storage. The reshuffle with respect to r8 may be performed |
| with any size S (b, w, d) and any of the general registers Z={0..13}. |
| The offset U should be representable by a signed 8-bit value in all cases. |
| Thus, the prefix word is assumed to be immediate byte offset mode followed |
| by another word containing the instruction. |
| |
| Degenerate cases: |
| 3) |
| push r8 |
| move.d sp,r8 |
| move.d r8,sp |
| pop r8 |
| |
| Prologue instructions C++-code. |
| Case 1) and 2) in the C-code may be followed by |
| |
| move.d r10,rS ; this |
| move.d r11,rT ; P1 |
| move.d r12,rU ; P2 |
| move.d r13,rV ; P3 |
| move.S [r8+U],rZ ; P4 |
| |
| if any of the call parameters are stored. The host expects these |
| instructions to be executed in order to get the call parameters right. */ |
| |
| /* Examine the prologue of a function. The variable ip is the address of |
| the first instruction of the prologue. The variable limit is the address |
| of the first instruction after the prologue. The variable fi contains the |
| information in struct frame_info. The variable frameless_p controls whether |
| the entire prologue is examined (0) or just enough instructions to |
| determine that it is a prologue (1). */ |
| |
| CORE_ADDR |
| cris_examine (CORE_ADDR ip, CORE_ADDR limit, struct frame_info *fi, |
| int frameless_p) |
| { |
| /* Present instruction. */ |
| unsigned short insn; |
| |
| /* Next instruction, lookahead. */ |
| unsigned short insn_next; |
| int regno; |
| |
| /* Is there a push fp? */ |
| int have_fp; |
| |
| /* Number of byte on stack used for local variables and movem. */ |
| int val; |
| |
| /* Highest register number in a movem. */ |
| int regsave; |
| |
| /* move.d r<source_register>,rS */ |
| short source_register; |
| |
| /* This frame is with respect to a leaf until a push srp is found. */ |
| get_frame_extra_info (fi)->leaf_function = 1; |
| |
| /* This frame is without the FP until a push fp is found. */ |
| have_fp = 0; |
| |
| /* Assume nothing on stack. */ |
| val = 0; |
| regsave = -1; |
| |
| /* No information about register contents so far. */ |
| |
| /* We only want to know the end of the prologue when fi->saved_regs == 0. |
| When the saved registers are allocated full information is required. */ |
| if (get_frame_saved_regs (fi)) |
| { |
| for (regno = 0; regno < NUM_REGS; regno++) |
| get_frame_saved_regs (fi)[regno] = 0; |
| } |
| |
| /* Find the prologue instructions. */ |
| do |
| { |
| insn = read_memory_unsigned_integer (ip, sizeof (short)); |
| ip += sizeof (short); |
| if (insn == 0xE1FC) |
| { |
| /* push <reg> 32 bit instruction */ |
| insn_next = read_memory_unsigned_integer (ip, sizeof (short)); |
| ip += sizeof (short); |
| regno = cris_get_operand2 (insn_next); |
| |
| /* This check, meant to recognize srp, used to be regno == |
| (SRP_REGNUM - NUM_GENREGS), but that covers r11 also. */ |
| if (insn_next == 0xBE7E) |
| { |
| if (frameless_p) |
| { |
| return ip; |
| } |
| get_frame_extra_info (fi)->leaf_function = 0; |
| } |
| else if (regno == DEPRECATED_FP_REGNUM) |
| { |
| have_fp = 1; |
| } |
| } |
| else if (insn == 0x866E) |
| { |
| /* move.d sp,r8 */ |
| if (frameless_p) |
| { |
| return ip; |
| } |
| continue; |
| } |
| else if (cris_get_operand2 (insn) == SP_REGNUM |
| && cris_get_mode (insn) == 0x0000 |
| && cris_get_opcode (insn) == 0x000A) |
| { |
| /* subq <val>,sp */ |
| val = cris_get_quick_value (insn); |
| } |
| else if (cris_get_mode (insn) == 0x0002 |
| && cris_get_opcode (insn) == 0x000F |
| && cris_get_size (insn) == 0x0003 |
| && cris_get_operand1 (insn) == SP_REGNUM) |
| { |
| /* movem r<regsave>,[sp] */ |
| if (frameless_p) |
| { |
| return ip; |
| } |
| regsave = cris_get_operand2 (insn); |
| } |
| else if (cris_get_operand2 (insn) == SP_REGNUM |
| && ((insn & 0x0F00) >> 8) == 0x0001 |
| && (cris_get_signed_offset (insn) < 0)) |
| { |
| /* Immediate byte offset addressing prefix word with sp as base |
| register. Used for CRIS v8 i.e. ETRAX 100 and newer if <val> |
| is between 64 and 128. |
| movem r<regsave>,[sp=sp-<val>] */ |
| val = -cris_get_signed_offset (insn); |
| insn_next = read_memory_unsigned_integer (ip, sizeof (short)); |
| ip += sizeof (short); |
| if (cris_get_mode (insn_next) == PREFIX_ASSIGN_MODE |
| && cris_get_opcode (insn_next) == 0x000F |
| && cris_get_size (insn_next) == 0x0003 |
| && cris_get_operand1 (insn_next) == SP_REGNUM) |
| { |
| if (frameless_p) |
| { |
| return ip; |
| } |
| regsave = cris_get_operand2 (insn_next); |
| } |
| else |
| { |
| /* The prologue ended before the limit was reached. */ |
| ip -= 2 * sizeof (short); |
| break; |
| } |
| } |
| else if (cris_get_mode (insn) == 0x0001 |
| && cris_get_opcode (insn) == 0x0009 |
| && cris_get_size (insn) == 0x0002) |
| { |
| /* move.d r<10..13>,r<0..15> */ |
| if (frameless_p) |
| { |
| return ip; |
| } |
| source_register = cris_get_operand1 (insn); |
| |
| /* FIXME? In the glibc solibs, the prologue might contain something |
| like (this example taken from relocate_doit): |
| move.d $pc,$r0 |
| sub.d 0xfffef426,$r0 |
| which isn't covered by the source_register check below. Question |
| is whether to add a check for this combo, or make better use of |
| the limit variable instead. */ |
| if (source_register < ARG1_REGNUM || source_register > ARG4_REGNUM) |
| { |
| /* The prologue ended before the limit was reached. */ |
| ip -= sizeof (short); |
| break; |
| } |
| } |
| else if (cris_get_operand2 (insn) == DEPRECATED_FP_REGNUM |
| /* The size is a fixed-size. */ |
| && ((insn & 0x0F00) >> 8) == 0x0001 |
| /* A negative offset. */ |
| && (cris_get_signed_offset (insn) < 0)) |
| { |
| /* move.S rZ,[r8-U] (?) */ |
| insn_next = read_memory_unsigned_integer (ip, sizeof (short)); |
| ip += sizeof (short); |
| regno = cris_get_operand2 (insn_next); |
| if ((regno >= 0 && regno < SP_REGNUM) |
| && cris_get_mode (insn_next) == PREFIX_OFFSET_MODE |
| && cris_get_opcode (insn_next) == 0x000F) |
| { |
| /* move.S rZ,[r8-U] */ |
| continue; |
| } |
| else |
| { |
| /* The prologue ended before the limit was reached. */ |
| ip -= 2 * sizeof (short); |
| break; |
| } |
| } |
| else if (cris_get_operand2 (insn) == DEPRECATED_FP_REGNUM |
| /* The size is a fixed-size. */ |
| && ((insn & 0x0F00) >> 8) == 0x0001 |
| /* A positive offset. */ |
| && (cris_get_signed_offset (insn) > 0)) |
| { |
| /* move.S [r8+U],rZ (?) */ |
| insn_next = read_memory_unsigned_integer (ip, sizeof (short)); |
| ip += sizeof (short); |
| regno = cris_get_operand2 (insn_next); |
| if ((regno >= 0 && regno < SP_REGNUM) |
| && cris_get_mode (insn_next) == PREFIX_OFFSET_MODE |
| && cris_get_opcode (insn_next) == 0x0009 |
| && cris_get_operand1 (insn_next) == regno) |
| { |
| /* move.S [r8+U],rZ */ |
| continue; |
| } |
| else |
| { |
| /* The prologue ended before the limit was reached. */ |
| ip -= 2 * sizeof (short); |
| break; |
| } |
| } |
| else |
| { |
| /* The prologue ended before the limit was reached. */ |
| ip -= sizeof (short); |
| break; |
| } |
| } |
| while (ip < limit); |
| |
| /* We only want to know the end of the prologue when |
| fi->saved_regs == 0. */ |
| if (!get_frame_saved_regs (fi)) |
| return ip; |
| |
| if (have_fp) |
| { |
| get_frame_saved_regs (fi)[DEPRECATED_FP_REGNUM] = get_frame_base (fi); |
| |
| /* Calculate the addresses. */ |
| for (regno = regsave; regno >= 0; regno--) |
| { |
| get_frame_saved_regs (fi)[regno] = get_frame_base (fi) - val; |
| val -= 4; |
| } |
| if (get_frame_extra_info (fi)->leaf_function) |
| { |
| /* Set the register SP to contain the stack pointer of |
| the caller. */ |
| get_frame_saved_regs (fi)[SP_REGNUM] = get_frame_base (fi) + 4; |
| } |
| else |
| { |
| /* Set the register SP to contain the stack pointer of |
| the caller. */ |
| get_frame_saved_regs (fi)[SP_REGNUM] = get_frame_base (fi) + 8; |
| |
| /* Set the register SRP to contain the return address of |
| the caller. */ |
| get_frame_saved_regs (fi)[SRP_REGNUM] = get_frame_base (fi) + 4; |
| } |
| } |
| return ip; |
| } |
| |
| /* Advance pc beyond any function entry prologue instructions at pc |
| to reach some "real" code. */ |
| |
| CORE_ADDR |
| cris_skip_prologue (CORE_ADDR pc) |
| { |
| return cris_skip_prologue_main (pc, 0); |
| } |
| |
| /* As cris_skip_prologue, but stops as soon as it knows that the function |
| has a frame. Its result is equal to its input pc if the function is |
| frameless, unequal otherwise. */ |
| |
| CORE_ADDR |
| cris_skip_prologue_frameless_p (CORE_ADDR pc) |
| { |
| return cris_skip_prologue_main (pc, 1); |
| } |
| |
| /* Given a PC value corresponding to the start of a function, return the PC |
| of the first instruction after the function prologue. */ |
| |
| CORE_ADDR |
| cris_skip_prologue_main (CORE_ADDR pc, int frameless_p) |
| { |
| struct cleanup *old_chain = make_cleanup (null_cleanup, NULL); |
| struct frame_info *fi; |
| struct symtab_and_line sal = find_pc_line (pc, 0); |
| int best_limit; |
| CORE_ADDR pc_after_prologue; |
| |
| /* frame_info now contains dynamic memory. Since fi is a dummy |
| here, I don't bother allocating memory for saved_regs. */ |
| fi = deprecated_frame_xmalloc_with_cleanup (0, sizeof (struct frame_extra_info)); |
| |
| /* If there is no symbol information then sal.end == 0, and we end up |
| examining only the first instruction in the function prologue. |
| Exaggerating the limit seems to be harmless. */ |
| if (sal.end > 0) |
| best_limit = sal.end; |
| else |
| best_limit = pc + 100; |
| |
| pc_after_prologue = cris_examine (pc, best_limit, fi, frameless_p); |
| do_cleanups (old_chain); |
| return pc_after_prologue; |
| } |
| |
| /* Use the program counter to determine the contents and size of a breakpoint |
| instruction. It returns a pointer to a string of bytes that encode a |
| breakpoint instruction, stores the length of the string to *lenptr, and |
| adjusts pcptr (if necessary) to point to the actual memory location where |
| the breakpoint should be inserted. */ |
| |
| const unsigned char * |
| cris_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr) |
| { |
| static unsigned char break_insn[] = {0x38, 0xe9}; |
| *lenptr = 2; |
| |
| return break_insn; |
| } |
| |
| /* Returns the register SRP (subroutine return pointer) which must contain |
| the content of the register PC after a function call. */ |
| |
| static CORE_ADDR |
| cris_saved_pc_after_call (struct frame_info *frame) |
| { |
| return read_register (SRP_REGNUM); |
| } |
| |
| /* Returns 1 if spec_reg is applicable to the current gdbarch's CRIS version, |
| 0 otherwise. */ |
| |
| int |
| cris_spec_reg_applicable (struct cris_spec_reg spec_reg) |
| { |
| int version = cris_version (); |
| |
| switch (spec_reg.applicable_version) |
| { |
| case cris_ver_version_all: |
| return 1; |
| case cris_ver_warning: |
| /* Indeterminate/obsolete. */ |
| return 0; |
| case cris_ver_sim: |
| /* Simulator only. */ |
| return 0; |
| case cris_ver_v0_3: |
| return (version >= 0 && version <= 3); |
| case cris_ver_v3p: |
| return (version >= 3); |
| case cris_ver_v8: |
| return (version == 8 || version == 9); |
| case cris_ver_v8p: |
| return (version >= 8); |
| case cris_ver_v10p: |
| return (version >= 10); |
| default: |
| /* Invalid cris version. */ |
| return 0; |
| } |
| } |
| |
| /* Returns the register size in unit byte. Returns 0 for an unimplemented |
| register, -1 for an invalid register. */ |
| |
| int |
| cris_register_size (int regno) |
| { |
| int i; |
| int spec_regno; |
| |
| if (regno >= 0 && regno < NUM_GENREGS) |
| { |
| /* General registers (R0 - R15) are 32 bits. */ |
| return 4; |
| } |
| else if (regno >= NUM_GENREGS && regno < NUM_REGS) |
| { |
| /* Special register (R16 - R31). cris_spec_regs is zero-based. |
| Adjust regno accordingly. */ |
| spec_regno = regno - NUM_GENREGS; |
| |
| /* The entries in cris_spec_regs are stored in register number order, |
| which means we can shortcut into the array when searching it. */ |
| for (i = spec_regno; cris_spec_regs[i].name != NULL; i++) |
| { |
| if (cris_spec_regs[i].number == spec_regno |
| && cris_spec_reg_applicable (cris_spec_regs[i])) |
| /* Go with the first applicable register. */ |
| return cris_spec_regs[i].reg_size; |
| } |
| /* Special register not applicable to this CRIS version. */ |
| return 0; |
| } |
| else |
| { |
| /* Invalid register. */ |
| return -1; |
| } |
| } |
| |
| /* Nonzero if regno should not be fetched from the target. This is the case |
| for unimplemented (size 0) and non-existant registers. */ |
| |
| int |
| cris_cannot_fetch_register (int regno) |
| { |
| return ((regno < 0 || regno >= NUM_REGS) |
| || (cris_register_size (regno) == 0)); |
| } |
| |
| /* Nonzero if regno should not be written to the target, for various |
| reasons. */ |
| |
| int |
| cris_cannot_store_register (int regno) |
| { |
| /* There are three kinds of registers we refuse to write to. |
| 1. Those that not implemented. |
| 2. Those that are read-only (depends on the processor mode). |
| 3. Those registers to which a write has no effect. |
| */ |
| |
| if (regno < 0 || regno >= NUM_REGS || cris_register_size (regno) == 0) |
| /* Not implemented. */ |
| return 1; |
| |
| else if (regno == VR_REGNUM) |
| /* Read-only. */ |
| return 1; |
| |
| else if (regno == P0_REGNUM || regno == P4_REGNUM || regno == P8_REGNUM) |
| /* Writing has no effect. */ |
| return 1; |
| |
| else if (cris_mode () == CRIS_MODE_USER) |
| { |
| if (regno == IBR_REGNUM || regno == BAR_REGNUM || regno == BRP_REGNUM |
| || regno == IRP_REGNUM) |
| /* Read-only in user mode. */ |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* Returns the register offset for the first byte of register regno's space |
| in the saved register state. Returns -1 for an invalid or unimplemented |
| register. */ |
| |
| int |
| cris_register_offset (int regno) |
| { |
| int i; |
| int reg_size; |
| int offset = 0; |
| |
| if (regno >= 0 && regno < NUM_REGS) |
| { |
| /* FIXME: The offsets should be cached and calculated only once, |
| when the architecture being debugged has changed. */ |
| for (i = 0; i < regno; i++) |
| offset += cris_register_size (i); |
| |
| return offset; |
| } |
| else |
| { |
| /* Invalid register. */ |
| return -1; |
| } |
| } |
| |
| /* Return the GDB type (defined in gdbtypes.c) for the "standard" data type |
| of data in register regno. */ |
| |
| struct type * |
| cris_register_virtual_type (int regno) |
| { |
| if (regno == SP_REGNUM || regno == PC_REGNUM |
| || (regno > P8_REGNUM && regno < USP_REGNUM)) |
| { |
| /* SP, PC, IBR, IRP, SRP, BAR, DCCR, BRP */ |
| return lookup_pointer_type (builtin_type_void); |
| } |
| else if (regno == P8_REGNUM || regno == USP_REGNUM |
| || (regno >= 0 && regno < SP_REGNUM)) |
| { |
| /* R0 - R13, P8, P15 */ |
| return builtin_type_unsigned_long; |
| } |
| else if (regno > P3_REGNUM && regno < P8_REGNUM) |
| { |
| /* P4, CCR, DCR0, DCR1 */ |
| return builtin_type_unsigned_short; |
| } |
| else if (regno > PC_REGNUM && regno < P4_REGNUM) |
| { |
| /* P0, P1, P2, P3 */ |
| return builtin_type_unsigned_char; |
| } |
| else |
| { |
| /* Invalid register. */ |
| return builtin_type_void; |
| } |
| } |
| |
| /* Stores a function return value of type type, where valbuf is the address |
| of the value to be stored. */ |
| |
| /* In the original CRIS ABI, R10 is used to store return values. */ |
| |
| void |
| cris_abi_original_store_return_value (struct type *type, char *valbuf) |
| { |
| int len = TYPE_LENGTH (type); |
| |
| if (len <= DEPRECATED_REGISTER_SIZE) |
| deprecated_write_register_bytes (REGISTER_BYTE (RET_REGNUM), valbuf, len); |
| else |
| internal_error (__FILE__, __LINE__, "cris_abi_original_store_return_value: type length too large."); |
| } |
| |
| /* In the CRIS ABI V2, R10 and R11 are used to store return values. */ |
| |
| void |
| cris_abi_v2_store_return_value (struct type *type, char *valbuf) |
| { |
| int len = TYPE_LENGTH (type); |
| |
| if (len <= 2 * DEPRECATED_REGISTER_SIZE) |
| { |
| /* Note that this works since R10 and R11 are consecutive registers. */ |
| deprecated_write_register_bytes (REGISTER_BYTE (RET_REGNUM), valbuf, |
| len); |
| } |
| else |
| internal_error (__FILE__, __LINE__, "cris_abi_v2_store_return_value: type length too large."); |
| } |
| |
| /* Return the name of register regno as a string. Return NULL for an invalid or |
| unimplemented register. */ |
| |
| const char * |
| cris_register_name (int regno) |
| { |
| static char *cris_genreg_names[] = |
| { "r0", "r1", "r2", "r3", \ |
| "r4", "r5", "r6", "r7", \ |
| "r8", "r9", "r10", "r11", \ |
| "r12", "r13", "sp", "pc" }; |
| |
| int i; |
| int spec_regno; |
| |
| if (regno >= 0 && regno < NUM_GENREGS) |
| { |
| /* General register. */ |
| return cris_genreg_names[regno]; |
| } |
| else if (regno >= NUM_GENREGS && regno < NUM_REGS) |
| { |
| /* Special register (R16 - R31). cris_spec_regs is zero-based. |
| Adjust regno accordingly. */ |
| spec_regno = regno - NUM_GENREGS; |
| |
| /* The entries in cris_spec_regs are stored in register number order, |
| which means we can shortcut into the array when searching it. */ |
| for (i = spec_regno; cris_spec_regs[i].name != NULL; i++) |
| { |
| if (cris_spec_regs[i].number == spec_regno |
| && cris_spec_reg_applicable (cris_spec_regs[i])) |
| /* Go with the first applicable register. */ |
| return cris_spec_regs[i].name; |
| } |
| /* Special register not applicable to this CRIS version. */ |
| return NULL; |
| } |
| else |
| { |
| /* Invalid register. */ |
| return NULL; |
| } |
| } |
| |
| int |
| cris_register_bytes_ok (long bytes) |
| { |
| return (bytes == DEPRECATED_REGISTER_BYTES); |
| } |
| |
| /* Extract from an array regbuf containing the raw register state a function |
| return value of type type, and copy that, in virtual format, into |
| valbuf. */ |
| |
| /* In the original CRIS ABI, R10 is used to return values. */ |
| |
| void |
| cris_abi_original_extract_return_value (struct type *type, char *regbuf, |
| char *valbuf) |
| { |
| int len = TYPE_LENGTH (type); |
| |
| if (len <= DEPRECATED_REGISTER_SIZE) |
| memcpy (valbuf, regbuf + REGISTER_BYTE (RET_REGNUM), len); |
| else |
| internal_error (__FILE__, __LINE__, "cris_abi_original_extract_return_value: type length too large"); |
| } |
| |
| /* In the CRIS ABI V2, R10 and R11 are used to store return values. */ |
| |
| void |
| cris_abi_v2_extract_return_value (struct type *type, char *regbuf, |
| char *valbuf) |
| { |
| int len = TYPE_LENGTH (type); |
| |
| if (len <= 2 * DEPRECATED_REGISTER_SIZE) |
| memcpy (valbuf, regbuf + REGISTER_BYTE (RET_REGNUM), len); |
| else |
| internal_error (__FILE__, __LINE__, "cris_abi_v2_extract_return_value: type length too large"); |
| } |
| |
| /* Store the address of the place in which to copy the structure the |
| subroutine will return. In the CRIS ABI, R9 is used in order to pass |
| the address of the allocated area where a structure return value must |
| be stored. R9 is call-clobbered, which means we must save it here for |
| later use. */ |
| |
| void |
| cris_store_struct_return (CORE_ADDR addr, CORE_ADDR sp) |
| { |
| write_register (STR_REGNUM, addr); |
| struct_return_address = addr; |
| } |
| |
| /* Extract from regbuf the address where a function should return a |
| structure value. It's not there in the CRIS ABI, so we must do it another |
| way. */ |
| |
| CORE_ADDR |
| cris_extract_struct_value_address (char *regbuf) |
| { |
| return struct_return_address; |
| } |
| |
| /* Returns 1 if a value of the given type being returned from a function |
| must have space allocated for it on the stack. gcc_p is true if the |
| function being considered is known to have been compiled by GCC. |
| In the CRIS ABI, structure return values are passed to the called |
| function by reference in register R9 to a caller-allocated area, so |
| this is always true. */ |
| |
| int |
| cris_use_struct_convention (int gcc_p, struct type *type) |
| { |
| return 1; |
| } |
| |
| /* Returns 1 if the given type will be passed by pointer rather than |
| directly. */ |
| |
| /* In the original CRIS ABI, arguments shorter than or equal to 32 bits are |
| passed by value. */ |
| |
| int |
| cris_abi_original_reg_struct_has_addr (int gcc_p, struct type *type) |
| { |
| return (TYPE_LENGTH (type) > 4); |
| } |
| |
| /* In the CRIS ABI V2, arguments shorter than or equal to 64 bits are passed |
| by value. */ |
| |
| int |
| cris_abi_v2_reg_struct_has_addr (int gcc_p, struct type *type) |
| { |
| return (TYPE_LENGTH (type) > 8); |
| } |
| |
| /* Returns 1 if the function invocation represented by fi does not have a |
| stack frame associated with it. Otherwise return 0. */ |
| |
| int |
| cris_frameless_function_invocation (struct frame_info *fi) |
| { |
| if ((get_frame_type (fi) == SIGTRAMP_FRAME)) |
| return 0; |
| else |
| return frameless_look_for_prologue (fi); |
| } |
| |
| /* See frame.h. Determines the address of all registers in the |
| current stack frame storing each in frame->saved_regs. Space for |
| frame->saved_regs shall be allocated by |
| DEPRECATED_FRAME_INIT_SAVED_REGS using frame_saved_regs_zalloc. */ |
| |
| void |
| cris_frame_init_saved_regs (struct frame_info *fi) |
| { |
| CORE_ADDR ip; |
| struct symtab_and_line sal; |
| int best_limit; |
| char *dummy_regs = deprecated_generic_find_dummy_frame (get_frame_pc (fi), |
| get_frame_base (fi)); |
| |
| /* Examine the entire prologue. */ |
| register int frameless_p = 0; |
| |
| /* Has this frame's registers already been initialized? */ |
| if (get_frame_saved_regs (fi)) |
| return; |
| |
| frame_saved_regs_zalloc (fi); |
| |
| if (dummy_regs) |
| { |
| /* I don't see this ever happening, considering the context in which |
| cris_frame_init_saved_regs is called (always when we're not in |
| a dummy frame). */ |
| memcpy (get_frame_saved_regs (fi), dummy_regs, SIZEOF_FRAME_SAVED_REGS); |
| } |
| else |
| { |
| ip = get_frame_func (fi); |
| sal = find_pc_line (ip, 0); |
| |
| /* If there is no symbol information then sal.end == 0, and we end up |
| examining only the first instruction in the function prologue. |
| Exaggerating the limit seems to be harmless. */ |
| if (sal.end > 0) |
| best_limit = sal.end; |
| else |
| best_limit = ip + 100; |
| |
| cris_examine (ip, best_limit, fi, frameless_p); |
| } |
| } |
| |
| /* Initialises the extra frame information at the creation of a new frame. |
| The inparameter fromleaf is 0 when the call is from create_new_frame. |
| When the call is from get_prev_frame_info, fromleaf is determined by |
| cris_frameless_function_invocation. */ |
| |
| void |
| cris_init_extra_frame_info (int fromleaf, struct frame_info *fi) |
| { |
| if (get_next_frame (fi)) |
| { |
| /* Called from get_prev_frame. */ |
| deprecated_update_frame_pc_hack (fi, DEPRECATED_FRAME_SAVED_PC (get_next_frame (fi))); |
| } |
| |
| frame_extra_info_zalloc (fi, sizeof (struct frame_extra_info)); |
| |
| get_frame_extra_info (fi)->return_pc = 0; |
| get_frame_extra_info (fi)->leaf_function = 0; |
| |
| if (DEPRECATED_PC_IN_CALL_DUMMY (get_frame_pc (fi), |
| get_frame_base (fi), |
| get_frame_base (fi))) |
| { |
| /* We need to setup fi->frame here because call_function_by_hand |
| gets it wrong by assuming it's always FP. */ |
| deprecated_update_frame_base_hack (fi, deprecated_read_register_dummy (get_frame_pc (fi), get_frame_base (fi), SP_REGNUM)); |
| get_frame_extra_info (fi)->return_pc = |
| deprecated_read_register_dummy (get_frame_pc (fi), |
| get_frame_base (fi), PC_REGNUM); |
| |
| /* FIXME: Is this necessarily true? */ |
| get_frame_extra_info (fi)->leaf_function = 0; |
| } |
| else |
| { |
| cris_frame_init_saved_regs (fi); |
| |
| /* Check fromleaf/frameless_function_invocation. (FIXME) */ |
| |
| if (get_frame_saved_regs (fi)[SRP_REGNUM] != 0) |
| { |
| /* SRP was saved on the stack; non-leaf function. */ |
| get_frame_extra_info (fi)->return_pc = |
| read_memory_integer (get_frame_saved_regs (fi)[SRP_REGNUM], |
| REGISTER_RAW_SIZE (SRP_REGNUM)); |
| } |
| else |
| { |
| /* SRP is still in a register; leaf function. */ |
| get_frame_extra_info (fi)->return_pc = read_register (SRP_REGNUM); |
| /* FIXME: Should leaf_function be set to 1 here? */ |
| get_frame_extra_info (fi)->leaf_function = 1; |
| } |
| } |
| } |
| |
| /* Return the content of the frame pointer in the present frame. In other |
| words, determine the address of the calling function's frame. */ |
| |
| CORE_ADDR |
| cris_frame_chain (struct frame_info *fi) |
| { |
| if (DEPRECATED_PC_IN_CALL_DUMMY (get_frame_pc (fi), |
| get_frame_base (fi), |
| get_frame_base (fi))) |
| { |
| return get_frame_base (fi); |
| } |
| else if (!inside_entry_file (get_frame_pc (fi))) |
| { |
| return read_memory_unsigned_integer (get_frame_base (fi), 4); |
| } |
| else |
| { |
| return 0; |
| } |
| } |
| |
| /* Return the saved PC (which equals the return address) of this frame. */ |
| |
| CORE_ADDR |
| cris_frame_saved_pc (struct frame_info *fi) |
| { |
| return get_frame_extra_info (fi)->return_pc; |
| } |
| |
| /* Setup the function arguments for calling a function in the inferior. */ |
| |
| CORE_ADDR |
| cris_abi_original_push_arguments (int nargs, struct value **args, |
| CORE_ADDR sp, int struct_return, |
| CORE_ADDR struct_addr) |
| { |
| int stack_alloc; |
| int stack_offset; |
| int argreg; |
| int argnum; |
| struct type *type; |
| int len; |
| CORE_ADDR regval; |
| char *val; |
| |
| /* Data and parameters reside in different areas on the stack. |
| Both frame pointers grow toward higher addresses. */ |
| CORE_ADDR fp_params; |
| CORE_ADDR fp_data; |
| |
| /* Are we returning a value using a structure return or a normal value |
| return? struct_addr is the address of the reserved space for the return |
| structure to be written on the stack. */ |
| if (struct_return) |
| { |
| write_register (STR_REGNUM, struct_addr); |
| } |
| |
| /* Make sure there's space on the stack. Allocate space for data and a |
| parameter to refer to that data. */ |
| for (argnum = 0, stack_alloc = 0; argnum < nargs; argnum++) |
| stack_alloc += (TYPE_LENGTH (VALUE_TYPE (args[argnum])) + DEPRECATED_REGISTER_SIZE); |
| sp -= stack_alloc; |
| /* We may over-allocate a little here, but that won't hurt anything. */ |
| |
| /* Initialize stack frame pointers. */ |
| fp_params = sp; |
| fp_data = sp + (nargs * DEPRECATED_REGISTER_SIZE); |
| |
| /* Now load as many as possible of the first arguments into |
| registers, and push the rest onto the stack. */ |
| argreg = ARG1_REGNUM; |
| stack_offset = 0; |
| |
| for (argnum = 0; argnum < nargs; argnum++) |
| { |
| type = VALUE_TYPE (args[argnum]); |
| len = TYPE_LENGTH (type); |
| val = (char *) VALUE_CONTENTS (args[argnum]); |
| |
| if (len <= DEPRECATED_REGISTER_SIZE && argreg <= ARG4_REGNUM) |
| { |
| /* Data fits in a register; put it in the first available |
| register. */ |
| write_register (argreg, *(unsigned long *) val); |
| argreg++; |
| } |
| else if (len > DEPRECATED_REGISTER_SIZE && argreg <= ARG4_REGNUM) |
| { |
| /* Data does not fit in register; pass it on the stack and |
| put its address in the first available register. */ |
| write_memory (fp_data, val, len); |
| write_register (argreg, fp_data); |
| fp_data += len; |
| argreg++; |
| } |
| else if (len > DEPRECATED_REGISTER_SIZE) |
| { |
| /* Data does not fit in register; put both data and |
| parameter on the stack. */ |
| write_memory (fp_data, val, len); |
| write_memory (fp_params, (char *) (&fp_data), DEPRECATED_REGISTER_SIZE); |
| fp_data += len; |
| fp_params += DEPRECATED_REGISTER_SIZE; |
| } |
| else |
| { |
| /* Data fits in a register, but we are out of registers; |
| put the parameter on the stack. */ |
| write_memory (fp_params, val, DEPRECATED_REGISTER_SIZE); |
| fp_params += DEPRECATED_REGISTER_SIZE; |
| } |
| } |
| |
| return sp; |
| } |
| |
| CORE_ADDR |
| cris_abi_v2_push_arguments (int nargs, struct value **args, CORE_ADDR sp, |
| int struct_return, CORE_ADDR struct_addr) |
| { |
| int stack_alloc; |
| int stack_offset; |
| int argreg; |
| int argnum; |
| |
| CORE_ADDR regval; |
| |
| /* The function's arguments and memory allocated by gdb for the arguments to |
| point at reside in separate areas on the stack. |
| Both frame pointers grow toward higher addresses. */ |
| CORE_ADDR fp_arg; |
| CORE_ADDR fp_mem; |
| |
| /* Are we returning a value using a structure return or a normal value |
| return? struct_addr is the address of the reserved space for the return |
| structure to be written on the stack. */ |
| if (struct_return) |
| { |
| write_register (STR_REGNUM, struct_addr); |
| } |
| |
| /* Allocate enough to keep things word-aligned on both parts of the |
| stack. */ |
| stack_alloc = 0; |
| for (argnum = 0; argnum < nargs; argnum++) |
| { |
| int len; |
| int reg_demand; |
| |
| len = TYPE_LENGTH (VALUE_TYPE (args[argnum])); |
| reg_demand = (len / DEPRECATED_REGISTER_SIZE) + (len % DEPRECATED_REGISTER_SIZE != 0 ? 1 : 0); |
| |
| /* reg_demand * DEPRECATED_REGISTER_SIZE is the amount of memory |
| we might need to allocate for this argument. 2 * |
| DEPRECATED_REGISTER_SIZE is the amount of stack space we |
| might need to pass the argument itself (either by value or by |
| reference). */ |
| stack_alloc += (reg_demand * DEPRECATED_REGISTER_SIZE + 2 * DEPRECATED_REGISTER_SIZE); |
| } |
| sp -= stack_alloc; |
| /* We may over-allocate a little here, but that won't hurt anything. */ |
| |
| /* Initialize frame pointers. */ |
| fp_arg = sp; |
| fp_mem = sp + (nargs * (2 * DEPRECATED_REGISTER_SIZE)); |
| |
| /* Now load as many as possible of the first arguments into registers, |
| and push the rest onto the stack. */ |
| argreg = ARG1_REGNUM; |
| stack_offset = 0; |
| |
| for (argnum = 0; argnum < nargs; argnum++) |
| { |
| int len; |
| char *val; |
| int reg_demand; |
| int i; |
| |
| len = TYPE_LENGTH (VALUE_TYPE (args[argnum])); |
| val = (char *) VALUE_CONTENTS (args[argnum]); |
| |
| /* How may registers worth of storage do we need for this argument? */ |
| reg_demand = (len / DEPRECATED_REGISTER_SIZE) + (len % DEPRECATED_REGISTER_SIZE != 0 ? 1 : 0); |
| |
| if (len <= (2 * DEPRECATED_REGISTER_SIZE) |
| && (argreg + reg_demand - 1 <= ARG4_REGNUM)) |
| { |
| /* Data passed by value. Fits in available register(s). */ |
| for (i = 0; i < reg_demand; i++) |
| { |
| write_register (argreg, *(unsigned long *) val); |
| argreg++; |
| val += DEPRECATED_REGISTER_SIZE; |
| } |
| } |
| else if (len <= (2 * DEPRECATED_REGISTER_SIZE) && argreg <= ARG4_REGNUM) |
| { |
| /* Data passed by value. Does not fit in available register(s). |
| Use the register(s) first, then the stack. */ |
| for (i = 0; i < reg_demand; i++) |
| { |
| if (argreg <= ARG4_REGNUM) |
| { |
| write_register (argreg, *(unsigned long *) val); |
| argreg++; |
| val += DEPRECATED_REGISTER_SIZE; |
| } |
| else |
| { |
| /* I guess this memory write could write the |
| remaining data all at once instead of in |
| DEPRECATED_REGISTER_SIZE chunks. */ |
| write_memory (fp_arg, val, DEPRECATED_REGISTER_SIZE); |
| fp_arg += DEPRECATED_REGISTER_SIZE; |
| val += DEPRECATED_REGISTER_SIZE; |
| } |
| } |
| } |
| else if (len > (2 * DEPRECATED_REGISTER_SIZE)) |
| { |
| /* Data passed by reference. Put it on the stack. */ |
| write_memory (fp_mem, val, len); |
| write_memory (fp_arg, (char *) (&fp_mem), DEPRECATED_REGISTER_SIZE); |
| |
| /* fp_mem need not be word-aligned since it's just a chunk of |
| memory being pointed at. That is, += len would do. */ |
| fp_mem += reg_demand * DEPRECATED_REGISTER_SIZE; |
| fp_arg += DEPRECATED_REGISTER_SIZE; |
| } |
| else |
| { |
| /* Data passed by value. No available registers. Put it on |
| the stack. */ |
| write_memory (fp_arg, val, len); |
| |
| /* fp_arg must be word-aligned (i.e., don't += len) to match |
| the function prologue. */ |
| fp_arg += reg_demand * DEPRECATED_REGISTER_SIZE; |
| } |
| } |
| |
| return sp; |
| } |
| |
| /* Never put the return address on the stack. The register SRP is pushed |
| by the called function unless it is a leaf-function. Due to the BRP |
| register the PC will change when continue is sent. */ |
| |
| CORE_ADDR |
| cris_push_return_address (CORE_ADDR pc, CORE_ADDR sp) |
| { |
| write_register (SRP_REGNUM, CALL_DUMMY_ADDRESS ()); |
| return sp; |
| } |
| |
| /* Restore the machine to the state it had before the current frame |
| was created. Discard the innermost frame from the stack and restore |
| all saved registers. */ |
| |
| void |
| cris_pop_frame (void) |
| { |
| register struct frame_info *fi = get_current_frame (); |
| register int regno; |
| register int stack_offset = 0; |
| |
| if (DEPRECATED_PC_IN_CALL_DUMMY (get_frame_pc (fi), |
| get_frame_base (fi), |
| get_frame_base (fi))) |
| { |
| /* This happens when we hit a breakpoint set at the entry point, |
| when returning from a dummy frame. */ |
| generic_pop_dummy_frame (); |
| } |
| else |
| { |
| cris_frame_init_saved_regs (fi); |
| |
| /* For each register, the address of where it was saved on entry to |
| the frame now lies in fi->saved_regs[regno], or zero if it was not |
| saved. This includes special registers such as PC and FP saved in |
| special ways in the stack frame. The SP_REGNUM is even more |
| special, the address here is the SP for the next frame, not the |
| address where the SP was saved. */ |
| |
| /* Restore general registers R0 - R7. They were pushed on the stack |
| after SP was saved. */ |
| for (regno = 0; regno < DEPRECATED_FP_REGNUM; regno++) |
| { |
| if (get_frame_saved_regs (fi)[regno]) |
| { |
| write_register (regno, |
| read_memory_integer (get_frame_saved_regs (fi)[regno], 4)); |
| } |
| } |
| |
| if (get_frame_saved_regs (fi)[DEPRECATED_FP_REGNUM]) |
| { |
| /* Pop the frame pointer (R8). It was pushed before SP |
| was saved. */ |
| write_register (DEPRECATED_FP_REGNUM, |
| read_memory_integer (get_frame_saved_regs (fi)[DEPRECATED_FP_REGNUM], 4)); |
| stack_offset += 4; |
| |
| /* Not a leaf function. */ |
| if (get_frame_saved_regs (fi)[SRP_REGNUM]) |
| { |
| /* SRP was pushed before SP was saved. */ |
| stack_offset += 4; |
| } |
| |
| /* Restore the SP and adjust for R8 and (possibly) SRP. */ |
| write_register (SP_REGNUM, get_frame_saved_regs (fi)[DEPRECATED_FP_REGNUM] + stack_offset); |
| } |
| else |
| { |
| /* Currently, we can't get the correct info into fi->saved_regs |
| without a frame pointer. */ |
| } |
| |
| /* Restore the PC. */ |
| write_register (PC_REGNUM, get_frame_extra_info (fi)->return_pc); |
| } |
| flush_cached_frames (); |
| } |
| |
| /* Calculates a value that measures how good inst_args constraints an |
| instruction. It stems from cris_constraint, found in cris-dis.c. */ |
| |
| static int |
| constraint (unsigned int insn, const signed char *inst_args, |
| inst_env_type *inst_env) |
| { |
| int retval = 0; |
| int tmp, i; |
| |
| const char *s = inst_args; |
| |
| for (; *s; s++) |
| switch (*s) |
| { |
| case 'm': |
| if ((insn & 0x30) == 0x30) |
| return -1; |
| break; |
| |
| case 'S': |
| /* A prefix operand. */ |
| if (inst_env->prefix_found) |
| break; |
| else |
| return -1; |
| |
| case 'B': |
| /* A "push" prefix. (This check was REMOVED by san 970921.) Check for |
| valid "push" size. In case of special register, it may be != 4. */ |
| if (inst_env->prefix_found) |
| break; |
| else |
| return -1; |
| |
| case 'D': |
| retval = (((insn >> 0xC) & 0xF) == (insn & 0xF)); |
| if (!retval) |
| return -1; |
| else |
| retval += 4; |
| break; |
| |
| case 'P': |
| tmp = (insn >> 0xC) & 0xF; |
| |
| for (i = 0; cris_spec_regs[i].name != NULL; i++) |
| { |
| /* Since we match four bits, we will give a value of |
| 4 - 1 = 3 in a match. If there is a corresponding |
| exact match of a special register in another pattern, it |
| will get a value of 4, which will be higher. This should |
| be correct in that an exact pattern would match better that |
| a general pattern. |
| Note that there is a reason for not returning zero; the |
| pattern for "clear" is partly matched in the bit-pattern |
| (the two lower bits must be zero), while the bit-pattern |
| for a move from a special register is matched in the |
| register constraint. |
| This also means we will will have a race condition if |
| there is a partly match in three bits in the bit pattern. */ |
| if (tmp == cris_spec_regs[i].number) |
| { |
| retval += 3; |
| break; |
| } |
| } |
| |
| if (cris_spec_regs[i].name == NULL) |
| return -1; |
| break; |
| } |
| return retval; |
| } |
| |
| /* Returns the number of bits set in the variable value. */ |
| |
| static int |
| number_of_bits (unsigned int value) |
| { |
| int number_of_bits = 0; |
| |
| while (value != 0) |
| { |
| number_of_bits += 1; |
| value &= (value - 1); |
| } |
| return number_of_bits; |
| } |
| |
| /* Finds the address that should contain the single step breakpoint(s). |
| It stems from code in cris-dis.c. */ |
| |
| static int |
| find_cris_op (unsigned short insn, inst_env_type *inst_env) |
| { |
| int i; |
| int max_level_of_match = -1; |
| int max_matched = -1; |
| int level_of_match; |
| |
| for (i = 0; cris_opcodes[i].name != NULL; i++) |
| { |
| if (((cris_opcodes[i].match & insn) == cris_opcodes[i].match) |
| && ((cris_opcodes[i].lose & insn) == 0)) |
| { |
| level_of_match = constraint (insn, cris_opcodes[i].args, inst_env); |
| if (level_of_match >= 0) |
| { |
| level_of_match += |
| number_of_bits (cris_opcodes[i].match | cris_opcodes[i].lose); |
| if (level_of_match > max_level_of_match) |
| { |
| max_matched = i; |
| max_level_of_match = level_of_match; |
| if (level_of_match == 16) |
| { |
| /* All bits matched, cannot find better. */ |
| break; |
| } |
| } |
| } |
| } |
| } |
| return max_matched; |
| } |
| |
| /* Attempts to find single-step breakpoints. Returns -1 on failure which is |
| actually an internal error. */ |
| |
| static int |
| find_step_target (inst_env_type *inst_env) |
| { |
| int i; |
| int offset; |
| unsigned short insn; |
| |
| /* Create a local register image and set the initial state. */ |
| for (i = 0; i < NUM_GENREGS; i++) |
| { |
| inst_env->reg[i] = (unsigned long) read_register (i); |
| } |
| offset = NUM_GENREGS; |
| for (i = 0; i < NUM_SPECREGS; i++) |
| { |
| inst_env->preg[i] = (unsigned long) read_register (offset + i); |
| } |
| inst_env->branch_found = 0; |
| inst_env->slot_needed = 0; |
| inst_env->delay_slot_pc_active = 0; |
| inst_env->prefix_found = 0; |
| inst_env->invalid = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| |
| /* Look for a step target. */ |
| do |
| { |
| /* Read an instruction from the client. */ |
| insn = read_memory_unsigned_integer (inst_env->reg[PC_REGNUM], 2); |
| |
| /* If the instruction is not in a delay slot the new content of the |
| PC is [PC] + 2. If the instruction is in a delay slot it is not |
| that simple. Since a instruction in a delay slot cannot change |
| the content of the PC, it does not matter what value PC will have. |
| Just make sure it is a valid instruction. */ |
| if (!inst_env->delay_slot_pc_active) |
| { |
| inst_env->reg[PC_REGNUM] += 2; |
| } |
| else |
| { |
| inst_env->delay_slot_pc_active = 0; |
| inst_env->reg[PC_REGNUM] = inst_env->delay_slot_pc; |
| } |
| /* Analyse the present instruction. */ |
| i = find_cris_op (insn, inst_env); |
| if (i == -1) |
| { |
| inst_env->invalid = 1; |
| } |
| else |
| { |
| cris_gdb_func (cris_opcodes[i].op, insn, inst_env); |
| } |
| } while (!inst_env->invalid |
| && (inst_env->prefix_found || inst_env->xflag_found |
| || inst_env->slot_needed)); |
| return i; |
| } |
| |
| /* There is no hardware single-step support. The function find_step_target |
| digs through the opcodes in order to find all possible targets. |
| Either one ordinary target or two targets for branches may be found. */ |
| |
| void |
| cris_software_single_step (enum target_signal ignore, int insert_breakpoints) |
| { |
| inst_env_type inst_env; |
| |
| if (insert_breakpoints) |
| { |
| /* Analyse the present instruction environment and insert |
| breakpoints. */ |
| int status = find_step_target (&inst_env); |
| if (status == -1) |
| { |
| /* Could not find a target. FIXME: Should do something. */ |
| } |
| else |
| { |
| /* Insert at most two breakpoints. One for the next PC content |
| and possibly another one for a branch, jump, etc. */ |
| next_pc = (CORE_ADDR) inst_env.reg[PC_REGNUM]; |
| target_insert_breakpoint (next_pc, break_mem[0]); |
| if (inst_env.branch_found |
| && (CORE_ADDR) inst_env.branch_break_address != next_pc) |
| { |
| branch_target_address = |
| (CORE_ADDR) inst_env.branch_break_address; |
| target_insert_breakpoint (branch_target_address, break_mem[1]); |
| branch_break_inserted = 1; |
| } |
| } |
| } |
| else |
| { |
| /* Remove breakpoints. */ |
| target_remove_breakpoint (next_pc, break_mem[0]); |
| if (branch_break_inserted) |
| { |
| target_remove_breakpoint (branch_target_address, break_mem[1]); |
| branch_break_inserted = 0; |
| } |
| } |
| } |
| |
| /* Calculates the prefix value for quick offset addressing mode. */ |
| |
| void |
| quick_mode_bdap_prefix (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* It's invalid to be in a delay slot. You can't have a prefix to this |
| instruction (not 100% sure). */ |
| if (inst_env->slot_needed || inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| inst_env->prefix_value = inst_env->reg[cris_get_operand2 (inst)]; |
| inst_env->prefix_value += cris_get_bdap_quick_offset (inst); |
| |
| /* A prefix doesn't change the xflag_found. But the rest of the flags |
| need updating. */ |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 1; |
| } |
| |
| /* Updates the autoincrement register. The size of the increment is derived |
| from the size of the operation. The PC is always kept aligned on even |
| word addresses. */ |
| |
| void |
| process_autoincrement (int size, unsigned short inst, inst_env_type *inst_env) |
| { |
| if (size == INST_BYTE_SIZE) |
| { |
| inst_env->reg[cris_get_operand1 (inst)] += 1; |
| |
| /* The PC must be word aligned, so increase the PC with one |
| word even if the size is byte. */ |
| if (cris_get_operand1 (inst) == REG_PC) |
| { |
| inst_env->reg[REG_PC] += 1; |
| } |
| } |
| else if (size == INST_WORD_SIZE) |
| { |
| inst_env->reg[cris_get_operand1 (inst)] += 2; |
| } |
| else if (size == INST_DWORD_SIZE) |
| { |
| inst_env->reg[cris_get_operand1 (inst)] += 4; |
| } |
| else |
| { |
| /* Invalid size. */ |
| inst_env->invalid = 1; |
| } |
| } |
| |
| /* Just a forward declaration. */ |
| |
| unsigned long get_data_from_address (unsigned short *inst, CORE_ADDR address); |
| |
| /* Calculates the prefix value for the general case of offset addressing |
| mode. */ |
| |
| void |
| bdap_prefix (unsigned short inst, inst_env_type *inst_env) |
| { |
| |
| long offset; |
| |
| /* It's invalid to be in a delay slot. */ |
| if (inst_env->slot_needed || inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| /* The calculation of prefix_value used to be after process_autoincrement, |
| but that fails for an instruction such as jsr [$r0+12] which is encoded |
| as 5f0d 0c00 30b9 when compiled with -fpic. Since PC is operand1 it |
| mustn't be incremented until we have read it and what it points at. */ |
| inst_env->prefix_value = inst_env->reg[cris_get_operand2 (inst)]; |
| |
| /* The offset is an indirection of the contents of the operand1 register. */ |
| inst_env->prefix_value += |
| get_data_from_address (&inst, inst_env->reg[cris_get_operand1 (inst)]); |
| |
| if (cris_get_mode (inst) == AUTOINC_MODE) |
| { |
| process_autoincrement (cris_get_size (inst), inst, inst_env); |
| } |
| |
| /* A prefix doesn't change the xflag_found. But the rest of the flags |
| need updating. */ |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 1; |
| } |
| |
| /* Calculates the prefix value for the index addressing mode. */ |
| |
| void |
| biap_prefix (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* It's invalid to be in a delay slot. I can't see that it's possible to |
| have a prefix to this instruction. So I will treat this as invalid. */ |
| if (inst_env->slot_needed || inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| inst_env->prefix_value = inst_env->reg[cris_get_operand1 (inst)]; |
| |
| /* The offset is the operand2 value shifted the size of the instruction |
| to the left. */ |
| inst_env->prefix_value += |
| inst_env->reg[cris_get_operand2 (inst)] << cris_get_size (inst); |
| |
| /* If the PC is operand1 (base) the address used is the address after |
| the main instruction, i.e. address + 2 (the PC is already compensated |
| for the prefix operation). */ |
| if (cris_get_operand1 (inst) == REG_PC) |
| { |
| inst_env->prefix_value += 2; |
| } |
| |
| /* A prefix doesn't change the xflag_found. But the rest of the flags |
| need updating. */ |
| inst_env->slot_needed = 0; |
| inst_env->xflag_found = 0; |
| inst_env->prefix_found = 1; |
| } |
| |
| /* Calculates the prefix value for the double indirect addressing mode. */ |
| |
| void |
| dip_prefix (unsigned short inst, inst_env_type *inst_env) |
| { |
| |
| CORE_ADDR address; |
| |
| /* It's invalid to be in a delay slot. */ |
| if (inst_env->slot_needed || inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| /* The prefix value is one dereference of the contents of the operand1 |
| register. */ |
| address = (CORE_ADDR) inst_env->reg[cris_get_operand1 (inst)]; |
| inst_env->prefix_value = read_memory_unsigned_integer (address, 4); |
| |
| /* Check if the mode is autoincrement. */ |
| if (cris_get_mode (inst) == AUTOINC_MODE) |
| { |
| inst_env->reg[cris_get_operand1 (inst)] += 4; |
| } |
| |
| /* A prefix doesn't change the xflag_found. But the rest of the flags |
| need updating. */ |
| inst_env->slot_needed = 0; |
| inst_env->xflag_found = 0; |
| inst_env->prefix_found = 1; |
| } |
| |
| /* Finds the destination for a branch with 8-bits offset. */ |
| |
| void |
| eight_bit_offset_branch_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| |
| short offset; |
| |
| /* If we have a prefix or are in a delay slot it's bad. */ |
| if (inst_env->slot_needed || inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| /* We have a branch, find out where the branch will land. */ |
| offset = cris_get_branch_short_offset (inst); |
| |
| /* Check if the offset is signed. */ |
| if (offset & BRANCH_SIGNED_SHORT_OFFSET_MASK) |
| { |
| offset |= 0xFF00; |
| } |
| |
| /* The offset ends with the sign bit, set it to zero. The address |
| should always be word aligned. */ |
| offset &= ~BRANCH_SIGNED_SHORT_OFFSET_MASK; |
| |
| inst_env->branch_found = 1; |
| inst_env->branch_break_address = inst_env->reg[REG_PC] + offset; |
| |
| inst_env->slot_needed = 1; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 1; |
| } |
| |
| /* Finds the destination for a branch with 16-bits offset. */ |
| |
| void |
| sixteen_bit_offset_branch_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| short offset; |
| |
| /* If we have a prefix or is in a delay slot it's bad. */ |
| if (inst_env->slot_needed || inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| /* We have a branch, find out the offset for the branch. */ |
| offset = read_memory_integer (inst_env->reg[REG_PC], 2); |
| |
| /* The instruction is one word longer than normal, so add one word |
| to the PC. */ |
| inst_env->reg[REG_PC] += 2; |
| |
| inst_env->branch_found = 1; |
| inst_env->branch_break_address = inst_env->reg[REG_PC] + offset; |
| |
| |
| inst_env->slot_needed = 1; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 1; |
| } |
| |
| /* Handles the ABS instruction. */ |
| |
| void |
| abs_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| |
| long value; |
| |
| /* ABS can't have a prefix, so it's bad if it does. */ |
| if (inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| /* Check if the operation affects the PC. */ |
| if (cris_get_operand2 (inst) == REG_PC) |
| { |
| |
| /* It's invalid to change to the PC if we are in a delay slot. */ |
| if (inst_env->slot_needed) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| value = (long) inst_env->reg[REG_PC]; |
| |
| /* The value of abs (SIGNED_DWORD_MASK) is SIGNED_DWORD_MASK. */ |
| if (value != SIGNED_DWORD_MASK) |
| { |
| value = -value; |
| inst_env->reg[REG_PC] = (long) value; |
| } |
| } |
| |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Handles the ADDI instruction. */ |
| |
| void |
| addi_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* It's invalid to have the PC as base register. And ADDI can't have |
| a prefix. */ |
| if (inst_env->prefix_found || (cris_get_operand1 (inst) == REG_PC)) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Handles the ASR instruction. */ |
| |
| void |
| asr_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| int shift_steps; |
| unsigned long value; |
| unsigned long signed_extend_mask = 0; |
| |
| /* ASR can't have a prefix, so check that it doesn't. */ |
| if (inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| /* Check if the PC is the target register. */ |
| if (cris_get_operand2 (inst) == REG_PC) |
| { |
| /* It's invalid to change the PC in a delay slot. */ |
| if (inst_env->slot_needed) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| /* Get the number of bits to shift. */ |
| shift_steps = cris_get_asr_shift_steps (inst_env->reg[cris_get_operand1 (inst)]); |
| value = inst_env->reg[REG_PC]; |
| |
| /* Find out how many bits the operation should apply to. */ |
| if (cris_get_size (inst) == INST_BYTE_SIZE) |
| { |
| if (value & SIGNED_BYTE_MASK) |
| { |
| signed_extend_mask = 0xFF; |
| signed_extend_mask = signed_extend_mask >> shift_steps; |
| signed_extend_mask = ~signed_extend_mask; |
| } |
| value = value >> shift_steps; |
| value |= signed_extend_mask; |
| value &= 0xFF; |
| inst_env->reg[REG_PC] &= 0xFFFFFF00; |
| inst_env->reg[REG_PC] |= value; |
| } |
| else if (cris_get_size (inst) == INST_WORD_SIZE) |
| { |
| if (value & SIGNED_WORD_MASK) |
| { |
| signed_extend_mask = 0xFFFF; |
| signed_extend_mask = signed_extend_mask >> shift_steps; |
| signed_extend_mask = ~signed_extend_mask; |
| } |
| value = value >> shift_steps; |
| value |= signed_extend_mask; |
| value &= 0xFFFF; |
| inst_env->reg[REG_PC] &= 0xFFFF0000; |
| inst_env->reg[REG_PC] |= value; |
| } |
| else if (cris_get_size (inst) == INST_DWORD_SIZE) |
| { |
| if (value & SIGNED_DWORD_MASK) |
| { |
| signed_extend_mask = 0xFFFFFFFF; |
| signed_extend_mask = signed_extend_mask >> shift_steps; |
| signed_extend_mask = ~signed_extend_mask; |
| } |
| value = value >> shift_steps; |
| value |= signed_extend_mask; |
| inst_env->reg[REG_PC] = value; |
| } |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Handles the ASRQ instruction. */ |
| |
| void |
| asrq_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| |
| int shift_steps; |
| unsigned long value; |
| unsigned long signed_extend_mask = 0; |
| |
| /* ASRQ can't have a prefix, so check that it doesn't. */ |
| if (inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| /* Check if the PC is the target register. */ |
| if (cris_get_operand2 (inst) == REG_PC) |
| { |
| |
| /* It's invalid to change the PC in a delay slot. */ |
| if (inst_env->slot_needed) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| /* The shift size is given as a 5 bit quick value, i.e. we don't |
| want the the sign bit of the quick value. */ |
| shift_steps = cris_get_asr_shift_steps (inst); |
| value = inst_env->reg[REG_PC]; |
| if (value & SIGNED_DWORD_MASK) |
| { |
| signed_extend_mask = 0xFFFFFFFF; |
| signed_extend_mask = signed_extend_mask >> shift_steps; |
| signed_extend_mask = ~signed_extend_mask; |
| } |
| value = value >> shift_steps; |
| value |= signed_extend_mask; |
| inst_env->reg[REG_PC] = value; |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Handles the AX, EI and SETF instruction. */ |
| |
| void |
| ax_ei_setf_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| if (inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| /* Check if the instruction is setting the X flag. */ |
| if (cris_is_xflag_bit_on (inst)) |
| { |
| inst_env->xflag_found = 1; |
| } |
| else |
| { |
| inst_env->xflag_found = 0; |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->disable_interrupt = 1; |
| } |
| |
| /* Checks if the instruction is in assign mode. If so, it updates the assign |
| register. Note that check_assign assumes that the caller has checked that |
| there is a prefix to this instruction. The mode check depends on this. */ |
| |
| void |
| check_assign (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* Check if it's an assign addressing mode. */ |
| if (cris_get_mode (inst) == PREFIX_ASSIGN_MODE) |
| { |
| /* Assign the prefix value to operand 1. */ |
| inst_env->reg[cris_get_operand1 (inst)] = inst_env->prefix_value; |
| } |
| } |
| |
| /* Handles the 2-operand BOUND instruction. */ |
| |
| void |
| two_operand_bound_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* It's invalid to have the PC as the index operand. */ |
| if (cris_get_operand2 (inst) == REG_PC) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| /* Check if we have a prefix. */ |
| if (inst_env->prefix_found) |
| { |
| check_assign (inst, inst_env); |
| } |
| /* Check if this is an autoincrement mode. */ |
| else if (cris_get_mode (inst) == AUTOINC_MODE) |
| { |
| /* It's invalid to change the PC in a delay slot. */ |
| if (inst_env->slot_needed) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| process_autoincrement (cris_get_size (inst), inst, inst_env); |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Handles the 3-operand BOUND instruction. */ |
| |
| void |
| three_operand_bound_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* It's an error if we haven't got a prefix. And it's also an error |
| if the PC is the destination register. */ |
| if ((!inst_env->prefix_found) || (cris_get_operand1 (inst) == REG_PC)) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Clears the status flags in inst_env. */ |
| |
| void |
| btst_nop_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* It's an error if we have got a prefix. */ |
| if (inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Clears the status flags in inst_env. */ |
| |
| void |
| clearf_di_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* It's an error if we have got a prefix. */ |
| if (inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 1; |
| } |
| |
| /* Handles the CLEAR instruction if it's in register mode. */ |
| |
| void |
| reg_mode_clear_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* Check if the target is the PC. */ |
| if (cris_get_operand2 (inst) == REG_PC) |
| { |
| /* The instruction will clear the instruction's size bits. */ |
| int clear_size = cris_get_clear_size (inst); |
| if (clear_size == INST_BYTE_SIZE) |
| { |
| inst_env->delay_slot_pc = inst_env->reg[REG_PC] & 0xFFFFFF00; |
| } |
| if (clear_size == INST_WORD_SIZE) |
| { |
| inst_env->delay_slot_pc = inst_env->reg[REG_PC] & 0xFFFF0000; |
| } |
| if (clear_size == INST_DWORD_SIZE) |
| { |
| inst_env->delay_slot_pc = 0x0; |
| } |
| /* The jump will be delayed with one delay slot. So we need a delay |
| slot. */ |
| inst_env->slot_needed = 1; |
| inst_env->delay_slot_pc_active = 1; |
| } |
| else |
| { |
| /* The PC will not change => no delay slot. */ |
| inst_env->slot_needed = 0; |
| } |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Handles the TEST instruction if it's in register mode. */ |
| |
| void |
| reg_mode_test_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* It's an error if we have got a prefix. */ |
| if (inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| |
| } |
| |
| /* Handles the CLEAR and TEST instruction if the instruction isn't |
| in register mode. */ |
| |
| void |
| none_reg_mode_clear_test_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* Check if we are in a prefix mode. */ |
| if (inst_env->prefix_found) |
| { |
| /* The only way the PC can change is if this instruction is in |
| assign addressing mode. */ |
| check_assign (inst, inst_env); |
| } |
| /* Indirect mode can't change the PC so just check if the mode is |
| autoincrement. */ |
| else if (cris_get_mode (inst) == AUTOINC_MODE) |
| { |
| process_autoincrement (cris_get_size (inst), inst, inst_env); |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Checks that the PC isn't the destination register or the instructions has |
| a prefix. */ |
| |
| void |
| dstep_logshift_mstep_neg_not_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* It's invalid to have the PC as the destination. The instruction can't |
| have a prefix. */ |
| if ((cris_get_operand2 (inst) == REG_PC) || inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Checks that the instruction doesn't have a prefix. */ |
| |
| void |
| break_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* The instruction can't have a prefix. */ |
| if (inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 1; |
| } |
| |
| /* Checks that the PC isn't the destination register and that the instruction |
| doesn't have a prefix. */ |
| |
| void |
| scc_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* It's invalid to have the PC as the destination. The instruction can't |
| have a prefix. */ |
| if ((cris_get_operand2 (inst) == REG_PC) || inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 1; |
| } |
| |
| /* Handles the register mode JUMP instruction. */ |
| |
| void |
| reg_mode_jump_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* It's invalid to do a JUMP in a delay slot. The mode is register, so |
| you can't have a prefix. */ |
| if ((inst_env->slot_needed) || (inst_env->prefix_found)) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| /* Just change the PC. */ |
| inst_env->reg[REG_PC] = inst_env->reg[cris_get_operand1 (inst)]; |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 1; |
| } |
| |
| /* Handles the JUMP instruction for all modes except register. */ |
| |
| void none_reg_mode_jump_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| unsigned long newpc; |
| CORE_ADDR address; |
| |
| /* It's invalid to do a JUMP in a delay slot. */ |
| if (inst_env->slot_needed) |
| { |
| inst_env->invalid = 1; |
| } |
| else |
| { |
| /* Check if we have a prefix. */ |
| if (inst_env->prefix_found) |
| { |
| check_assign (inst, inst_env); |
| |
| /* Get the new value for the the PC. */ |
| newpc = |
| read_memory_unsigned_integer ((CORE_ADDR) inst_env->prefix_value, |
| 4); |
| } |
| else |
| { |
| /* Get the new value for the PC. */ |
| address = (CORE_ADDR) inst_env->reg[cris_get_operand1 (inst)]; |
| newpc = read_memory_unsigned_integer (address, 4); |
| |
| /* Check if we should increment a register. */ |
| if (cris_get_mode (inst) == AUTOINC_MODE) |
| { |
| inst_env->reg[cris_get_operand1 (inst)] += 4; |
| } |
| } |
| inst_env->reg[REG_PC] = newpc; |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 1; |
| } |
| |
| /* Handles moves to special registers (aka P-register) for all modes. */ |
| |
| void |
| move_to_preg_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| if (inst_env->prefix_found) |
| { |
| /* The instruction has a prefix that means we are only interested if |
| the instruction is in assign mode. */ |
| if (cris_get_mode (inst) == PREFIX_ASSIGN_MODE) |
| { |
| /* The prefix handles the problem if we are in a delay slot. */ |
| if (cris_get_operand1 (inst) == REG_PC) |
| { |
| /* Just take care of the assign. */ |
| check_assign (inst, inst_env); |
| } |
| } |
| } |
| else if (cris_get_mode (inst) == AUTOINC_MODE) |
| { |
| /* The instruction doesn't have a prefix, the only case left that we |
| are interested in is the autoincrement mode. */ |
| if (cris_get_operand1 (inst) == REG_PC) |
| { |
| /* If the PC is to be incremented it's invalid to be in a |
| delay slot. */ |
| if (inst_env->slot_needed) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| /* The increment depends on the size of the special register. */ |
| if (cris_register_size (cris_get_operand2 (inst)) == 1) |
| { |
| process_autoincrement (INST_BYTE_SIZE, inst, inst_env); |
| } |
| else if (cris_register_size (cris_get_operand2 (inst)) == 2) |
| { |
| process_autoincrement (INST_WORD_SIZE, inst, inst_env); |
| } |
| else |
| { |
| process_autoincrement (INST_DWORD_SIZE, inst, inst_env); |
| } |
| } |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 1; |
| } |
| |
| /* Handles moves from special registers (aka P-register) for all modes |
| except register. */ |
| |
| void |
| none_reg_mode_move_from_preg_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| if (inst_env->prefix_found) |
| { |
| /* The instruction has a prefix that means we are only interested if |
| the instruction is in assign mode. */ |
| if (cris_get_mode (inst) == PREFIX_ASSIGN_MODE) |
| { |
| /* The prefix handles the problem if we are in a delay slot. */ |
| if (cris_get_operand1 (inst) == REG_PC) |
| { |
| /* Just take care of the assign. */ |
| check_assign (inst, inst_env); |
| } |
| } |
| } |
| /* The instruction doesn't have a prefix, the only case left that we |
| are interested in is the autoincrement mode. */ |
| else if (cris_get_mode (inst) == AUTOINC_MODE) |
| { |
| if (cris_get_operand1 (inst) == REG_PC) |
| { |
| /* If the PC is to be incremented it's invalid to be in a |
| delay slot. */ |
| if (inst_env->slot_needed) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| /* The increment depends on the size of the special register. */ |
| if (cris_register_size (cris_get_operand2 (inst)) == 1) |
| { |
| process_autoincrement (INST_BYTE_SIZE, inst, inst_env); |
| } |
| else if (cris_register_size (cris_get_operand2 (inst)) == 2) |
| { |
| process_autoincrement (INST_WORD_SIZE, inst, inst_env); |
| } |
| else |
| { |
| process_autoincrement (INST_DWORD_SIZE, inst, inst_env); |
| } |
| } |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 1; |
| } |
| |
| /* Handles moves from special registers (aka P-register) when the mode |
| is register. */ |
| |
| void |
| reg_mode_move_from_preg_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* Register mode move from special register can't have a prefix. */ |
| if (inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| if (cris_get_operand1 (inst) == REG_PC) |
| { |
| /* It's invalid to change the PC in a delay slot. */ |
| if (inst_env->slot_needed) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| /* The destination is the PC, the jump will have a delay slot. */ |
| inst_env->delay_slot_pc = inst_env->preg[cris_get_operand2 (inst)]; |
| inst_env->slot_needed = 1; |
| inst_env->delay_slot_pc_active = 1; |
| } |
| else |
| { |
| /* If the destination isn't PC, there will be no jump. */ |
| inst_env->slot_needed = 0; |
| } |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 1; |
| } |
| |
| /* Handles the MOVEM from memory to general register instruction. */ |
| |
| void |
| move_mem_to_reg_movem_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| if (inst_env->prefix_found) |
| { |
| /* The prefix handles the problem if we are in a delay slot. Is the |
| MOVEM instruction going to change the PC? */ |
| if (cris_get_operand2 (inst) >= REG_PC) |
| { |
| inst_env->reg[REG_PC] = |
| read_memory_unsigned_integer (inst_env->prefix_value, 4); |
| } |
| /* The assign value is the value after the increment. Normally, the |
| assign value is the value before the increment. */ |
| if ((cris_get_operand1 (inst) == REG_PC) |
| && (cris_get_mode (inst) == PREFIX_ASSIGN_MODE)) |
| { |
| inst_env->reg[REG_PC] = inst_env->prefix_value; |
| inst_env->reg[REG_PC] += 4 * (cris_get_operand2 (inst) + 1); |
| } |
| } |
| else |
| { |
| /* Is the MOVEM instruction going to change the PC? */ |
| if (cris_get_operand2 (inst) == REG_PC) |
| { |
| /* It's invalid to change the PC in a delay slot. */ |
| if (inst_env->slot_needed) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| inst_env->reg[REG_PC] = |
| read_memory_unsigned_integer (inst_env->reg[cris_get_operand1 (inst)], |
| 4); |
| } |
| /* The increment is not depending on the size, instead it's depending |
| on the number of registers loaded from memory. */ |
| if ((cris_get_operand1 (inst) == REG_PC) && (cris_get_mode (inst) == AUTOINC_MODE)) |
| { |
| /* It's invalid to change the PC in a delay slot. */ |
| if (inst_env->slot_needed) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| inst_env->reg[REG_PC] += 4 * (cris_get_operand2 (inst) + 1); |
| } |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Handles the MOVEM to memory from general register instruction. */ |
| |
| void |
| move_reg_to_mem_movem_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| if (inst_env->prefix_found) |
| { |
| /* The assign value is the value after the increment. Normally, the |
| assign value is the value before the increment. */ |
| if ((cris_get_operand1 (inst) == REG_PC) && |
| (cris_get_mode (inst) == PREFIX_ASSIGN_MODE)) |
| { |
| /* The prefix handles the problem if we are in a delay slot. */ |
| inst_env->reg[REG_PC] = inst_env->prefix_value; |
| inst_env->reg[REG_PC] += 4 * (cris_get_operand2 (inst) + 1); |
| } |
| } |
| else |
| { |
| /* The increment is not depending on the size, instead it's depending |
| on the number of registers loaded to memory. */ |
| if ((cris_get_operand1 (inst) == REG_PC) && (cris_get_mode (inst) == AUTOINC_MODE)) |
| { |
| /* It's invalid to change the PC in a delay slot. */ |
| if (inst_env->slot_needed) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| inst_env->reg[REG_PC] += 4 * (cris_get_operand2 (inst) + 1); |
| } |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Handles the pop instruction to a general register. |
| POP is a assembler macro for MOVE.D [SP+], Rd. */ |
| |
| void |
| reg_pop_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* POP can't have a prefix. */ |
| if (inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| if (cris_get_operand2 (inst) == REG_PC) |
| { |
| /* It's invalid to change the PC in a delay slot. */ |
| if (inst_env->slot_needed) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| inst_env->reg[REG_PC] = |
| read_memory_unsigned_integer (inst_env->reg[REG_SP], 4); |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Handles moves from register to memory. */ |
| |
| void |
| move_reg_to_mem_index_inc_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* Check if we have a prefix. */ |
| if (inst_env->prefix_found) |
| { |
| /* The only thing that can change the PC is an assign. */ |
| check_assign (inst, inst_env); |
| } |
| else if ((cris_get_operand1 (inst) == REG_PC) |
| && (cris_get_mode (inst) == AUTOINC_MODE)) |
| { |
| /* It's invalid to change the PC in a delay slot. */ |
| if (inst_env->slot_needed) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| process_autoincrement (cris_get_size (inst), inst, inst_env); |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Handles the intructions that's not yet implemented, by setting |
| inst_env->invalid to true. */ |
| |
| void |
| not_implemented_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| inst_env->invalid = 1; |
| } |
| |
| /* Handles the XOR instruction. */ |
| |
| void |
| xor_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* XOR can't have a prefix. */ |
| if (inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| /* Check if the PC is the target. */ |
| if (cris_get_operand2 (inst) == REG_PC) |
| { |
| /* It's invalid to change the PC in a delay slot. */ |
| if (inst_env->slot_needed) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| inst_env->reg[REG_PC] ^= inst_env->reg[cris_get_operand1 (inst)]; |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Handles the MULS instruction. */ |
| |
| void |
| muls_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* MULS/U can't have a prefix. */ |
| if (inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| /* Consider it invalid if the PC is the target. */ |
| if (cris_get_operand2 (inst) == REG_PC) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Handles the MULU instruction. */ |
| |
| void |
| mulu_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| /* MULS/U can't have a prefix. */ |
| if (inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| /* Consider it invalid if the PC is the target. */ |
| if (cris_get_operand2 (inst) == REG_PC) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Calculate the result of the instruction for ADD, SUB, CMP AND, OR and MOVE. |
| The MOVE instruction is the move from source to register. */ |
| |
| void |
| add_sub_cmp_and_or_move_action (unsigned short inst, inst_env_type *inst_env, |
| unsigned long source1, unsigned long source2) |
| { |
| unsigned long pc_mask; |
| unsigned long operation_mask; |
| |
| /* Find out how many bits the operation should apply to. */ |
| if (cris_get_size (inst) == INST_BYTE_SIZE) |
| { |
| pc_mask = 0xFFFFFF00; |
| operation_mask = 0xFF; |
| } |
| else if (cris_get_size (inst) == INST_WORD_SIZE) |
| { |
| pc_mask = 0xFFFF0000; |
| operation_mask = 0xFFFF; |
| } |
| else if (cris_get_size (inst) == INST_DWORD_SIZE) |
| { |
| pc_mask = 0x0; |
| operation_mask = 0xFFFFFFFF; |
| } |
| else |
| { |
| /* The size is out of range. */ |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| /* The instruction just works on uw_operation_mask bits. */ |
| source2 &= operation_mask; |
| source1 &= operation_mask; |
| |
| /* Now calculate the result. The opcode's 3 first bits separates |
| the different actions. */ |
| switch (cris_get_opcode (inst) & 7) |
| { |
| case 0: /* add */ |
| source1 += source2; |
| break; |
| |
| case 1: /* move */ |
| source1 = source2; |
| break; |
| |
| case 2: /* subtract */ |
| source1 -= source2; |
| break; |
| |
| case 3: /* compare */ |
| break; |
| |
| case 4: /* and */ |
| source1 &= source2; |
| break; |
| |
| case 5: /* or */ |
| source1 |= source2; |
| break; |
| |
| default: |
| inst_env->invalid = 1; |
| return; |
| |
| break; |
| } |
| |
| /* Make sure that the result doesn't contain more than the instruction |
| size bits. */ |
| source2 &= operation_mask; |
| |
| /* Calculate the new breakpoint address. */ |
| inst_env->reg[REG_PC] &= pc_mask; |
| inst_env->reg[REG_PC] |= source1; |
| |
| } |
| |
| /* Extends the value from either byte or word size to a dword. If the mode |
| is zero extend then the value is extended with zero. If instead the mode |
| is signed extend the sign bit of the value is taken into consideration. */ |
| |
| unsigned long |
| do_sign_or_zero_extend (unsigned long value, unsigned short *inst) |
| { |
| /* The size can be either byte or word, check which one it is. |
| Don't check the highest bit, it's indicating if it's a zero |
| or sign extend. */ |
| if (cris_get_size (*inst) & INST_WORD_SIZE) |
| { |
| /* Word size. */ |
| value &= 0xFFFF; |
| |
| /* Check if the instruction is signed extend. If so, check if value has |
| the sign bit on. */ |
| if (cris_is_signed_extend_bit_on (*inst) && (value & SIGNED_WORD_MASK)) |
| { |
| value |= SIGNED_WORD_EXTEND_MASK; |
| } |
| } |
| else |
| { |
| /* Byte size. */ |
| value &= 0xFF; |
| |
| /* Check if the instruction is signed extend. If so, check if value has |
| the sign bit on. */ |
| if (cris_is_signed_extend_bit_on (*inst) && (value & SIGNED_BYTE_MASK)) |
| { |
| value |= SIGNED_BYTE_EXTEND_MASK; |
| } |
| } |
| /* The size should now be dword. */ |
| cris_set_size_to_dword (inst); |
| return value; |
| } |
| |
| /* Handles the register mode for the ADD, SUB, CMP, AND, OR and MOVE |
| instruction. The MOVE instruction is the move from source to register. */ |
| |
| void |
| reg_mode_add_sub_cmp_and_or_move_op (unsigned short inst, |
| inst_env_type *inst_env) |
| { |
| unsigned long operand1; |
| unsigned long operand2; |
| |
| /* It's invalid to have a prefix to the instruction. This is a register |
| mode instruction and can't have a prefix. */ |
| if (inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| /* Check if the instruction has PC as its target. */ |
| if (cris_get_operand2 (inst) == REG_PC) |
| { |
| if (inst_env->slot_needed) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| /* The instruction has the PC as its target register. */ |
| operand1 = inst_env->reg[cris_get_operand1 (inst)]; |
| operand2 = inst_env->reg[REG_PC]; |
| |
| /* Check if it's a extend, signed or zero instruction. */ |
| if (cris_get_opcode (inst) < 4) |
| { |
| operand1 = do_sign_or_zero_extend (operand1, &inst); |
| } |
| /* Calculate the PC value after the instruction, i.e. where the |
| breakpoint should be. The order of the udw_operands is vital. */ |
| add_sub_cmp_and_or_move_action (inst, inst_env, operand2, operand1); |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Returns the data contained at address. The size of the data is derived from |
| the size of the operation. If the instruction is a zero or signed |
| extend instruction, the size field is changed in instruction. */ |
| |
| unsigned long |
| get_data_from_address (unsigned short *inst, CORE_ADDR address) |
| { |
| int size = cris_get_size (*inst); |
| unsigned long value; |
| |
| /* If it's an extend instruction we don't want the signed extend bit, |
| because it influences the size. */ |
| if (cris_get_opcode (*inst) < 4) |
| { |
| size &= ~SIGNED_EXTEND_BIT_MASK; |
| } |
| /* Is there a need for checking the size? Size should contain the number of |
| bytes to read. */ |
| size = 1 << size; |
| value = read_memory_unsigned_integer (address, size); |
| |
| /* Check if it's an extend, signed or zero instruction. */ |
| if (cris_get_opcode (*inst) < 4) |
| { |
| value = do_sign_or_zero_extend (value, inst); |
| } |
| return value; |
| } |
| |
| /* Handles the assign addresing mode for the ADD, SUB, CMP, AND, OR and MOVE |
| instructions. The MOVE instruction is the move from source to register. */ |
| |
| void |
| handle_prefix_assign_mode_for_aritm_op (unsigned short inst, |
| inst_env_type *inst_env) |
| { |
| unsigned long operand2; |
| unsigned long operand3; |
| |
| check_assign (inst, inst_env); |
| if (cris_get_operand2 (inst) == REG_PC) |
| { |
| operand2 = inst_env->reg[REG_PC]; |
| |
| /* Get the value of the third operand. */ |
| operand3 = get_data_from_address (&inst, inst_env->prefix_value); |
| |
| /* Calculate the PC value after the instruction, i.e. where the |
| breakpoint should be. The order of the udw_operands is vital. */ |
| add_sub_cmp_and_or_move_action (inst, inst_env, operand2, operand3); |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Handles the three-operand addressing mode for the ADD, SUB, CMP, AND and |
| OR instructions. Note that for this to work as expected, the calling |
| function must have made sure that there is a prefix to this instruction. */ |
| |
| void |
| three_operand_add_sub_cmp_and_or_op (unsigned short inst, |
| inst_env_type *inst_env) |
| { |
| unsigned long operand2; |
| unsigned long operand3; |
| |
| if (cris_get_operand1 (inst) == REG_PC) |
| { |
| /* The PC will be changed by the instruction. */ |
| operand2 = inst_env->reg[cris_get_operand2 (inst)]; |
| |
| /* Get the value of the third operand. */ |
| operand3 = get_data_from_address (&inst, inst_env->prefix_value); |
| |
| /* Calculate the PC value after the instruction, i.e. where the |
| breakpoint should be. */ |
| add_sub_cmp_and_or_move_action (inst, inst_env, operand2, operand3); |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Handles the index addresing mode for the ADD, SUB, CMP, AND, OR and MOVE |
| instructions. The MOVE instruction is the move from source to register. */ |
| |
| void |
| handle_prefix_index_mode_for_aritm_op (unsigned short inst, |
| inst_env_type *inst_env) |
| { |
| if (cris_get_operand1 (inst) != cris_get_operand2 (inst)) |
| { |
| /* If the instruction is MOVE it's invalid. If the instruction is ADD, |
| SUB, AND or OR something weird is going on (if everything works these |
| instructions should end up in the three operand version). */ |
| inst_env->invalid = 1; |
| return; |
| } |
| else |
| { |
| /* three_operand_add_sub_cmp_and_or does the same as we should do here |
| so use it. */ |
| three_operand_add_sub_cmp_and_or_op (inst, inst_env); |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Handles the autoincrement and indirect addresing mode for the ADD, SUB, |
| CMP, AND OR and MOVE instruction. The MOVE instruction is the move from |
| source to register. */ |
| |
| void |
| handle_inc_and_index_mode_for_aritm_op (unsigned short inst, |
| inst_env_type *inst_env) |
| { |
| unsigned long operand1; |
| unsigned long operand2; |
| unsigned long operand3; |
| int size; |
| |
| /* The instruction is either an indirect or autoincrement addressing mode. |
| Check if the destination register is the PC. */ |
| if (cris_get_operand2 (inst) == REG_PC) |
| { |
| /* Must be done here, get_data_from_address may change the size |
| field. */ |
| size = cris_get_size (inst); |
| operand2 = inst_env->reg[REG_PC]; |
| |
| /* Get the value of the third operand, i.e. the indirect operand. */ |
| operand1 = inst_env->reg[cris_get_operand1 (inst)]; |
| operand3 = get_data_from_address (&inst, operand1); |
| |
| /* Calculate the PC value after the instruction, i.e. where the |
| breakpoint should be. The order of the udw_operands is vital. */ |
| add_sub_cmp_and_or_move_action (inst, inst_env, operand2, operand3); |
| } |
| /* If this is an autoincrement addressing mode, check if the increment |
| changes the PC. */ |
| if ((cris_get_operand1 (inst) == REG_PC) && (cris_get_mode (inst) == AUTOINC_MODE)) |
| { |
| /* Get the size field. */ |
| size = cris_get_size (inst); |
| |
| /* If it's an extend instruction we don't want the signed extend bit, |
| because it influences the size. */ |
| if (cris_get_opcode (inst) < 4) |
| { |
| size &= ~SIGNED_EXTEND_BIT_MASK; |
| } |
| process_autoincrement (size, inst, inst_env); |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Handles the two-operand addressing mode, all modes except register, for |
| the ADD, SUB CMP, AND and OR instruction. */ |
| |
| void |
| none_reg_mode_add_sub_cmp_and_or_move_op (unsigned short inst, |
| inst_env_type *inst_env) |
| { |
| if (inst_env->prefix_found) |
| { |
| if (cris_get_mode (inst) == PREFIX_INDEX_MODE) |
| { |
| handle_prefix_index_mode_for_aritm_op (inst, inst_env); |
| } |
| else if (cris_get_mode (inst) == PREFIX_ASSIGN_MODE) |
| { |
| handle_prefix_assign_mode_for_aritm_op (inst, inst_env); |
| } |
| else |
| { |
| /* The mode is invalid for a prefixed base instruction. */ |
| inst_env->invalid = 1; |
| return; |
| } |
| } |
| else |
| { |
| handle_inc_and_index_mode_for_aritm_op (inst, inst_env); |
| } |
| } |
| |
| /* Handles the quick addressing mode for the ADD and SUB instruction. */ |
| |
| void |
| quick_mode_add_sub_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| unsigned long operand1; |
| unsigned long operand2; |
| |
| /* It's a bad idea to be in a prefix instruction now. This is a quick mode |
| instruction and can't have a prefix. */ |
| if (inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| |
| /* Check if the instruction has PC as its target. */ |
| if (cris_get_operand2 (inst) == REG_PC) |
| { |
| if (inst_env->slot_needed) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| operand1 = cris_get_quick_value (inst); |
| operand2 = inst_env->reg[REG_PC]; |
| |
| /* The size should now be dword. */ |
| cris_set_size_to_dword (&inst); |
| |
| /* Calculate the PC value after the instruction, i.e. where the |
| breakpoint should be. */ |
| add_sub_cmp_and_or_move_action (inst, inst_env, operand2, operand1); |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Handles the quick addressing mode for the CMP, AND and OR instruction. */ |
| |
| void |
| quick_mode_and_cmp_move_or_op (unsigned short inst, inst_env_type *inst_env) |
| { |
| unsigned long operand1; |
| unsigned long operand2; |
| |
| /* It's a bad idea to be in a prefix instruction now. This is a quick mode |
| instruction and can't have a prefix. */ |
| if (inst_env->prefix_found) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| /* Check if the instruction has PC as its target. */ |
| if (cris_get_operand2 (inst) == REG_PC) |
| { |
| if (inst_env->slot_needed) |
| { |
| inst_env->invalid = 1; |
| return; |
| } |
| /* The instruction has the PC as its target register. */ |
| operand1 = cris_get_quick_value (inst); |
| operand2 = inst_env->reg[REG_PC]; |
| |
| /* The quick value is signed, so check if we must do a signed extend. */ |
| if (operand1 & SIGNED_QUICK_VALUE_MASK) |
| { |
| /* sign extend */ |
| operand1 |= SIGNED_QUICK_VALUE_EXTEND_MASK; |
| } |
| /* The size should now be dword. */ |
| cris_set_size_to_dword (&inst); |
| |
| /* Calculate the PC value after the instruction, i.e. where the |
| breakpoint should be. */ |
| add_sub_cmp_and_or_move_action (inst, inst_env, operand2, operand1); |
| } |
| inst_env->slot_needed = 0; |
| inst_env->prefix_found = 0; |
| inst_env->xflag_found = 0; |
| inst_env->disable_interrupt = 0; |
| } |
| |
| /* Translate op_type to a function and call it. */ |
| |
| static void cris_gdb_func (enum cris_op_type op_type, unsigned short inst, |
| inst_env_type *inst_env) |
| { |
| switch (op_type) |
| { |
| case cris_not_implemented_op: |
| not_implemented_op (inst, inst_env); |
| break; |
| |
| case cris_abs_op: |
| abs_op (inst, inst_env); |
| break; |
| |
| case cris_addi_op: |
| addi_op (inst, inst_env); |
| break; |
| |
| case cris_asr_op: |
| asr_op (inst, inst_env); |
| break; |
| |
| case cris_asrq_op: |
| asrq_op (inst, inst_env); |
| break; |
| |
| case cris_ax_ei_setf_op: |
| ax_ei_setf_op (inst, inst_env); |
| break; |
| |
| case cris_bdap_prefix: |
| bdap_prefix (inst, inst_env); |
| break; |
| |
| case cris_biap_prefix: |
| biap_prefix (inst, inst_env); |
| break; |
| |
| case cris_break_op: |
| break_op (inst, inst_env); |
| break; |
| |
| case cris_btst_nop_op: |
| btst_nop_op (inst, inst_env); |
| break; |
| |
| case cris_clearf_di_op: |
| clearf_di_op (inst, inst_env); |
| break; |
| |
| case cris_dip_prefix: |
| dip_prefix (inst, inst_env); |
| break; |
| |
| case cris_dstep_logshift_mstep_neg_not_op: |
| dstep_logshift_mstep_neg_not_op (inst, inst_env); |
| break; |
| |
| case cris_eight_bit_offset_branch_op: |
| eight_bit_offset_branch_op (inst, inst_env); |
| break; |
| |
| case cris_move_mem_to_reg_movem_op: |
| move_mem_to_reg_movem_op (inst, inst_env); |
| break; |
| |
| case cris_move_reg_to_mem_movem_op: |
| move_reg_to_mem_movem_op (inst, inst_env); |
| break; |
| |
| case cris_move_to_preg_op: |
| move_to_preg_op (inst, inst_env); |
| break; |
| |
| case cris_muls_op: |
| muls_op (inst, inst_env); |
| break; |
| |
| case cris_mulu_op: |
| mulu_op (inst, inst_env); |
| break; |
| |
| case cris_none_reg_mode_add_sub_cmp_and_or_move_op: |
| none_reg_mode_add_sub_cmp_and_or_move_op (inst, inst_env); |
| break; |
| |
| case cris_none_reg_mode_clear_test_op: |
| none_reg_mode_clear_test_op (inst, inst_env); |
| break; |
| |
| case cris_none_reg_mode_jump_op: |
| none_reg_mode_jump_op (inst, inst_env); |
| break; |
| |
| case cris_none_reg_mode_move_from_preg_op: |
| none_reg_mode_move_from_preg_op (inst, inst_env); |
| break; |
| |
| case cris_quick_mode_add_sub_op: |
| quick_mode_add_sub_op (inst, inst_env); |
| break; |
| |
| case cris_quick_mode_and_cmp_move_or_op: |
| quick_mode_and_cmp_move_or_op (inst, inst_env); |
| break; |
| |
| case cris_quick_mode_bdap_prefix: |
| quick_mode_bdap_prefix (inst, inst_env); |
| break; |
| |
| case cris_reg_mode_add_sub_cmp_and_or_move_op: |
| reg_mode_add_sub_cmp_and_or_move_op (inst, inst_env); |
| break; |
| |
| case cris_reg_mode_clear_op: |
| reg_mode_clear_op (inst, inst_env); |
| break; |
| |
| case cris_reg_mode_jump_op: |
| reg_mode_jump_op (inst, inst_env); |
| break; |
| |
| case cris_reg_mode_move_from_preg_op: |
| reg_mode_move_from_preg_op (inst, inst_env); |
| break; |
| |
| case cris_reg_mode_test_op: |
| reg_mode_test_op (inst, inst_env); |
| break; |
| |
| case cris_scc_op: |
| scc_op (inst, inst_env); |
| break; |
| |
| case cris_sixteen_bit_offset_branch_op: |
| sixteen_bit_offset_branch_op (inst, inst_env); |
| break; |
| |
| case cris_three_operand_add_sub_cmp_and_or_op: |
| three_operand_add_sub_cmp_and_or_op (inst, inst_env); |
| break; |
| |
| case cris_three_operand_bound_op: |
| three_operand_bound_op (inst, inst_env); |
| break; |
| |
| case cris_two_operand_bound_op: |
| two_operand_bound_op (inst, inst_env); |
| break; |
| |
| case cris_xor_op: |
| xor_op (inst, inst_env); |
| break; |
| } |
| } |
| |
| /* This wrapper is to avoid cris_get_assembler being called before |
| exec_bfd has been set. */ |
| |
| static int |
| cris_delayed_get_disassembler (bfd_vma addr, disassemble_info *info) |
| { |
| deprecated_tm_print_insn = cris_get_disassembler (exec_bfd); |
| return TARGET_PRINT_INSN (addr, info); |
| } |
| |
| /* Copied from <asm/elf.h>. */ |
| typedef unsigned long elf_greg_t; |
| |
| /* Same as user_regs_struct struct in <asm/user.h>. */ |
| typedef elf_greg_t elf_gregset_t[35]; |
| |
| /* Unpack an elf_gregset_t into GDB's register cache. */ |
| |
| void |
| supply_gregset (elf_gregset_t *gregsetp) |
| { |
| int i; |
| elf_greg_t *regp = *gregsetp; |
| static char zerobuf[4] = {0}; |
| |
| /* The kernel dumps all 32 registers as unsigned longs, but supply_register |
| knows about the actual size of each register so that's no problem. */ |
| for (i = 0; i < NUM_GENREGS + NUM_SPECREGS; i++) |
| { |
| supply_register (i, (char *)®p[i]); |
| } |
| } |
| |
| /* Use a local version of this function to get the correct types for |
| regsets, until multi-arch core support is ready. */ |
| |
| static void |
| fetch_core_registers (char *core_reg_sect, unsigned core_reg_size, |
| int which, CORE_ADDR reg_addr) |
| { |
| elf_gregset_t gregset; |
| |
| switch (which) |
| { |
| case 0: |
| if (core_reg_size != sizeof (gregset)) |
| { |
| warning ("wrong size gregset struct in core file"); |
| } |
| else |
| { |
| memcpy (&gregset, core_reg_sect, sizeof (gregset)); |
| supply_gregset (&gregset); |
| } |
| |
| default: |
| /* We've covered all the kinds of registers we know about here, |
| so this must be something we wouldn't know what to do with |
| anyway. Just ignore it. */ |
| break; |
| } |
| } |
| |
| static struct core_fns cris_elf_core_fns = |
| { |
| bfd_target_elf_flavour, /* core_flavour */ |
| default_check_format, /* check_format */ |
| default_core_sniffer, /* core_sniffer */ |
| fetch_core_registers, /* core_read_registers */ |
| NULL /* next */ |
| }; |
| |
| /* Fetch (and possibly build) an appropriate link_map_offsets |
| structure for native GNU/Linux CRIS targets using the struct |
| offsets defined in link.h (but without actual reference to that |
| file). |
| |
| This makes it possible to access GNU/Linux CRIS shared libraries |
| from a GDB that was not built on an GNU/Linux CRIS host (for cross |
| debugging). |
| |
| See gdb/solib-svr4.h for an explanation of these fields. */ |
| |
| struct link_map_offsets * |
| cris_linux_svr4_fetch_link_map_offsets (void) |
| { |
| static struct link_map_offsets lmo; |
| static struct link_map_offsets *lmp = NULL; |
| |
| if (lmp == NULL) |
| { |
| lmp = &lmo; |
| |
| lmo.r_debug_size = 8; /* The actual size is 20 bytes, but |
| this is all we need. */ |
| lmo.r_map_offset = 4; |
| lmo.r_map_size = 4; |
| |
| lmo.link_map_size = 20; |
| |
| lmo.l_addr_offset = 0; |
| lmo.l_addr_size = 4; |
| |
| lmo.l_name_offset = 4; |
| lmo.l_name_size = 4; |
| |
| lmo.l_next_offset = 12; |
| lmo.l_next_size = 4; |
| |
| lmo.l_prev_offset = 16; |
| lmo.l_prev_size = 4; |
| } |
| |
| return lmp; |
| } |
| |
| static void |
| cris_fpless_backtrace (char *noargs, int from_tty) |
| { |
| /* Points at the instruction after the jsr (except when in innermost frame |
| where it points at the original pc). */ |
| CORE_ADDR pc = 0; |
| |
| /* Temporary variable, used for parsing from the start of the function that |
| the pc is in, up to the pc. */ |
| CORE_ADDR tmp_pc = 0; |
| CORE_ADDR sp = 0; |
| |
| /* Information about current frame. */ |
| struct symtab_and_line sal; |
| char* func_name; |
| |
| /* Present instruction. */ |
| unsigned short insn; |
| |
| /* Next instruction, lookahead. */ |
| unsigned short insn_next; |
| |
| /* This is to store the offset between sp at start of function and until we |
| reach push srp (if any). */ |
| int sp_add_later = 0; |
| int push_srp_found = 0; |
| |
| int val = 0; |
| |
| /* Frame counter. */ |
| int frame = 0; |
| |
| /* For the innermost frame, we want to look at srp in case it's a leaf |
| function (since there's no push srp in that case). */ |
| int innermost_frame = 1; |
| |
| deprecated_read_register_gen (PC_REGNUM, (char *) &pc); |
| deprecated_read_register_gen (SP_REGNUM, (char *) &sp); |
| |
| /* We make an explicit return when we can't find an outer frame. */ |
| while (1) |
| { |
| /* Get file name and line number. */ |
| sal = find_pc_line (pc, 0); |
| |
| /* Get function name. */ |
| find_pc_partial_function (pc, &func_name, (CORE_ADDR *) NULL, |
| (CORE_ADDR *) NULL); |
| |
| /* Print information about current frame. */ |
| printf_unfiltered ("#%i 0x%08lx in %s", frame++, pc, func_name); |
| if (sal.symtab) |
| { |
| printf_unfiltered (" at %s:%i", sal.symtab->filename, sal.line); |
| } |
| printf_unfiltered ("\n"); |
| |
| /* Get the start address of this function. */ |
| tmp_pc = get_pc_function_start (pc); |
| |
| /* Mini parser, only meant to find push sp and sub ...,sp from the start |
| of the function, up to the pc. */ |
| while (tmp_pc < pc) |
| { |
| insn = read_memory_unsigned_integer (tmp_pc, sizeof (short)); |
| tmp_pc += sizeof (short); |
| if (insn == 0xE1FC) |
| { |
| /* push <reg> 32 bit instruction */ |
| insn_next = read_memory_unsigned_integer (tmp_pc, |
| sizeof (short)); |
| tmp_pc += sizeof (short); |
| |
| /* Recognize srp. */ |
| if (insn_next == 0xBE7E) |
| { |
| /* For subsequent (not this one though) push or sub which |
| affects sp, adjust sp immediately. */ |
| push_srp_found = 1; |
| |
| /* Note: this will break if we ever encounter a |
| push vr (1 byte) or push ccr (2 bytes). */ |
| sp_add_later += 4; |
| } |
| else |
| { |
| /* Some other register was pushed. */ |
| if (push_srp_found) |
| { |
| sp += 4; |
| } |
| else |
| { |
| sp_add_later += 4; |
| } |
| } |
| } |
| else if (cris_get_operand2 (insn) == SP_REGNUM |
| && cris_get_mode (insn) == 0x0000 |
| && cris_get_opcode (insn) == 0x000A) |
| { |
| /* subq <val>,sp */ |
| val = cris_get_quick_value (insn); |
| |
| if (push_srp_found) |
| { |
| sp += val; |
| } |
| else |
| { |
| sp_add_later += val; |
| } |
| |
| } |
| else if (cris_get_operand2 (insn) == SP_REGNUM |
| /* Autoincrement addressing mode. */ |
| && cris_get_mode (insn) == 0x0003 |
| /* Opcode. */ |
| && ((insn) & 0x03E0) >> 5 == 0x0004) |
| { |
| /* subu <val>,sp */ |
| val = get_data_from_address (&insn, tmp_pc); |
| |
| if (push_srp_found) |
| { |
| sp += val; |
| } |
| else |
| { |
| sp_add_later += val; |
| } |
| } |
| else if (cris_get_operand2 (insn) == SP_REGNUM |
| && ((insn & 0x0F00) >> 8) == 0x0001 |
| && (cris_get_signed_offset (insn) < 0)) |
| { |
| /* Immediate byte offset addressing prefix word with sp as base |
| register. Used for CRIS v8 i.e. ETRAX 100 and newer if <val> |
| is between 64 and 128. |
| movem r<regsave>,[sp=sp-<val>] */ |
| val = -cris_get_signed_offset (insn); |
| insn_next = read_memory_unsigned_integer (tmp_pc, |
| sizeof (short)); |
| tmp_pc += sizeof (short); |
| |
| if (cris_get_mode (insn_next) == PREFIX_ASSIGN_MODE |
| && cris_get_opcode (insn_next) == 0x000F |
| && cris_get_size (insn_next) == 0x0003 |
| && cris_get_operand1 (insn_next) == SP_REGNUM) |
| { |
| if (push_srp_found) |
| { |
| sp += val; |
| } |
| else |
| { |
| sp_add_later += val; |
| } |
| } |
| } |
| } |
| |
| if (push_srp_found) |
| { |
| /* Reset flag. */ |
| push_srp_found = 0; |
| |
| /* sp should now point at where srp is stored on the stack. Update |
| the pc to the srp. */ |
| pc = read_memory_unsigned_integer (sp, 4); |
| } |
| else if (innermost_frame) |
| { |
| /* We couldn't find a push srp in the prologue, so this must be |
| a leaf function, and thus we use the srp register directly. |
| This should happen at most once, for the innermost function. */ |
| deprecated_read_register_gen (SRP_REGNUM, (char *) &pc); |
| } |
| else |
| { |
| /* Couldn't find an outer frame. */ |
| return; |
| } |
| |
| /* Reset flag. (In case the innermost frame wasn't a leaf, we don't |
| want to look at the srp register later either). */ |
| innermost_frame = 0; |
| |
| /* Now, add the offset for everything up to, and including push srp, |
| that was held back during the prologue parsing. */ |
| sp += sp_add_later; |
| sp_add_later = 0; |
| } |
| } |
| |
| void |
| _initialize_cris_tdep (void) |
| { |
| struct cmd_list_element *c; |
| |
| gdbarch_register (bfd_arch_cris, cris_gdbarch_init, cris_dump_tdep); |
| |
| /* Used in disassembly. */ |
| deprecated_tm_print_insn = cris_delayed_get_disassembler; |
| |
| /* CRIS-specific user-commands. */ |
| c = add_set_cmd ("cris-version", class_support, var_integer, |
| (char *) &usr_cmd_cris_version, |
| "Set the current CRIS version.", &setlist); |
| set_cmd_sfunc (c, cris_version_update); |
| add_show_from_set (c, &showlist); |
| |
| c = add_set_enum_cmd ("cris-mode", class_support, cris_mode_enums, |
| &usr_cmd_cris_mode, |
| "Set the current CRIS mode.", &setlist); |
| set_cmd_sfunc (c, cris_mode_update); |
| add_show_from_set (c, &showlist); |
| |
| c = add_set_enum_cmd ("cris-abi", class_support, cris_abi_enums, |
| &usr_cmd_cris_abi, |
| "Set the current CRIS ABI version.", &setlist); |
| set_cmd_sfunc (c, cris_abi_update); |
| add_show_from_set (c, &showlist); |
| |
| c = add_cmd ("cris-fpless-backtrace", class_support, cris_fpless_backtrace, |
| "Display call chain using the subroutine return pointer.\n" |
| "Note that this displays the address after the jump to the " |
| "subroutine.", &cmdlist); |
| |
| add_core_fns (&cris_elf_core_fns); |
| |
| } |
| |
| /* Prints out all target specific values. */ |
| |
| static void |
| cris_dump_tdep (struct gdbarch *gdbarch, struct ui_file *file) |
| { |
| struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); |
| if (tdep != NULL) |
| { |
| fprintf_unfiltered (file, "cris_dump_tdep: tdep->cris_version = %i\n", |
| tdep->cris_version); |
| fprintf_unfiltered (file, "cris_dump_tdep: tdep->cris_mode = %s\n", |
| tdep->cris_mode); |
| fprintf_unfiltered (file, "cris_dump_tdep: tdep->cris_abi = %s\n", |
| tdep->cris_abi); |
| |
| } |
| } |
| |
| static void |
| cris_version_update (char *ignore_args, int from_tty, |
| struct cmd_list_element *c) |
| { |
| struct gdbarch_info info; |
| |
| /* NOTE: cagney/2002-03-17: The add_show_from_set() function clones |
| the set command passed as a parameter. The clone operation will |
| include (BUG?) any ``set'' command callback, if present. |
| Commands like ``info set'' call all the ``show'' command |
| callbacks. Unfortunatly, for ``show'' commands cloned from |
| ``set'', this includes callbacks belonging to ``set'' commands. |
| Making this worse, this only occures if add_show_from_set() is |
| called after add_cmd_sfunc() (BUG?). */ |
| |
| /* From here on, trust the user's CRIS version setting. */ |
| if (cmd_type (c) == set_cmd) |
| { |
| usr_cmd_cris_version_valid = 1; |
| |
| /* Update the current architecture, if needed. */ |
| gdbarch_info_init (&info); |
| if (!gdbarch_update_p (info)) |
| internal_error (__FILE__, __LINE__, "cris_gdbarch_update: failed to update architecture."); |
| } |
| } |
| |
| static void |
| cris_mode_update (char *ignore_args, int from_tty, |
| struct cmd_list_element *c) |
| { |
| struct gdbarch_info info; |
| |
| /* NOTE: cagney/2002-03-17: The add_show_from_set() function clones |
| the set command passed as a parameter. The clone operation will |
| include (BUG?) any ``set'' command callback, if present. |
| Commands like ``info set'' call all the ``show'' command |
| callbacks. Unfortunatly, for ``show'' commands cloned from |
| ``set'', this includes callbacks belonging to ``set'' commands. |
| Making this worse, this only occures if add_show_from_set() is |
| called after add_cmd_sfunc() (BUG?). */ |
| |
| /* From here on, trust the user's CRIS mode setting. */ |
| if (cmd_type (c) == set_cmd) |
| { |
| usr_cmd_cris_mode_valid = 1; |
| |
| /* Update the current architecture, if needed. */ |
| gdbarch_info_init (&info); |
| if (!gdbarch_update_p (info)) |
| internal_error (__FILE__, __LINE__, "cris_gdbarch_update: failed to update architecture."); |
| } |
| } |
| |
| static void |
| cris_abi_update (char *ignore_args, int from_tty, |
| struct cmd_list_element *c) |
| { |
| struct gdbarch_info info; |
| |
| /* NOTE: cagney/2002-03-17: The add_show_from_set() function clones |
| the set command passed as a parameter. The clone operation will |
| include (BUG?) any ``set'' command callback, if present. |
| Commands like ``info set'' call all the ``show'' command |
| callbacks. Unfortunatly, for ``show'' commands cloned from |
| ``set'', this includes callbacks belonging to ``set'' commands. |
| Making this worse, this only occures if add_show_from_set() is |
| called after add_cmd_sfunc() (BUG?). */ |
| |
| /* From here on, trust the user's CRIS ABI setting. */ |
| if (cmd_type (c) == set_cmd) |
| { |
| usr_cmd_cris_abi_valid = 1; |
| |
| /* Update the current architecture, if needed. */ |
| gdbarch_info_init (&info); |
| if (!gdbarch_update_p (info)) |
| internal_error (__FILE__, __LINE__, "cris_gdbarch_update: failed to update architecture."); |
| } |
| } |
| |
| /* Copied from pa64solib.c, with a couple of minor changes. */ |
| |
| static CORE_ADDR |
| bfd_lookup_symbol (bfd *abfd, const char *symname) |
| { |
| unsigned int storage_needed; |
| asymbol *sym; |
| asymbol **symbol_table; |
| unsigned int number_of_symbols; |
| unsigned int i; |
| struct cleanup *back_to; |
| CORE_ADDR symaddr = 0; |
| |
| storage_needed = bfd_get_symtab_upper_bound (abfd); |
| |
| if (storage_needed > 0) |
| { |
| symbol_table = (asymbol **) xmalloc (storage_needed); |
| back_to = make_cleanup (free, symbol_table); |
| number_of_symbols = bfd_canonicalize_symtab (abfd, symbol_table); |
| |
| for (i = 0; i < number_of_symbols; i++) |
| { |
| sym = *symbol_table++; |
| if (!strcmp (sym->name, symname)) |
| { |
| /* Bfd symbols are section relative. */ |
| symaddr = sym->value + sym->section->vma; |
| break; |
| } |
| } |
| do_cleanups (back_to); |
| } |
| return (symaddr); |
| } |
| |
| static struct gdbarch * |
| cris_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) |
| { |
| struct gdbarch *gdbarch; |
| struct gdbarch_tdep *tdep; |
| int cris_version; |
| const char *cris_mode; |
| const char *cris_abi; |
| CORE_ADDR cris_abi_sym = 0; |
| int register_bytes; |
| |
| if (usr_cmd_cris_version_valid) |
| { |
| /* Trust the user's CRIS version setting. */ |
| cris_version = usr_cmd_cris_version; |
| } |
| else |
| { |
| /* Assume it's CRIS version 10. */ |
| cris_version = 10; |
| } |
| |
| if (usr_cmd_cris_mode_valid) |
| { |
| /* Trust the user's CRIS mode setting. */ |
| cris_mode = usr_cmd_cris_mode; |
| } |
| else if (cris_version == 10) |
| { |
| /* Assume CRIS version 10 is in user mode. */ |
| cris_mode = CRIS_MODE_USER; |
| } |
| else |
| { |
| /* Strictly speaking, older CRIS version don't have a supervisor mode, |
| but we regard its only mode as supervisor mode. */ |
| cris_mode = CRIS_MODE_SUPERVISOR; |
| } |
| |
| if (usr_cmd_cris_abi_valid) |
| { |
| /* Trust the user's ABI setting. */ |
| cris_abi = usr_cmd_cris_abi; |
| } |
| else if (info.abfd) |
| { |
| if (bfd_get_flavour (info.abfd) == bfd_target_elf_flavour) |
| { |
| /* An elf target uses the new ABI. */ |
| cris_abi = CRIS_ABI_V2; |
| } |
| else if (bfd_get_flavour (info.abfd) == bfd_target_aout_flavour) |
| { |
| /* An a.out target may use either ABI. Look for hints in the |
| symbol table. */ |
| cris_abi_sym = bfd_lookup_symbol (info.abfd, CRIS_ABI_SYMBOL); |
| cris_abi = cris_abi_sym ? CRIS_ABI_V2 : CRIS_ABI_ORIGINAL; |
| } |
| else |
| { |
| /* Unknown bfd flavour. Assume it's the new ABI. */ |
| cris_abi = CRIS_ABI_V2; |
| } |
| } |
| else if (arches != NULL) |
| { |
| /* No bfd available. Stick with the ABI from the most recently |
| selected architecture of this same family (the head of arches |
| always points to this). (This is to avoid changing the ABI |
| when the user updates the architecture with the 'set |
| cris-version' command.) */ |
| cris_abi = gdbarch_tdep (arches->gdbarch)->cris_abi; |
| } |
| else |
| { |
| /* No bfd, and no previously selected architecture available. |
| Assume it's the new ABI. */ |
| cris_abi = CRIS_ABI_V2; |
| } |
| |
| /* Make the current settings visible to the user. */ |
| usr_cmd_cris_version = cris_version; |
| usr_cmd_cris_mode = cris_mode; |
| usr_cmd_cris_abi = cris_abi; |
| |
| /* Find a candidate among the list of pre-declared architectures. Both |
| CRIS version and ABI must match. */ |
| for (arches = gdbarch_list_lookup_by_info (arches, &info); |
| arches != NULL; |
| arches = gdbarch_list_lookup_by_info (arches->next, &info)) |
| { |
| if ((gdbarch_tdep (arches->gdbarch)->cris_version == cris_version) |
| && (gdbarch_tdep (arches->gdbarch)->cris_mode == cris_mode) |
| && (gdbarch_tdep (arches->gdbarch)->cris_abi == cris_abi)) |
| return arches->gdbarch; |
| } |
| |
| /* No matching architecture was found. Create a new one. */ |
| tdep = (struct gdbarch_tdep *) xmalloc (sizeof (struct gdbarch_tdep)); |
| gdbarch = gdbarch_alloc (&info, tdep); |
| |
| /* NOTE: cagney/2002-12-06: This can be deleted when this arch is |
| ready to unwind the PC first (see frame.c:get_prev_frame()). */ |
| set_gdbarch_deprecated_init_frame_pc (gdbarch, init_frame_pc_default); |
| |
| tdep->cris_version = cris_version; |
| tdep->cris_mode = cris_mode; |
| tdep->cris_abi = cris_abi; |
| |
| /* INIT shall ensure that the INFO.BYTE_ORDER is non-zero. */ |
| switch (info.byte_order) |
| { |
| case BFD_ENDIAN_LITTLE: |
| /* Ok. */ |
| break; |
| |
| case BFD_ENDIAN_BIG: |
| internal_error (__FILE__, __LINE__, "cris_gdbarch_init: big endian byte order in info"); |
| break; |
| |
| default: |
| internal_error (__FILE__, __LINE__, "cris_gdbarch_init: unknown byte order in info"); |
| } |
| |
| /* Initialize the ABI dependent things. */ |
| if (tdep->cris_abi == CRIS_ABI_ORIGINAL) |
| { |
| set_gdbarch_double_bit (gdbarch, 32); |
| set_gdbarch_deprecated_push_arguments (gdbarch, cris_abi_original_push_arguments); |
| set_gdbarch_deprecated_store_return_value (gdbarch, |
| cris_abi_original_store_return_value); |
| set_gdbarch_deprecated_extract_return_value |
| (gdbarch, cris_abi_original_extract_return_value); |
| set_gdbarch_reg_struct_has_addr |
| (gdbarch, cris_abi_original_reg_struct_has_addr); |
| } |
| else if (tdep->cris_abi == CRIS_ABI_V2) |
| { |
| set_gdbarch_double_bit (gdbarch, 64); |
| set_gdbarch_deprecated_push_arguments (gdbarch, cris_abi_v2_push_arguments); |
| set_gdbarch_deprecated_store_return_value (gdbarch, cris_abi_v2_store_return_value); |
| set_gdbarch_deprecated_extract_return_value |
| (gdbarch, cris_abi_v2_extract_return_value); |
| set_gdbarch_reg_struct_has_addr (gdbarch, |
| cris_abi_v2_reg_struct_has_addr); |
| } |
| else |
| internal_error (__FILE__, __LINE__, "cris_gdbarch_init: unknown CRIS ABI"); |
| |
| /* The default definition of a long double is 2 * TARGET_DOUBLE_BIT, |
| which means we have to set this explicitly. */ |
| set_gdbarch_long_double_bit (gdbarch, 64); |
| |
| /* There are 32 registers (some of which may not be implemented). */ |
| set_gdbarch_num_regs (gdbarch, 32); |
| set_gdbarch_sp_regnum (gdbarch, 14); |
| set_gdbarch_deprecated_fp_regnum (gdbarch, 8); |
| set_gdbarch_pc_regnum (gdbarch, 15); |
| |
| set_gdbarch_register_name (gdbarch, cris_register_name); |
| |
| /* Length of ordinary registers used in push_word and a few other places. |
| REGISTER_RAW_SIZE is the real way to know how big a register is. */ |
| set_gdbarch_deprecated_register_size (gdbarch, 4); |
| |
| /* NEW */ |
| set_gdbarch_register_bytes_ok (gdbarch, cris_register_bytes_ok); |
| set_gdbarch_software_single_step (gdbarch, cris_software_single_step); |
| |
| |
| set_gdbarch_cannot_store_register (gdbarch, cris_cannot_store_register); |
| set_gdbarch_cannot_fetch_register (gdbarch, cris_cannot_fetch_register); |
| |
| |
| /* The total amount of space needed to store (in an array called registers) |
| GDB's copy of the machine's register state. Note: We can not use |
| cris_register_size at this point, since it relies on current_gdbarch |
| being set. */ |
| switch (tdep->cris_version) |
| { |
| case 0: |
| case 1: |
| case 2: |
| case 3: |
| /* Support for these may be added later. */ |
| internal_error (__FILE__, __LINE__, "cris_gdbarch_init: unsupported CRIS version"); |
| break; |
| |
| case 8: |
| case 9: |
| /* CRIS v8 and v9, a.k.a. ETRAX 100. General registers R0 - R15 |
| (32 bits), special registers P0 - P1 (8 bits), P4 - P5 (16 bits), |
| and P8 - P14 (32 bits). */ |
| register_bytes = (16 * 4) + (2 * 1) + (2 * 2) + (7 * 4); |
| break; |
| |
| case 10: |
| case 11: |
| /* CRIS v10 and v11, a.k.a. ETRAX 100LX. In addition to ETRAX 100, |
| P7 (32 bits), and P15 (32 bits) have been implemented. */ |
| register_bytes = (16 * 4) + (2 * 1) + (2 * 2) + (9 * 4); |
| break; |
| |
| default: |
| internal_error (__FILE__, __LINE__, "cris_gdbarch_init: unknown CRIS version"); |
| } |
| |
| set_gdbarch_deprecated_register_bytes (gdbarch, register_bytes); |
| |
| /* Returns the register offset for the first byte of register regno's space |
| in the saved register state. */ |
| set_gdbarch_register_byte (gdbarch, cris_register_offset); |
| |
| /* The length of the registers in the actual machine representation. */ |
| set_gdbarch_register_raw_size (gdbarch, cris_register_size); |
| |
| /* The largest value REGISTER_RAW_SIZE can have. */ |
| set_gdbarch_deprecated_max_register_raw_size (gdbarch, 32); |
| |
| /* The length of the registers in the program's representation. */ |
| set_gdbarch_register_virtual_size (gdbarch, cris_register_size); |
| |
| /* The largest value REGISTER_VIRTUAL_SIZE can have. */ |
| set_gdbarch_deprecated_max_register_virtual_size (gdbarch, 32); |
| |
| set_gdbarch_register_virtual_type (gdbarch, cris_register_virtual_type); |
| |
| /* Use generic dummy frames. */ |
| |
| /* Read all about dummy frames in blockframe.c. */ |
| set_gdbarch_deprecated_pc_in_call_dummy (gdbarch, deprecated_pc_in_call_dummy_at_entry_point); |
| |
| /* Defined to 1 to indicate that the target supports inferior function |
| calls. */ |
| set_gdbarch_deprecated_call_dummy_words (gdbarch, 0); |
| set_gdbarch_deprecated_sizeof_call_dummy_words (gdbarch, 0); |
| |
| set_gdbarch_deprecated_get_saved_register (gdbarch, deprecated_generic_get_saved_register); |
| |
| /* No register requires conversion from raw format to virtual format. */ |
| set_gdbarch_register_convertible (gdbarch, generic_register_convertible_not); |
| |
| set_gdbarch_deprecated_push_return_address (gdbarch, cris_push_return_address); |
| set_gdbarch_deprecated_pop_frame (gdbarch, cris_pop_frame); |
| |
| set_gdbarch_deprecated_store_struct_return (gdbarch, cris_store_struct_return); |
| set_gdbarch_deprecated_extract_struct_value_address |
| (gdbarch, cris_extract_struct_value_address); |
| set_gdbarch_use_struct_convention (gdbarch, cris_use_struct_convention); |
| |
| set_gdbarch_deprecated_frame_init_saved_regs (gdbarch, cris_frame_init_saved_regs); |
| set_gdbarch_deprecated_init_extra_frame_info (gdbarch, cris_init_extra_frame_info); |
| set_gdbarch_skip_prologue (gdbarch, cris_skip_prologue); |
| set_gdbarch_prologue_frameless_p (gdbarch, generic_prologue_frameless_p); |
| |
| /* The stack grows downward. */ |
| set_gdbarch_inner_than (gdbarch, core_addr_lessthan); |
| |
| set_gdbarch_breakpoint_from_pc (gdbarch, cris_breakpoint_from_pc); |
| |
| /* The PC must not be decremented after a breakpoint. (The breakpoint |
| handler takes care of that.) */ |
| set_gdbarch_decr_pc_after_break (gdbarch, 0); |
| |
| /* Offset from address of function to start of its code. */ |
| set_gdbarch_function_start_offset (gdbarch, 0); |
| |
| /* The number of bytes at the start of arglist that are not really args, |
| 0 in the CRIS ABI. */ |
| set_gdbarch_frame_args_skip (gdbarch, 0); |
| set_gdbarch_frameless_function_invocation |
| (gdbarch, cris_frameless_function_invocation); |
| set_gdbarch_deprecated_frame_chain (gdbarch, cris_frame_chain); |
| |
| set_gdbarch_deprecated_frame_saved_pc (gdbarch, cris_frame_saved_pc); |
| set_gdbarch_deprecated_saved_pc_after_call (gdbarch, cris_saved_pc_after_call); |
| |
| set_gdbarch_frame_num_args (gdbarch, frame_num_args_unknown); |
| |
| /* Helpful for backtracing and returning in a call dummy. */ |
| set_gdbarch_save_dummy_frame_tos (gdbarch, generic_save_dummy_frame_tos); |
| |
| /* Should be using push_dummy_call. */ |
| set_gdbarch_deprecated_dummy_write_sp (gdbarch, generic_target_write_sp); |
| |
| /* Use target_specific function to define link map offsets. */ |
| set_solib_svr4_fetch_link_map_offsets |
| (gdbarch, cris_linux_svr4_fetch_link_map_offsets); |
| |
| return gdbarch; |
| } |