diff options
author | nobody <> | 2002-09-17 20:42:02 +0000 |
---|---|---|
committer | nobody <> | 2002-09-17 20:42:02 +0000 |
commit | c80c4635cedcdb1cb2efa481eceda2b69cc8cbd7 (patch) | |
tree | 4cc86b964e683ac5ff944eae7d5ca48ed02a8014 | |
parent | 047807b33f6355af51cd2481644d5b9a5757406d (diff) | |
download | gdb-c80c4635cedcdb1cb2efa481eceda2b69cc8cbd7.zip gdb-c80c4635cedcdb1cb2efa481eceda2b69cc8cbd7.tar.gz gdb-c80c4635cedcdb1cb2efa481eceda2b69cc8cbd7.tar.bz2 |
This commit was manufactured by cvs2svn to create branch
'carlton_dictionary-branch'.
Cherrypick from gdb_5_3-branch 2002-09-03 22:29:15 UTC nobody 'This commit was manufactured by cvs2svn to create branch 'gdb_5_3-branch'.':
gdb/ns32k-tdep.c
gdb/sh3-rom.c
gdb/vax-tdep.c
Cherrypick from master 2002-09-17 20:42:01 UTC Andrew Cagney <cagney@redhat.com> '2002-09-17 Andrew Cagney <cagney@redhat.com>':
gdb/cris-tdep.c
gdb/mcore-tdep.c
-rw-r--r-- | gdb/cris-tdep.c | 4348 | ||||
-rw-r--r-- | gdb/mcore-tdep.c | 1180 | ||||
-rw-r--r-- | gdb/ns32k-tdep.c | 626 | ||||
-rw-r--r-- | gdb/sh3-rom.c | 384 | ||||
-rw-r--r-- | gdb/vax-tdep.c | 702 |
5 files changed, 7240 insertions, 0 deletions
diff --git a/gdb/cris-tdep.c b/gdb/cris-tdep.c new file mode 100644 index 0000000..0bfb5b3 --- /dev/null +++ b/gdb/cris-tdep.c @@ -0,0 +1,4348 @@ +/* Target dependent code for CRIS, for GDB, the GNU debugger. + Copyright 2001 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. */ + + +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. + 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. */ + +/* 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 + int signal_handler_caller + 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 signal_handler_caller is non-zero when the frame is + associated with the call of a signal handler. + + 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. */ + fi->extra_info->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 (fi->saved_regs) + { + for (regno = 0; regno < NUM_REGS; regno++) + fi->saved_regs[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; + } + fi->extra_info->leaf_function = 0; + } + else if (regno == 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) == 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) == 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 (!fi->saved_regs) + return ip; + + if (have_fp) + { + fi->saved_regs[FP_REGNUM] = FRAME_FP (fi); + + /* Calculate the addresses. */ + for (regno = regsave; regno >= 0; regno--) + { + fi->saved_regs[regno] = FRAME_FP (fi) - val; + val -= 4; + } + if (fi->extra_info->leaf_function) + { + /* Set the register SP to contain the stack pointer of + the caller. */ + fi->saved_regs[SP_REGNUM] = FRAME_FP (fi) + 4; + } + else + { + /* Set the register SP to contain the stack pointer of + the caller. */ + fi->saved_regs[SP_REGNUM] = FRAME_FP (fi) + 8; + + /* Set the register SRP to contain the return address of + the caller. */ + fi->saved_regs[SRP_REGNUM] = FRAME_FP (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 frame_info fi; + static struct frame_extra_info fei; + 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 use static memory for extra_info, and don't bother allocating + memory for saved_regs. */ + fi.saved_regs = 0; + fi.extra_info = &fei; + + /* 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); + 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 <= REGISTER_SIZE) + 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 * REGISTER_SIZE) + { + /* Note that this works since R10 and R11 are consecutive registers. */ + 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 == 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 <= 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 * 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 (fi->signal_handler_caller) + 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 FRAME_INIT_SAVED_REGS using either frame_saved_regs_zalloc + or frame_obstack_alloc. */ + +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 (fi->pc, fi->frame); + + /* Examine the entire prologue. */ + register int frameless_p = 0; + + /* Has this frame's registers already been initialized? */ + if (fi->saved_regs) + 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 (&fi->saved_regs, dummy_regs, sizeof (fi->saved_regs)); + } + else + { + ip = get_pc_function_start (fi->pc); + 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 (fi->next) + { + /* Called from get_prev_frame. */ + fi->pc = FRAME_SAVED_PC (fi->next); + } + + fi->extra_info = (struct frame_extra_info *) + frame_obstack_alloc (sizeof (struct frame_extra_info)); + + fi->extra_info->return_pc = 0; + fi->extra_info->leaf_function = 0; + + if (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame)) + { + /* We need to setup fi->frame here because run_stack_dummy gets it wrong + by assuming it's always FP. */ + fi->frame = deprecated_read_register_dummy (fi->pc, fi->frame, + SP_REGNUM); + fi->extra_info->return_pc = + deprecated_read_register_dummy (fi->pc, fi->frame, PC_REGNUM); + + /* FIXME: Is this necessarily true? */ + fi->extra_info->leaf_function = 0; + } + else + { + cris_frame_init_saved_regs (fi); + + /* Check fromleaf/frameless_function_invocation. (FIXME) */ + + if (fi->saved_regs[SRP_REGNUM] != 0) + { + /* SRP was saved on the stack; non-leaf function. */ + fi->extra_info->return_pc = + read_memory_integer (fi->saved_regs[SRP_REGNUM], + REGISTER_RAW_SIZE (SRP_REGNUM)); + } + else + { + /* SRP is still in a register; leaf function. */ + fi->extra_info->return_pc = read_register (SRP_REGNUM); + /* FIXME: Should leaf_function be set to 1 here? */ + fi->extra_info->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 (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame)) + { + return fi->frame; + } + else if (!inside_entry_file (fi->pc)) + { + return read_memory_unsigned_integer (FRAME_FP (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 fi->extra_info->return_pc; +} + +/* Return the address of the argument block for the frame described + by struct frame_info. */ + +CORE_ADDR +cris_frame_args_address (struct frame_info *fi) +{ + return FRAME_FP (fi); +} + +/* Return the address of the locals block for the frame + described by struct frame_info. */ + +CORE_ADDR +cris_frame_locals_address (struct frame_info *fi) +{ + return FRAME_FP (fi); +} + +/* 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])) + 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 * 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 <= 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 > 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 > 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), REGISTER_SIZE); + fp_data += len; + fp_params += 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, REGISTER_SIZE); + fp_params += 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 / REGISTER_SIZE) + (len % REGISTER_SIZE != 0 ? 1 : 0); + + /* reg_demand * REGISTER_SIZE is the amount of memory we might need to + allocate for this argument. 2 * 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 * REGISTER_SIZE + 2 * 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 * 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 / REGISTER_SIZE) + (len % REGISTER_SIZE != 0 ? 1 : 0); + + if (len <= (2 * 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 += REGISTER_SIZE; + } + } + else if (len <= (2 * 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 += REGISTER_SIZE; + } + else + { + /* I guess this memory write could write the remaining data + all at once instead of in REGISTER_SIZE chunks. */ + write_memory (fp_arg, val, REGISTER_SIZE); + fp_arg += REGISTER_SIZE; + val += REGISTER_SIZE; + } + } + } + else if (len > (2 * REGISTER_SIZE)) + { + /* Data passed by reference. Put it on the stack. */ + write_memory (fp_mem, val, len); + write_memory (fp_arg, (char *) (&fp_mem), 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 * REGISTER_SIZE; + fp_arg += 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 * 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 (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame)) + { + /* 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 < FP_REGNUM; regno++) + { + if (fi->saved_regs[regno]) + { + write_register (regno, + read_memory_integer (fi->saved_regs[regno], 4)); + } + } + + if (fi->saved_regs[FP_REGNUM]) + { + /* Pop the frame pointer (R8). It was pushed before SP + was saved. */ + write_register (FP_REGNUM, + read_memory_integer (fi->saved_regs[FP_REGNUM], 4)); + stack_offset += 4; + + /* Not a leaf function. */ + if (fi->saved_regs[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, fi->saved_regs[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, fi->extra_info->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) +{ + 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; + + read_register_gen (PC_REGNUM, (char *) &pc); + 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. */ + 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. */ + 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, (PTR) 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); + + 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_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_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_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_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_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_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_max_register_virtual_size (gdbarch, 32); + + set_gdbarch_register_virtual_type (gdbarch, cris_register_virtual_type); + + /* Use generic dummy frames. */ + set_gdbarch_use_generic_dummy_frames (gdbarch, 1); + + /* Where to execute the call in the memory segments. */ + set_gdbarch_call_dummy_location (gdbarch, AT_ENTRY_POINT); + set_gdbarch_call_dummy_address (gdbarch, entry_point_address); + + /* Start execution at the beginning of dummy. */ + set_gdbarch_call_dummy_start_offset (gdbarch, 0); + set_gdbarch_call_dummy_breakpoint_offset (gdbarch, 0); + + /* Set to 1 since call_dummy_breakpoint_offset was defined. */ + set_gdbarch_call_dummy_breakpoint_offset_p (gdbarch, 1); + + /* Read all about dummy frames in blockframe.c. */ + set_gdbarch_call_dummy_length (gdbarch, 0); + set_gdbarch_pc_in_call_dummy (gdbarch, pc_in_call_dummy_at_entry_point); + + /* Defined to 1 to indicate that the target supports inferior function + calls. */ + set_gdbarch_call_dummy_p (gdbarch, 1); + set_gdbarch_call_dummy_words (gdbarch, 0); + set_gdbarch_sizeof_call_dummy_words (gdbarch, 0); + + /* No stack adjustment needed when peforming an inferior function call. */ + set_gdbarch_call_dummy_stack_adjust_p (gdbarch, 0); + set_gdbarch_fix_call_dummy (gdbarch, generic_fix_call_dummy); + + set_gdbarch_get_saved_register (gdbarch, 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_push_dummy_frame (gdbarch, generic_push_dummy_frame); + set_gdbarch_push_return_address (gdbarch, cris_push_return_address); + set_gdbarch_pop_frame (gdbarch, cris_pop_frame); + + set_gdbarch_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_frame_init_saved_regs (gdbarch, cris_frame_init_saved_regs); + set_gdbarch_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_frame_chain (gdbarch, cris_frame_chain); + set_gdbarch_frame_chain_valid (gdbarch, generic_file_frame_chain_valid); + + set_gdbarch_frame_saved_pc (gdbarch, cris_frame_saved_pc); + set_gdbarch_frame_args_address (gdbarch, cris_frame_args_address); + set_gdbarch_frame_locals_address (gdbarch, cris_frame_locals_address); + set_gdbarch_saved_pc_after_call (gdbarch, cris_saved_pc_after_call); + + set_gdbarch_frame_num_args (gdbarch, frame_num_args_unknown); + + /* No extra stack alignment needed. Set to 1 by default. */ + set_gdbarch_extra_stack_alignment_needed (gdbarch, 0); + + /* Helpful for backtracing and returning in a call dummy. */ + set_gdbarch_save_dummy_frame_tos (gdbarch, generic_save_dummy_frame_tos); + + /* 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; +} diff --git a/gdb/mcore-tdep.c b/gdb/mcore-tdep.c new file mode 100644 index 0000000..395bb44 --- /dev/null +++ b/gdb/mcore-tdep.c @@ -0,0 +1,1180 @@ +/* Target-machine dependent code for Motorola MCore for GDB, the GNU debugger + Copyright 1999, 2000, 2001 Free Software Foundation, Inc. + + 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 "value.h" +#include "gdbcmd.h" +#include "regcache.h" +#include "symfile.h" +#include "gdbcore.h" +#include "inferior.h" +#include "arch-utils.h" + +/* Functions declared and used only in this file */ + +static CORE_ADDR mcore_analyze_prologue (struct frame_info *fi, CORE_ADDR pc, int skip_prologue); + +static struct frame_info *analyze_dummy_frame (CORE_ADDR pc, CORE_ADDR frame); + +static int get_insn (CORE_ADDR pc); + +/* Functions exported from this file */ + +int mcore_use_struct_convention (int gcc_p, struct type *type); + +void _initialize_mcore (void); + +void mcore_init_extra_frame_info (int fromleaf, struct frame_info *fi); + +CORE_ADDR mcore_frame_saved_pc (struct frame_info *fi); + +CORE_ADDR mcore_find_callers_reg (struct frame_info *fi, int regnum); + +CORE_ADDR mcore_frame_args_address (struct frame_info *fi); + +CORE_ADDR mcore_frame_locals_address (struct frame_info *fi); + +CORE_ADDR mcore_push_return_address (CORE_ADDR pc, CORE_ADDR sp); + +CORE_ADDR mcore_push_arguments (int nargs, struct value ** args, CORE_ADDR sp, + int struct_return, CORE_ADDR struct_addr); + +void mcore_pop_frame (); + +CORE_ADDR mcore_skip_prologue (CORE_ADDR pc); + +CORE_ADDR mcore_frame_chain (struct frame_info *fi); + +const unsigned char *mcore_breakpoint_from_pc (CORE_ADDR * bp_addr, int *bp_size); + +int mcore_use_struct_convention (int gcc_p, struct type *type); + +void mcore_store_return_value (struct type *type, char *valbuf); + +CORE_ADDR mcore_extract_struct_value_address (char *regbuf); + +void mcore_extract_return_value (struct type *type, char *regbuf, char *valbuf); + +#ifdef MCORE_DEBUG +int mcore_debug = 0; +#endif + + +/* All registers are 4 bytes long. */ +#define MCORE_REG_SIZE 4 +#define MCORE_NUM_REGS 65 + +/* Some useful register numbers. */ +#define PR_REGNUM 15 +#define FIRST_ARGREG 2 +#define LAST_ARGREG 7 +#define RETVAL_REGNUM 2 + + +/* Additional info that we use for managing frames */ +struct frame_extra_info + { + /* A generic status word */ + int status; + + /* Size of this frame */ + int framesize; + + /* The register that is acting as a frame pointer, if + it is being used. This is undefined if status + does not contain the flag MY_FRAME_IN_FP. */ + int fp_regnum; + }; + +/* frame_extra_info status flags */ + +/* The base of the current frame is actually in the stack pointer. + This happens when there is no frame pointer (MCore ABI does not + require a frame pointer) or when we're stopped in the prologue or + epilogue itself. In these cases, mcore_analyze_prologue will need + to update fi->frame before returning or analyzing the register + save instructions. */ +#define MY_FRAME_IN_SP 0x1 + +/* The base of the current frame is in a frame pointer register. + This register is noted in frame_extra_info->fp_regnum. + + Note that the existence of an FP might also indicate that the + function has called alloca. */ +#define MY_FRAME_IN_FP 0x2 + +/* This flag is set to indicate that this frame is the top-most + frame. This tells frame chain not to bother trying to unwind + beyond this frame. */ +#define NO_MORE_FRAMES 0x4 + +/* Instruction macros used for analyzing the prologue */ +#define IS_SUBI0(x) (((x) & 0xfe0f) == 0x2400) /* subi r0,oimm5 */ +#define IS_STM(x) (((x) & 0xfff0) == 0x0070) /* stm rf-r15,r0 */ +#define IS_STWx0(x) (((x) & 0xf00f) == 0x9000) /* stw rz,(r0,disp) */ +#define IS_STWxy(x) (((x) & 0xf000) == 0x9000) /* stw rx,(ry,disp) */ +#define IS_MOVx0(x) (((x) & 0xfff0) == 0x1200) /* mov rn,r0 */ +#define IS_LRW1(x) (((x) & 0xff00) == 0x7100) /* lrw r1,literal */ +#define IS_MOVI1(x) (((x) & 0xf80f) == 0x6001) /* movi r1,imm7 */ +#define IS_BGENI1(x) (((x) & 0xfe0f) == 0x3201) /* bgeni r1,imm5 */ +#define IS_BMASKI1(x) (((x) & 0xfe0f) == 0x2C01) /* bmaski r1,imm5 */ +#define IS_ADDI1(x) (((x) & 0xfe0f) == 0x2001) /* addi r1,oimm5 */ +#define IS_SUBI1(x) (((x) & 0xfe0f) == 0x2401) /* subi r1,oimm5 */ +#define IS_RSUBI1(x) (((x) & 0xfe0f) == 0x2801) /* rsubi r1,imm5 */ +#define IS_NOT1(x) (((x) & 0xffff) == 0x01f1) /* not r1 */ +#define IS_ROTLI1(x) (((x) & 0xfe0f) == 0x3801) /* rotli r1,imm5 */ +#define IS_BSETI1(x) (((x) & 0xfe0f) == 0x3401) /* bseti r1,imm5 */ +#define IS_BCLRI1(x) (((x) & 0xfe0f) == 0x3001) /* bclri r1,imm5 */ +#define IS_IXH1(x) (((x) & 0xffff) == 0x1d11) /* ixh r1,r1 */ +#define IS_IXW1(x) (((x) & 0xffff) == 0x1511) /* ixw r1,r1 */ +#define IS_SUB01(x) (((x) & 0xffff) == 0x0510) /* subu r0,r1 */ +#define IS_RTS(x) (((x) & 0xffff) == 0x00cf) /* jmp r15 */ + +#define IS_R1_ADJUSTER(x) \ + (IS_ADDI1(x) || IS_SUBI1(x) || IS_ROTLI1(x) || IS_BSETI1(x) \ + || IS_BCLRI1(x) || IS_RSUBI1(x) || IS_NOT1(x) \ + || IS_IXH1(x) || IS_IXW1(x)) + + +#ifdef MCORE_DEBUG +static void +mcore_dump_insn (char *commnt, CORE_ADDR pc, int insn) +{ + if (mcore_debug) + { + printf_filtered ("MCORE: %s %08x %08x ", + commnt, (unsigned int) pc, (unsigned int) insn); + TARGET_PRINT_INSN (pc, &tm_print_insn_info); + printf_filtered ("\n"); + } +} +#define mcore_insn_debug(args) { if (mcore_debug) printf_filtered args; } +#else /* !MCORE_DEBUG */ +#define mcore_dump_insn(a,b,c) {} +#define mcore_insn_debug(args) {} +#endif + + +static struct type * +mcore_register_virtual_type (int regnum) +{ + if (regnum < 0 || regnum >= MCORE_NUM_REGS) + internal_error (__FILE__, __LINE__, + "mcore_register_virtual_type: illegal register number %d", + regnum); + else + return builtin_type_int; +} + +static int +mcore_register_byte (int regnum) +{ + if (regnum < 0 || regnum >= MCORE_NUM_REGS) + internal_error (__FILE__, __LINE__, + "mcore_register_byte: illegal register number %d", + regnum); + else + return (regnum * MCORE_REG_SIZE); +} + +static int +mcore_register_size (int regnum) +{ + + if (regnum < 0 || regnum >= MCORE_NUM_REGS) + internal_error (__FILE__, __LINE__, + "mcore_register_size: illegal register number %d", + regnum); + else + return MCORE_REG_SIZE; +} + +/* The registers of the Motorola MCore processors */ + +static const char * +mcore_register_name (int regnum) +{ + + static char *register_names[] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", + "ar0", "ar1", "ar2", "ar3", "ar4", "ar5", "ar6", "ar7", + "ar8", "ar9", "ar10", "ar11", "ar12", "ar13", "ar14", "ar15", + "psr", "vbr", "epsr", "fpsr", "epc", "fpc", "ss0", "ss1", + "ss2", "ss3", "ss4", "gcr", "gsr", "cr13", "cr14", "cr15", + "cr16", "cr17", "cr18", "cr19", "cr20", "cr21", "cr22", "cr23", + "cr24", "cr25", "cr26", "cr27", "cr28", "cr29", "cr30", "cr31", + "pc" + }; + + if (regnum < 0 || + regnum >= sizeof (register_names) / sizeof (register_names[0])) + internal_error (__FILE__, __LINE__, + "mcore_register_name: illegal register number %d", + regnum); + else + return register_names[regnum]; +} + +/* Given the address at which to insert a breakpoint (BP_ADDR), + what will that breakpoint be? + + For MCore, we have a breakpoint instruction. Since all MCore + instructions are 16 bits, this is all we need, regardless of + address. bpkt = 0x0000 */ + +const unsigned char * +mcore_breakpoint_from_pc (CORE_ADDR * bp_addr, int *bp_size) +{ + static char breakpoint[] = + {0x00, 0x00}; + *bp_size = 2; + return breakpoint; +} + +static CORE_ADDR +mcore_saved_pc_after_call (struct frame_info *frame) +{ + return read_register (PR_REGNUM); +} + +/* This is currently handled by init_extra_frame_info. */ +static void +mcore_frame_init_saved_regs (struct frame_info *frame) +{ + +} + +/* This is currently handled by mcore_push_arguments */ +static void +mcore_store_struct_return (CORE_ADDR addr, CORE_ADDR sp) +{ + +} + +static int +mcore_reg_struct_has_addr (int gcc_p, struct type *type) +{ + return 0; +} + + +/* Helper function for several routines below. This funtion simply + sets up a fake, aka dummy, frame (not a _call_ dummy frame) that + we can analyze with mcore_analyze_prologue. */ + +static struct frame_info * +analyze_dummy_frame (CORE_ADDR pc, CORE_ADDR frame) +{ + static struct frame_info *dummy = NULL; + + if (dummy == NULL) + { + dummy = (struct frame_info *) xmalloc (sizeof (struct frame_info)); + dummy->saved_regs = (CORE_ADDR *) xmalloc (SIZEOF_FRAME_SAVED_REGS); + dummy->extra_info = + (struct frame_extra_info *) xmalloc (sizeof (struct frame_extra_info)); + } + + dummy->next = NULL; + dummy->prev = NULL; + dummy->pc = pc; + dummy->frame = frame; + dummy->extra_info->status = 0; + dummy->extra_info->framesize = 0; + memset (dummy->saved_regs, '\000', SIZEOF_FRAME_SAVED_REGS); + mcore_analyze_prologue (dummy, 0, 0); + return dummy; +} + +/* Function prologues on the Motorola MCore processors consist of: + + - adjustments to the stack pointer (r1 used as scratch register) + - store word/multiples that use r0 as the base address + - making a copy of r0 into another register (a "frame" pointer) + + Note that the MCore really doesn't have a real frame pointer. + Instead, the compiler may copy the SP into a register (usually + r8) to act as an arg pointer. For our target-dependent purposes, + the frame info's "frame" member will be the beginning of the + frame. The SP could, in fact, point below this. + + The prologue ends when an instruction fails to meet either of + the first two criteria or when an FP is made. We make a special + exception for gcc. When compiling unoptimized code, gcc will + setup stack slots. We need to make sure that we skip the filling + of these stack slots as much as possible. This is only done + when SKIP_PROLOGUE is set, so that it does not mess up + backtraces. */ + +/* Analyze the prologue of frame FI to determine where registers are saved, + the end of the prologue, etc. Return the address of the first line + of "real" code (i.e., the end of the prologue). */ + +static CORE_ADDR +mcore_analyze_prologue (struct frame_info *fi, CORE_ADDR pc, int skip_prologue) +{ + CORE_ADDR func_addr, func_end, addr, stop; + CORE_ADDR stack_size; + int insn, rn; + int status; + int fp_regnum = 0; /* dummy, valid when (flags & MY_FRAME_IN_FP) */ + int flags; + int framesize; + int register_offsets[NUM_REGS]; + char *name; + + /* If provided, use the PC in the frame to look up the + start of this function. */ + pc = (fi == NULL ? pc : fi->pc); + + /* Find the start of this function. */ + status = find_pc_partial_function (pc, &name, &func_addr, &func_end); + + /* If the start of this function could not be found or if the debbuger + is stopped at the first instruction of the prologue, do nothing. */ + if (status == 0) + return pc; + + /* If the debugger is entry function, give up. */ + if (func_addr == entry_point_address ()) + { + if (fi != NULL) + fi->extra_info->status |= NO_MORE_FRAMES; + return pc; + } + + /* At the start of a function, our frame is in the stack pointer. */ + flags = MY_FRAME_IN_SP; + + /* Start decoding the prologue. We start by checking two special cases: + + 1. We're about to return + 2. We're at the first insn of the prologue. + + If we're about to return, our frame has already been deallocated. + If we are stopped at the first instruction of a prologue, + then our frame has not yet been set up. */ + + /* Get the first insn from memory (all MCore instructions are 16 bits) */ + mcore_insn_debug (("MCORE: starting prologue decoding\n")); + insn = get_insn (pc); + mcore_dump_insn ("got 1: ", pc, insn); + + /* Check for return. */ + if (fi != NULL && IS_RTS (insn)) + { + mcore_insn_debug (("MCORE: got jmp r15")); + if (fi->next == NULL) + fi->frame = read_sp (); + return fi->pc; + } + + /* Check for first insn of prologue */ + if (fi != NULL && fi->pc == func_addr) + { + if (fi->next == NULL) + fi->frame = read_sp (); + return fi->pc; + } + + /* Figure out where to stop scanning */ + stop = (fi ? fi->pc : func_end); + + /* Don't walk off the end of the function */ + stop = (stop > func_end ? func_end : stop); + + /* REGISTER_OFFSETS will contain offsets, from the top of the frame + (NOT the frame pointer), for the various saved registers or -1 + if the register is not saved. */ + for (rn = 0; rn < NUM_REGS; rn++) + register_offsets[rn] = -1; + + /* Analyze the prologue. Things we determine from analyzing the + prologue include: + * the size of the frame + * where saved registers are located (and which are saved) + * FP used? */ + mcore_insn_debug (("MCORE: Scanning prologue: func_addr=0x%x, stop=0x%x\n", + (unsigned int) func_addr, (unsigned int) stop)); + + framesize = 0; + for (addr = func_addr; addr < stop; addr += 2) + { + /* Get next insn */ + insn = get_insn (addr); + mcore_dump_insn ("got 2: ", addr, insn); + + if (IS_SUBI0 (insn)) + { + int offset = 1 + ((insn >> 4) & 0x1f); + mcore_insn_debug (("MCORE: got subi r0,%d; continuing\n", offset)); + framesize += offset; + continue; + } + else if (IS_STM (insn)) + { + /* Spill register(s) */ + int offset; + int start_register; + + /* BIG WARNING! The MCore ABI does not restrict functions + to taking only one stack allocation. Therefore, when + we save a register, we record the offset of where it was + saved relative to the current framesize. This will + then give an offset from the SP upon entry to our + function. Remember, framesize is NOT constant until + we're done scanning the prologue. */ + start_register = (insn & 0xf); + mcore_insn_debug (("MCORE: got stm r%d-r15,(r0)\n", start_register)); + + for (rn = start_register, offset = 0; rn <= 15; rn++, offset += 4) + { + register_offsets[rn] = framesize - offset; + mcore_insn_debug (("MCORE: r%d saved at 0x%x (offset %d)\n", rn, + register_offsets[rn], offset)); + } + mcore_insn_debug (("MCORE: continuing\n")); + continue; + } + else if (IS_STWx0 (insn)) + { + /* Spill register: see note for IS_STM above. */ + int imm; + + rn = (insn >> 8) & 0xf; + imm = (insn >> 4) & 0xf; + register_offsets[rn] = framesize - (imm << 2); + mcore_insn_debug (("MCORE: r%d saved at offset 0x%x\n", rn, register_offsets[rn])); + mcore_insn_debug (("MCORE: continuing\n")); + continue; + } + else if (IS_MOVx0 (insn)) + { + /* We have a frame pointer, so this prologue is over. Note + the register which is acting as the frame pointer. */ + flags |= MY_FRAME_IN_FP; + flags &= ~MY_FRAME_IN_SP; + fp_regnum = insn & 0xf; + mcore_insn_debug (("MCORE: Found a frame pointer: r%d\n", fp_regnum)); + + /* If we found an FP, we're at the end of the prologue. */ + mcore_insn_debug (("MCORE: end of prologue\n")); + if (skip_prologue) + continue; + + /* If we're decoding prologue, stop here. */ + addr += 2; + break; + } + else if (IS_STWxy (insn) && (flags & MY_FRAME_IN_FP) && ((insn & 0xf) == fp_regnum)) + { + /* Special case. Skip over stack slot allocs, too. */ + mcore_insn_debug (("MCORE: push arg onto stack.\n")); + continue; + } + else if (IS_LRW1 (insn) || IS_MOVI1 (insn) + || IS_BGENI1 (insn) || IS_BMASKI1 (insn)) + { + int adjust = 0; + int offset = 0; + int insn2; + + mcore_insn_debug (("MCORE: looking at large frame\n")); + if (IS_LRW1 (insn)) + { + adjust = + read_memory_integer ((addr + 2 + ((insn & 0xff) << 2)) & 0xfffffffc, 4); + } + else if (IS_MOVI1 (insn)) + adjust = (insn >> 4) & 0x7f; + else if (IS_BGENI1 (insn)) + adjust = 1 << ((insn >> 4) & 0x1f); + else /* IS_BMASKI (insn) */ + adjust = (1 << (adjust >> 4) & 0x1f) - 1; + + mcore_insn_debug (("MCORE: base framesize=0x%x\n", adjust)); + + /* May have zero or more insns which modify r1 */ + mcore_insn_debug (("MCORE: looking for r1 adjusters...\n")); + offset = 2; + insn2 = get_insn (addr + offset); + while (IS_R1_ADJUSTER (insn2)) + { + int imm; + + imm = (insn2 >> 4) & 0x1f; + mcore_dump_insn ("got 3: ", addr + offset, insn); + if (IS_ADDI1 (insn2)) + { + adjust += (imm + 1); + mcore_insn_debug (("MCORE: addi r1,%d\n", imm + 1)); + } + else if (IS_SUBI1 (insn2)) + { + adjust -= (imm + 1); + mcore_insn_debug (("MCORE: subi r1,%d\n", imm + 1)); + } + else if (IS_RSUBI1 (insn2)) + { + adjust = imm - adjust; + mcore_insn_debug (("MCORE: rsubi r1,%d\n", imm + 1)); + } + else if (IS_NOT1 (insn2)) + { + adjust = ~adjust; + mcore_insn_debug (("MCORE: not r1\n")); + } + else if (IS_ROTLI1 (insn2)) + { + adjust <<= imm; + mcore_insn_debug (("MCORE: rotli r1,%d\n", imm + 1)); + } + else if (IS_BSETI1 (insn2)) + { + adjust |= (1 << imm); + mcore_insn_debug (("MCORE: bseti r1,%d\n", imm)); + } + else if (IS_BCLRI1 (insn2)) + { + adjust &= ~(1 << imm); + mcore_insn_debug (("MCORE: bclri r1,%d\n", imm)); + } + else if (IS_IXH1 (insn2)) + { + adjust *= 3; + mcore_insn_debug (("MCORE: ix.h r1,r1\n")); + } + else if (IS_IXW1 (insn2)) + { + adjust *= 5; + mcore_insn_debug (("MCORE: ix.w r1,r1\n")); + } + + offset += 2; + insn2 = get_insn (addr + offset); + }; + + mcore_insn_debug (("MCORE: done looking for r1 adjusters\n")); + + /* If the next insn adjusts the stack pointer, we keep everything; + if not, we scrap it and we've found the end of the prologue. */ + if (IS_SUB01 (insn2)) + { + addr += offset; + framesize += adjust; + mcore_insn_debug (("MCORE: found stack adjustment of 0x%x bytes.\n", adjust)); + mcore_insn_debug (("MCORE: skipping to new address 0x%x\n", addr)); + mcore_insn_debug (("MCORE: continuing\n")); + continue; + } + + /* None of these instructions are prologue, so don't touch + anything. */ + mcore_insn_debug (("MCORE: no subu r1,r0, NOT altering framesize.\n")); + break; + } + + /* This is not a prologue insn, so stop here. */ + mcore_insn_debug (("MCORE: insn is not a prologue insn -- ending scan\n")); + break; + } + + mcore_insn_debug (("MCORE: done analyzing prologue\n")); + mcore_insn_debug (("MCORE: prologue end = 0x%x\n", addr)); + + /* Save everything we have learned about this frame into FI. */ + if (fi != NULL) + { + fi->extra_info->framesize = framesize; + fi->extra_info->fp_regnum = fp_regnum; + fi->extra_info->status = flags; + + /* Fix the frame pointer. When gcc uses r8 as a frame pointer, + it is really an arg ptr. We adjust fi->frame to be a "real" + frame pointer. */ + if (fi->next == NULL) + { + if (fi->extra_info->status & MY_FRAME_IN_SP) + fi->frame = read_sp () + framesize; + else + fi->frame = read_register (fp_regnum) + framesize; + } + + /* Note where saved registers are stored. The offsets in REGISTER_OFFSETS + are computed relative to the top of the frame. */ + for (rn = 0; rn < NUM_REGS; rn++) + { + if (register_offsets[rn] >= 0) + { + fi->saved_regs[rn] = fi->frame - register_offsets[rn]; + mcore_insn_debug (("Saved register %s stored at 0x%08x, value=0x%08x\n", + mcore_register_names[rn], fi->saved_regs[rn], + read_memory_integer (fi->saved_regs[rn], 4))); + } + } + } + + /* Return addr of first non-prologue insn. */ + return addr; +} + +/* Given a GDB frame, determine the address of the calling function's frame. + This will be used to create a new GDB frame struct, and then + INIT_EXTRA_FRAME_INFO and INIT_FRAME_PC will be called for the new frame. */ + +CORE_ADDR +mcore_frame_chain (struct frame_info * fi) +{ + struct frame_info *dummy; + CORE_ADDR callers_addr; + + /* Analyze the prologue of this function. */ + if (fi->extra_info->status == 0) + mcore_analyze_prologue (fi, 0, 0); + + /* If mcore_analyze_prologue set NO_MORE_FRAMES, quit now. */ + if (fi->extra_info->status & NO_MORE_FRAMES) + return 0; + + /* Now that we've analyzed our prologue, we can start to ask + for information about our caller. The easiest way to do + this is to analyze our caller's prologue. + + If our caller has a frame pointer, then we need to find + the value of that register upon entry to our frame. + This value is either in fi->saved_regs[rn] if it's saved, + or it's still in a register. + + If our caller does not have a frame pointer, then his frame base + is <our base> + -<caller's frame size>. */ + dummy = analyze_dummy_frame (FRAME_SAVED_PC (fi), fi->frame); + + if (dummy->extra_info->status & MY_FRAME_IN_FP) + { + int fp = dummy->extra_info->fp_regnum; + + /* Our caller has a frame pointer. */ + if (fi->saved_regs[fp] != 0) + { + /* The "FP" was saved on the stack. Don't forget to adjust + the "FP" with the framesize to get a real FP. */ + callers_addr = read_memory_integer (fi->saved_regs[fp], REGISTER_SIZE) + + dummy->extra_info->framesize; + } + else + { + /* It's still in the register. Don't forget to adjust + the "FP" with the framesize to get a real FP. */ + callers_addr = read_register (fp) + dummy->extra_info->framesize; + } + } + else + { + /* Our caller does not have a frame pointer. */ + callers_addr = fi->frame + dummy->extra_info->framesize; + } + + return callers_addr; +} + +/* Skip the prologue of the function at PC. */ + +CORE_ADDR +mcore_skip_prologue (CORE_ADDR pc) +{ + CORE_ADDR func_addr, func_end; + struct symtab_and_line sal; + + /* If we have line debugging information, then the end of the + prologue should be the first assembly instruction of the first + source line */ + if (find_pc_partial_function (pc, NULL, &func_addr, &func_end)) + { + sal = find_pc_line (func_addr, 0); + if (sal.end && sal.end < func_end) + return sal.end; + } + + return mcore_analyze_prologue (NULL, pc, 1); +} + +/* Return the address at which function arguments are offset. */ +CORE_ADDR +mcore_frame_args_address (struct frame_info * fi) +{ + return fi->frame - fi->extra_info->framesize; +} + +CORE_ADDR +mcore_frame_locals_address (struct frame_info * fi) +{ + return fi->frame - fi->extra_info->framesize; +} + +/* Return the frame pointer in use at address PC. */ + +void +mcore_virtual_frame_pointer (CORE_ADDR pc, int *reg, LONGEST *offset) +{ + struct frame_info *dummy = analyze_dummy_frame (pc, 0); + if (dummy->extra_info->status & MY_FRAME_IN_SP) + { + *reg = SP_REGNUM; + *offset = 0; + } + else + { + *reg = dummy->extra_info->fp_regnum; + *offset = 0; + } +} + +/* Find the value of register REGNUM in frame FI. */ + +CORE_ADDR +mcore_find_callers_reg (struct frame_info *fi, int regnum) +{ + for (; fi != NULL; fi = fi->next) + { + if (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame)) + return deprecated_read_register_dummy (fi->pc, fi->frame, regnum); + else if (fi->saved_regs[regnum] != 0) + return read_memory_integer (fi->saved_regs[regnum], + REGISTER_SIZE); + } + + return read_register (regnum); +} + +/* Find the saved pc in frame FI. */ + +CORE_ADDR +mcore_frame_saved_pc (struct frame_info * fi) +{ + + if (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame)) + return deprecated_read_register_dummy (fi->pc, fi->frame, PC_REGNUM); + else + return mcore_find_callers_reg (fi, PR_REGNUM); +} + +/* INFERIOR FUNCTION CALLS */ + +/* This routine gets called when either the user uses the "return" + command, or the call dummy breakpoint gets hit. */ + +void +mcore_pop_frame (void) +{ + int rn; + struct frame_info *fi = get_current_frame (); + + if (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame)) + generic_pop_dummy_frame (); + else + { + /* Write out the PC we saved. */ + write_register (PC_REGNUM, FRAME_SAVED_PC (fi)); + + /* Restore any saved registers. */ + for (rn = 0; rn < NUM_REGS; rn++) + { + if (fi->saved_regs[rn] != 0) + { + ULONGEST value; + + value = read_memory_unsigned_integer (fi->saved_regs[rn], + REGISTER_SIZE); + write_register (rn, value); + } + } + + /* Actually cut back the stack. */ + write_register (SP_REGNUM, FRAME_FP (fi)); + } + + /* Finally, throw away any cached frame information. */ + flush_cached_frames (); +} + +/* Setup arguments and PR for a call to the target. First six arguments + go in FIRST_ARGREG -> LAST_ARGREG, subsequent args go on to the stack. + + * Types with lengths greater than REGISTER_SIZE may not be split + between registers and the stack, and they must start in an even-numbered + register. Subsequent args will go onto the stack. + + * Structs may be split between registers and stack, left-aligned. + + * If the function returns a struct which will not fit into registers (it's + more than eight bytes), we must allocate for that, too. Gdb will tell + us where this buffer is (STRUCT_ADDR), and we simply place it into + FIRST_ARGREG, since the MCORE treats struct returns (of less than eight + bytes) as hidden first arguments. */ + +CORE_ADDR +mcore_push_arguments (int nargs, struct value **args, CORE_ADDR sp, + int struct_return, CORE_ADDR struct_addr) +{ + int argreg; + int argnum; + struct stack_arg + { + int len; + char *val; + } + *stack_args; + int nstack_args = 0; + + stack_args = (struct stack_arg *) alloca (nargs * sizeof (struct stack_arg)); + + argreg = FIRST_ARGREG; + + /* Align the stack. This is mostly a nop, but not always. It will be needed + if we call a function which has argument overflow. */ + sp &= ~3; + + /* If this function returns a struct which does not fit in the + return registers, we must pass a buffer to the function + which it can use to save the return value. */ + if (struct_return) + write_register (argreg++, struct_addr); + + /* FIXME: what about unions? */ + for (argnum = 0; argnum < nargs; argnum++) + { + char *val = (char *) VALUE_CONTENTS (args[argnum]); + int len = TYPE_LENGTH (VALUE_TYPE (args[argnum])); + struct type *type = VALUE_TYPE (args[argnum]); + int olen; + + mcore_insn_debug (("MCORE PUSH: argreg=%d; len=%d; %s\n", + argreg, len, TYPE_CODE (type) == TYPE_CODE_STRUCT ? "struct" : "not struct")); + /* Arguments larger than a register must start in an even + numbered register. */ + olen = len; + + if (TYPE_CODE (type) != TYPE_CODE_STRUCT && len > REGISTER_SIZE && argreg % 2) + { + mcore_insn_debug (("MCORE PUSH: %d > REGISTER_SIZE: and %s is not even\n", + len, mcore_register_names[argreg])); + argreg++; + } + + if ((argreg <= LAST_ARGREG && len <= (LAST_ARGREG - argreg + 1) * REGISTER_SIZE) + || (TYPE_CODE (type) == TYPE_CODE_STRUCT)) + { + /* Something that will fit entirely into registers (or a struct + which may be split between registers and stack). */ + mcore_insn_debug (("MCORE PUSH: arg %d going into regs\n", argnum)); + + if (TYPE_CODE (type) == TYPE_CODE_STRUCT && olen < REGISTER_SIZE) + { + /* Small structs must be right aligned within the register, + the most significant bits are undefined. */ + write_register (argreg, extract_unsigned_integer (val, len)); + argreg++; + len = 0; + } + + while (len > 0 && argreg <= LAST_ARGREG) + { + write_register (argreg, extract_unsigned_integer (val, REGISTER_SIZE)); + argreg++; + val += REGISTER_SIZE; + len -= REGISTER_SIZE; + } + + /* Any remainder for the stack is noted below... */ + } + else if (TYPE_CODE (VALUE_TYPE (args[argnum])) != TYPE_CODE_STRUCT + && len > REGISTER_SIZE) + { + /* All subsequent args go onto the stack. */ + mcore_insn_debug (("MCORE PUSH: does not fit into regs, going onto stack\n")); + argnum = LAST_ARGREG + 1; + } + + if (len > 0) + { + /* Note that this must be saved onto the stack */ + mcore_insn_debug (("MCORE PUSH: adding arg %d to stack\n", argnum)); + stack_args[nstack_args].val = val; + stack_args[nstack_args].len = len; + nstack_args++; + } + + } + + /* We're done with registers and stack allocation. Now do the actual + stack pushes. */ + while (nstack_args--) + { + sp -= stack_args[nstack_args].len; + write_memory (sp, stack_args[nstack_args].val, stack_args[nstack_args].len); + } + + /* Return adjusted stack pointer. */ + return sp; +} + +/* Store the return address for the call dummy. For MCore, we've + opted to use generic call dummies, so we simply store the + CALL_DUMMY_ADDRESS into the PR register (r15). */ + +CORE_ADDR +mcore_push_return_address (CORE_ADDR pc, CORE_ADDR sp) +{ + write_register (PR_REGNUM, CALL_DUMMY_ADDRESS ()); + return sp; +} + +/* Setting/getting return values from functions. + + The Motorola MCore processors use r2/r3 to return anything + not larger than 32 bits. Everything else goes into a caller- + supplied buffer, which is passed in via a hidden first + argument. + + For gdb, this leaves us two routes, based on what + USE_STRUCT_CONVENTION (mcore_use_struct_convention) returns. + If this macro returns 1, gdb will call STORE_STRUCT_RETURN and + EXTRACT_STRUCT_VALUE_ADDRESS. + + If USE_STRUCT_CONVENTION retruns 0, then gdb uses STORE_RETURN_VALUE + and EXTRACT_RETURN_VALUE to store/fetch the functions return value. */ + +/* Should we use EXTRACT_STRUCT_VALUE_ADDRESS instead of + EXTRACT_RETURN_VALUE? GCC_P is true if compiled with gcc + and TYPE is the type (which is known to be struct, union or array). */ + +int +mcore_use_struct_convention (int gcc_p, struct type *type) +{ + return (TYPE_LENGTH (type) > 8); +} + +/* Where is the return value saved? For MCore, a pointer to + this buffer was passed as a hidden first argument, so + just return that address. */ + +CORE_ADDR +mcore_extract_struct_value_address (char *regbuf) +{ + return extract_address (regbuf + REGISTER_BYTE (FIRST_ARGREG), REGISTER_SIZE); +} + +/* Given a function which returns a value of type TYPE, extract the + the function's return value and place the result into VALBUF. + REGBUF is the register contents of the target. */ + +void +mcore_extract_return_value (struct type *type, char *regbuf, char *valbuf) +{ + /* Copy the return value (starting) in RETVAL_REGNUM to VALBUF. */ + /* Only getting the first byte! if len = 1, we need the last byte of + the register, not the first. */ + memcpy (valbuf, regbuf + REGISTER_BYTE (RETVAL_REGNUM) + + (TYPE_LENGTH (type) < 4 ? 4 - TYPE_LENGTH (type) : 0), TYPE_LENGTH (type)); +} + +/* Store the return value in VALBUF (of type TYPE) where the caller + expects to see it. + + Values less than 32 bits are stored in r2, right justified and + sign or zero extended. + + Values between 32 and 64 bits are stored in r2 (most + significant word) and r3 (least significant word, left justified). + Note that this includes structures of less than eight bytes, too. */ + +void +mcore_store_return_value (struct type *type, char *valbuf) +{ + int value_size; + int return_size; + int offset; + char *zeros; + + value_size = TYPE_LENGTH (type); + + /* Return value fits into registers. */ + return_size = (value_size + REGISTER_SIZE - 1) & ~(REGISTER_SIZE - 1); + offset = REGISTER_BYTE (RETVAL_REGNUM) + (return_size - value_size); + zeros = alloca (return_size); + memset (zeros, 0, return_size); + + write_register_bytes (REGISTER_BYTE (RETVAL_REGNUM), zeros, return_size); + write_register_bytes (offset, valbuf, value_size); +} + +/* Initialize our target-dependent "stuff" for this newly created frame. + + This includes allocating space for saved registers and analyzing + the prologue of this frame. */ + +void +mcore_init_extra_frame_info (int fromleaf, struct frame_info *fi) +{ + if (fi && fi->next) + fi->pc = FRAME_SAVED_PC (fi->next); + + frame_saved_regs_zalloc (fi); + + fi->extra_info = (struct frame_extra_info *) + frame_obstack_alloc (sizeof (struct frame_extra_info)); + fi->extra_info->status = 0; + fi->extra_info->framesize = 0; + + if (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame)) + { + /* We need to setup fi->frame here because run_stack_dummy gets it wrong + by assuming it's always FP. */ + fi->frame = deprecated_read_register_dummy (fi->pc, fi->frame, SP_REGNUM); + } + else + mcore_analyze_prologue (fi, 0, 0); +} + +/* Get an insturction from memory. */ + +static int +get_insn (CORE_ADDR pc) +{ + char buf[4]; + int status = read_memory_nobpt (pc, buf, 2); + if (status != 0) + return 0; + + return extract_unsigned_integer (buf, 2); +} + +static struct gdbarch * +mcore_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) +{ + static LONGEST call_dummy_words[7] = { }; + struct gdbarch_tdep *tdep = NULL; + struct gdbarch *gdbarch; + + /* find a candidate among the list of pre-declared architectures. */ + arches = gdbarch_list_lookup_by_info (arches, &info); + if (arches != NULL) + return (arches->gdbarch); + + gdbarch = gdbarch_alloc (&info, 0); + + /* Registers: */ + + /* All registers are 32 bits */ + set_gdbarch_register_size (gdbarch, MCORE_REG_SIZE); + set_gdbarch_max_register_raw_size (gdbarch, MCORE_REG_SIZE); + set_gdbarch_max_register_virtual_size (gdbarch, MCORE_REG_SIZE); + set_gdbarch_register_name (gdbarch, mcore_register_name); + set_gdbarch_register_virtual_type (gdbarch, mcore_register_virtual_type); + set_gdbarch_register_virtual_size (gdbarch, mcore_register_size); + set_gdbarch_register_raw_size (gdbarch, mcore_register_size); + set_gdbarch_register_byte (gdbarch, mcore_register_byte); + set_gdbarch_register_bytes (gdbarch, MCORE_REG_SIZE * MCORE_NUM_REGS); + set_gdbarch_num_regs (gdbarch, MCORE_NUM_REGS); + set_gdbarch_pc_regnum (gdbarch, 64); + set_gdbarch_sp_regnum (gdbarch, 0); + set_gdbarch_fp_regnum (gdbarch, 0); + set_gdbarch_get_saved_register (gdbarch, generic_unwind_get_saved_register); + + /* Call Dummies: */ + + set_gdbarch_call_dummy_p (gdbarch, 1); + set_gdbarch_use_generic_dummy_frames (gdbarch, 1); + set_gdbarch_call_dummy_words (gdbarch, call_dummy_words); + set_gdbarch_sizeof_call_dummy_words (gdbarch, 0); + set_gdbarch_call_dummy_start_offset (gdbarch, 0); + set_gdbarch_call_dummy_breakpoint_offset_p (gdbarch, 1); + set_gdbarch_call_dummy_breakpoint_offset (gdbarch, 0); + set_gdbarch_call_dummy_location (gdbarch, AT_ENTRY_POINT); + set_gdbarch_fix_call_dummy (gdbarch, generic_fix_call_dummy); + set_gdbarch_call_dummy_address (gdbarch, entry_point_address); + set_gdbarch_save_dummy_frame_tos (gdbarch, generic_save_dummy_frame_tos); + set_gdbarch_pc_in_call_dummy (gdbarch, generic_pc_in_call_dummy); + set_gdbarch_call_dummy_stack_adjust_p (gdbarch, 0); + set_gdbarch_saved_pc_after_call (gdbarch, mcore_saved_pc_after_call); + set_gdbarch_function_start_offset (gdbarch, 0); + set_gdbarch_decr_pc_after_break (gdbarch, 0); + set_gdbarch_breakpoint_from_pc (gdbarch, mcore_breakpoint_from_pc); + set_gdbarch_push_return_address (gdbarch, mcore_push_return_address); + set_gdbarch_push_dummy_frame (gdbarch, generic_push_dummy_frame); + set_gdbarch_push_arguments (gdbarch, mcore_push_arguments); + set_gdbarch_call_dummy_length (gdbarch, 0); + + /* Frames: */ + + set_gdbarch_init_extra_frame_info (gdbarch, mcore_init_extra_frame_info); + set_gdbarch_frame_chain (gdbarch, mcore_frame_chain); + set_gdbarch_frame_chain_valid (gdbarch, generic_func_frame_chain_valid); + set_gdbarch_frame_init_saved_regs (gdbarch, mcore_frame_init_saved_regs); + set_gdbarch_frame_saved_pc (gdbarch, mcore_frame_saved_pc); + set_gdbarch_deprecated_store_return_value (gdbarch, mcore_store_return_value); + set_gdbarch_deprecated_extract_return_value (gdbarch, + mcore_extract_return_value); + set_gdbarch_store_struct_return (gdbarch, mcore_store_struct_return); + set_gdbarch_deprecated_extract_struct_value_address (gdbarch, + mcore_extract_struct_value_address); + set_gdbarch_skip_prologue (gdbarch, mcore_skip_prologue); + set_gdbarch_frame_args_skip (gdbarch, 0); + set_gdbarch_frame_args_address (gdbarch, mcore_frame_args_address); + set_gdbarch_frame_locals_address (gdbarch, mcore_frame_locals_address); + set_gdbarch_frame_num_args (gdbarch, frame_num_args_unknown); + set_gdbarch_pop_frame (gdbarch, mcore_pop_frame); + set_gdbarch_virtual_frame_pointer (gdbarch, mcore_virtual_frame_pointer); + + /* Misc.: */ + + /* Stack grows down. */ + set_gdbarch_inner_than (gdbarch, core_addr_lessthan); + set_gdbarch_use_struct_convention (gdbarch, mcore_use_struct_convention); + set_gdbarch_believe_pcc_promotion (gdbarch, 1); + /* MCore will never pass a sturcture by reference. It will always be split + between registers and stack. */ + set_gdbarch_reg_struct_has_addr (gdbarch, mcore_reg_struct_has_addr); + + return gdbarch; +} + +static void +mcore_dump_tdep (struct gdbarch *current_gdbarch, struct ui_file *file) +{ + +} + +void +_initialize_mcore_tdep (void) +{ + extern int print_insn_mcore (bfd_vma, disassemble_info *); + gdbarch_register (bfd_arch_mcore, mcore_gdbarch_init, mcore_dump_tdep); + tm_print_insn = print_insn_mcore; + +#ifdef MCORE_DEBUG + add_show_from_set (add_set_cmd ("mcoredebug", no_class, + var_boolean, (char *) &mcore_debug, + "Set mcore debugging.\n", &setlist), + &showlist); +#endif +} diff --git a/gdb/ns32k-tdep.c b/gdb/ns32k-tdep.c new file mode 100644 index 0000000..58c2e13 --- /dev/null +++ b/gdb/ns32k-tdep.c @@ -0,0 +1,626 @@ +/* Target dependent code for the NS32000, for GDB. + Copyright 1986, 1988, 1991, 1992, 1994, 1995, 1998, 1999, 2000, 2001, + 2002 Free Software Foundation, Inc. + + 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 "gdbtypes.h" +#include "gdbcore.h" +#include "inferior.h" +#include "regcache.h" +#include "target.h" + +#include "arch-utils.h" + +#include "ns32k-tdep.h" + +static int sign_extend (int value, int bits); +static CORE_ADDR ns32k_get_enter_addr (CORE_ADDR); +static int ns32k_localcount (CORE_ADDR enter_pc); +static void flip_bytes (void *, int); + +static const char * +ns32k_register_name_32082 (int regno) +{ + static char *register_names[] = + { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "sp", "fp", "pc", "ps", + "l0", "l1", "l2", "l3", "xx", + }; + + if (regno < 0) + return NULL; + if (regno >= sizeof (register_names) / sizeof (*register_names)) + return NULL; + + return (register_names[regno]); +} + +static const char * +ns32k_register_name_32382 (int regno) +{ + static char *register_names[] = + { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "sp", "fp", "pc", "ps", + "fsr", + "l0", "l1", "l2", "l3", "l4", "l5", "l6", "l7", "xx", + }; + + if (regno < 0) + return NULL; + if (regno >= sizeof (register_names) / sizeof (*register_names)) + return NULL; + + return (register_names[regno]); +} + +static int +ns32k_register_byte_32082 (int regno) +{ + if (regno >= NS32K_LP0_REGNUM) + return (NS32K_LP0_REGNUM * 4) + ((regno - NS32K_LP0_REGNUM) * 8); + + return (regno * 4); +} + +static int +ns32k_register_byte_32382 (int regno) +{ + /* This is a bit yuk. The even numbered double precision floating + point long registers occupy the same space as the even:odd numbered + single precision floating point registers, but the extra 32381 FPU + registers are at the end. Doing it this way is compatible for both + 32081 and 32381 equipped machines. */ + + return ((regno < NS32K_LP0_REGNUM ? regno + : (regno - NS32K_LP0_REGNUM) & 1 ? regno - 1 + : (regno - NS32K_LP0_REGNUM + FP0_REGNUM)) * 4); +} + +static int +ns32k_register_raw_size (int regno) +{ + /* All registers are 4 bytes, except for the doubled floating + registers. */ + + return ((regno >= NS32K_LP0_REGNUM) ? 8 : 4); +} + +static int +ns32k_register_virtual_size (int regno) +{ + return ((regno >= NS32K_LP0_REGNUM) ? 8 : 4); +} + +static struct type * +ns32k_register_virtual_type (int regno) +{ + if (regno < FP0_REGNUM) + return (builtin_type_int); + + if (regno < FP0_REGNUM + 8) + return (builtin_type_float); + + if (regno < NS32K_LP0_REGNUM) + return (builtin_type_int); + + return (builtin_type_double); +} + +/* Immediately after a function call, return the saved PC. Can't + always go through the frames for this because on some systems, + the new frame is not set up until the new function executes some + instructions. */ + +static CORE_ADDR +ns32k_saved_pc_after_call (struct frame_info *frame) +{ + return (read_memory_integer (read_register (SP_REGNUM), 4)); +} + +/* Advance PC across any function entry prologue instructions + to reach some "real" code. */ + +static CORE_ADDR +umax_skip_prologue (CORE_ADDR pc) +{ + register unsigned char op = read_memory_integer (pc, 1); + if (op == 0x82) + { + op = read_memory_integer (pc + 2, 1); + if ((op & 0x80) == 0) + pc += 3; + else if ((op & 0xc0) == 0x80) + pc += 4; + else + pc += 6; + } + return pc; +} + +static const unsigned char * +ns32k_breakpoint_from_pc (CORE_ADDR *pcp, int *lenp) +{ + static const unsigned char breakpoint_insn[] = { 0xf2 }; + + *lenp = sizeof (breakpoint_insn); + return breakpoint_insn; +} + +/* Return number of args passed to a frame. + Can return -1, meaning no way to tell. + Encore's C compiler often reuses same area on stack for args, + so this will often not work properly. If the arg names + are known, it's likely most of them will be printed. */ + +static int +umax_frame_num_args (struct frame_info *fi) +{ + int numargs; + CORE_ADDR pc; + CORE_ADDR enter_addr; + unsigned int insn; + unsigned int addr_mode; + int width; + + numargs = -1; + enter_addr = ns32k_get_enter_addr ((fi)->pc); + if (enter_addr > 0) + { + pc = ((enter_addr == 1) + ? SAVED_PC_AFTER_CALL (fi) + : FRAME_SAVED_PC (fi)); + insn = read_memory_integer (pc, 2); + addr_mode = (insn >> 11) & 0x1f; + insn = insn & 0x7ff; + if ((insn & 0x7fc) == 0x57c + && addr_mode == 0x14) /* immediate */ + { + if (insn == 0x57c) /* adjspb */ + width = 1; + else if (insn == 0x57d) /* adjspw */ + width = 2; + else if (insn == 0x57f) /* adjspd */ + width = 4; + else + internal_error (__FILE__, __LINE__, "bad else"); + numargs = read_memory_integer (pc + 2, width); + if (width > 1) + flip_bytes (&numargs, width); + numargs = -sign_extend (numargs, width * 8) / 4; + } + } + return numargs; +} + +static int +sign_extend (int value, int bits) +{ + value = value & ((1 << bits) - 1); + return (value & (1 << (bits - 1)) + ? value | (~((1 << bits) - 1)) + : value); +} + +static void +flip_bytes (void *p, int count) +{ + char tmp; + char *ptr = 0; + + while (count > 0) + { + tmp = *ptr; + ptr[0] = ptr[count - 1]; + ptr[count - 1] = tmp; + ptr++; + count -= 2; + } +} + +/* Return the number of locals in the current frame given a + pc pointing to the enter instruction. This is used by + ns32k_frame_init_saved_regs. */ + +static int +ns32k_localcount (CORE_ADDR enter_pc) +{ + unsigned char localtype; + int localcount; + + localtype = read_memory_integer (enter_pc + 2, 1); + if ((localtype & 0x80) == 0) + localcount = localtype; + else if ((localtype & 0xc0) == 0x80) + localcount = (((localtype & 0x3f) << 8) + | (read_memory_integer (enter_pc + 3, 1) & 0xff)); + else + localcount = (((localtype & 0x3f) << 24) + | ((read_memory_integer (enter_pc + 3, 1) & 0xff) << 16) + | ((read_memory_integer (enter_pc + 4, 1) & 0xff) << 8) + | (read_memory_integer (enter_pc + 5, 1) & 0xff)); + return localcount; +} + + +/* Nonzero if instruction at PC is a return instruction. */ + +static int +ns32k_about_to_return (CORE_ADDR pc) +{ + return (read_memory_integer (pc, 1) == 0x12); +} + +/* Get the address of the enter opcode for this function, if it is active. + Returns positive address > 1 if pc is between enter/exit, + 1 if pc before enter or after exit, 0 otherwise. */ +static CORE_ADDR +ns32k_get_enter_addr (CORE_ADDR pc) +{ + CORE_ADDR enter_addr; + unsigned char op; + + if (pc == 0) + return 0; + + if (ns32k_about_to_return (pc)) + return 1; /* after exit */ + + enter_addr = get_pc_function_start (pc); + + if (pc == enter_addr) + return 1; /* before enter */ + + op = read_memory_integer (enter_addr, 1); + + if (op != 0x82) + return 0; /* function has no enter/exit */ + + return enter_addr; /* pc is between enter and exit */ +} + +static CORE_ADDR +ns32k_frame_chain (struct frame_info *frame) +{ + /* In the case of the NS32000 series, the frame's nominal address is the + FP value, and that address is saved at the previous FP value as a + 4-byte word. */ + + if (inside_entry_file (frame->pc)) + return 0; + + return (read_memory_integer (frame->frame, 4)); +} + +static CORE_ADDR +ns32k_frame_saved_pc (struct frame_info *frame) +{ + if (frame->signal_handler_caller) + return (sigtramp_saved_pc (frame)); /* XXXJRT */ + + return (read_memory_integer (frame->frame + 4, 4)); +} + +static CORE_ADDR +ns32k_frame_args_address (struct frame_info *frame) +{ + if (ns32k_get_enter_addr (frame->pc) > 1) + return (frame->frame); + + return (read_register (SP_REGNUM) - 4); +} + +static CORE_ADDR +ns32k_frame_locals_address (struct frame_info *frame) +{ + return (frame->frame); +} + +/* Code to initialize the addresses of the saved registers of frame described + by FRAME_INFO. This includes special registers such as pc and fp saved in + special ways in the stack frame. sp is even more special: the address we + return for it IS the sp for the next frame. */ + +static void +ns32k_frame_init_saved_regs (struct frame_info *frame) +{ + int regmask, regnum; + int localcount; + CORE_ADDR enter_addr, next_addr; + + if (frame->saved_regs) + return; + + frame_saved_regs_zalloc (frame); + + enter_addr = ns32k_get_enter_addr (frame->pc); + if (enter_addr > 1) + { + regmask = read_memory_integer (enter_addr + 1, 1) & 0xff; + localcount = ns32k_localcount (enter_addr); + next_addr = frame->frame + localcount; + + for (regnum = 0; regnum < 8; regnum++) + { + if (regmask & (1 << regnum)) + frame->saved_regs[regnum] = next_addr -= 4; + } + + frame->saved_regs[SP_REGNUM] = frame->frame + 4; + frame->saved_regs[PC_REGNUM] = frame->frame + 4; + frame->saved_regs[FP_REGNUM] = read_memory_integer (frame->frame, 4); + } + else if (enter_addr == 1) + { + CORE_ADDR sp = read_register (SP_REGNUM); + frame->saved_regs[PC_REGNUM] = sp; + frame->saved_regs[SP_REGNUM] = sp + 4; + } +} + +static void +ns32k_push_dummy_frame (void) +{ + CORE_ADDR sp = read_register (SP_REGNUM); + int regnum; + + sp = push_word (sp, read_register (PC_REGNUM)); + sp = push_word (sp, read_register (FP_REGNUM)); + write_register (FP_REGNUM, sp); + + for (regnum = 0; regnum < 8; regnum++) + sp = push_word (sp, read_register (regnum)); + + write_register (SP_REGNUM, sp); +} + +static void +ns32k_pop_frame (void) +{ + struct frame_info *frame = get_current_frame (); + CORE_ADDR fp; + int regnum; + + fp = frame->frame; + FRAME_INIT_SAVED_REGS (frame); + + for (regnum = 0; regnum < 8; regnum++) + if (frame->saved_regs[regnum]) + write_register (regnum, + read_memory_integer (frame->saved_regs[regnum], 4)); + + write_register (FP_REGNUM, read_memory_integer (fp, 4)); + write_register (PC_REGNUM, read_memory_integer (fp + 4, 4)); + write_register (SP_REGNUM, fp + 8); + flush_cached_frames (); +} + +/* The NS32000 call dummy sequence: + + enter 0xff,0 82 ff 00 + jsr @0x00010203 7f ae c0 01 02 03 + adjspd 0x69696969 7f a5 01 02 03 04 + bpt f2 + + It is 16 bytes long. */ + +static LONGEST ns32k_call_dummy_words[] = +{ + 0x7f00ff82, + 0x0201c0ae, + 0x01a57f03, + 0xf2040302 +}; +static int sizeof_ns32k_call_dummy_words = sizeof (ns32k_call_dummy_words); + +#define NS32K_CALL_DUMMY_ADDR 5 +#define NS32K_CALL_DUMMY_NARGS 11 + +static void +ns32k_fix_call_dummy (char *dummy, CORE_ADDR pc, CORE_ADDR fun, int nargs, + struct value **args, struct type *type, int gcc_p) +{ + int flipped; + + flipped = fun | 0xc0000000; + flip_bytes (&flipped, 4); + store_unsigned_integer (dummy + NS32K_CALL_DUMMY_ADDR, 4, flipped); + + flipped = - nargs * 4; + flip_bytes (&flipped, 4); + store_unsigned_integer (dummy + NS32K_CALL_DUMMY_NARGS, 4, flipped); +} + +static void +ns32k_store_struct_return (CORE_ADDR addr, CORE_ADDR sp) +{ + /* On this machine, this is a no-op (Encore Umax didn't use GCC). */ +} + +static void +ns32k_extract_return_value (struct type *valtype, char *regbuf, char *valbuf) +{ + memcpy (valbuf, + regbuf + REGISTER_BYTE (TYPE_CODE (valtype) == TYPE_CODE_FLT ? + FP0_REGNUM : 0), TYPE_LENGTH (valtype)); +} + +static void +ns32k_store_return_value (struct type *valtype, char *valbuf) +{ + write_register_bytes (TYPE_CODE (valtype) == TYPE_CODE_FLT ? + FP0_REGNUM : 0, valbuf, TYPE_LENGTH (valtype)); +} + +static CORE_ADDR +ns32k_extract_struct_value_address (char *regbuf) +{ + return (extract_address (regbuf + REGISTER_BYTE (0), REGISTER_RAW_SIZE (0))); +} + +void +ns32k_gdbarch_init_32082 (struct gdbarch *gdbarch) +{ + set_gdbarch_num_regs (gdbarch, NS32K_NUM_REGS_32082); + + set_gdbarch_register_name (gdbarch, ns32k_register_name_32082); + set_gdbarch_register_bytes (gdbarch, NS32K_REGISTER_BYTES_32082); + set_gdbarch_register_byte (gdbarch, ns32k_register_byte_32082); +} + +void +ns32k_gdbarch_init_32382 (struct gdbarch *gdbarch) +{ + set_gdbarch_num_regs (gdbarch, NS32K_NUM_REGS_32382); + + set_gdbarch_register_name (gdbarch, ns32k_register_name_32382); + set_gdbarch_register_bytes (gdbarch, NS32K_REGISTER_BYTES_32382); + set_gdbarch_register_byte (gdbarch, ns32k_register_byte_32382); +} + +/* Initialize the current architecture based on INFO. If possible, re-use an + architecture from ARCHES, which is a list of architectures already created + during this debugging session. + + Called e.g. at program startup, when reading a core file, and when reading + a binary file. */ + +static struct gdbarch * +ns32k_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) +{ + struct gdbarch_tdep *tdep; + struct gdbarch *gdbarch; + enum gdb_osabi osabi = GDB_OSABI_UNKNOWN; + + /* Try to determine the OS ABI of the object we are loading. */ + if (info.abfd != NULL) + { + osabi = gdbarch_lookup_osabi (info.abfd); + } + + /* Find a candidate among extant architectures. */ + for (arches = gdbarch_list_lookup_by_info (arches, &info); + arches != NULL; + arches = gdbarch_list_lookup_by_info (arches->next, &info)) + { + /* Make sure the OS ABI selection matches. */ + tdep = gdbarch_tdep (arches->gdbarch); + if (tdep && tdep->osabi == osabi) + return arches->gdbarch; + } + + tdep = xmalloc (sizeof (struct gdbarch_tdep)); + gdbarch = gdbarch_alloc (&info, tdep); + + tdep->osabi = osabi; + + /* Register info */ + ns32k_gdbarch_init_32082 (gdbarch); + set_gdbarch_num_regs (gdbarch, NS32K_SP_REGNUM); + set_gdbarch_num_regs (gdbarch, NS32K_FP_REGNUM); + set_gdbarch_num_regs (gdbarch, NS32K_PC_REGNUM); + set_gdbarch_num_regs (gdbarch, NS32K_PS_REGNUM); + + set_gdbarch_register_size (gdbarch, NS32K_REGISTER_SIZE); + set_gdbarch_register_raw_size (gdbarch, ns32k_register_raw_size); + set_gdbarch_max_register_raw_size (gdbarch, NS32K_MAX_REGISTER_RAW_SIZE); + set_gdbarch_register_virtual_size (gdbarch, ns32k_register_virtual_size); + set_gdbarch_max_register_virtual_size (gdbarch, + NS32K_MAX_REGISTER_VIRTUAL_SIZE); + set_gdbarch_register_virtual_type (gdbarch, ns32k_register_virtual_type); + + /* Frame and stack info */ + set_gdbarch_skip_prologue (gdbarch, umax_skip_prologue); + set_gdbarch_saved_pc_after_call (gdbarch, ns32k_saved_pc_after_call); + + set_gdbarch_frame_num_args (gdbarch, umax_frame_num_args); + set_gdbarch_frameless_function_invocation (gdbarch, + generic_frameless_function_invocation_not); + + set_gdbarch_frame_chain (gdbarch, ns32k_frame_chain); + set_gdbarch_frame_chain_valid (gdbarch, func_frame_chain_valid); + set_gdbarch_frame_saved_pc (gdbarch, ns32k_frame_saved_pc); + + set_gdbarch_frame_args_address (gdbarch, ns32k_frame_args_address); + set_gdbarch_frame_locals_address (gdbarch, ns32k_frame_locals_address); + + set_gdbarch_frame_init_saved_regs (gdbarch, ns32k_frame_init_saved_regs); + + set_gdbarch_frame_args_skip (gdbarch, 8); + + set_gdbarch_inner_than (gdbarch, core_addr_lessthan); + + /* Return value info */ + set_gdbarch_store_struct_return (gdbarch, ns32k_store_struct_return); + set_gdbarch_deprecated_extract_return_value (gdbarch, ns32k_extract_return_value); + set_gdbarch_deprecated_store_return_value (gdbarch, ns32k_store_return_value); + set_gdbarch_deprecated_extract_struct_value_address (gdbarch, + ns32k_extract_struct_value_address); + + /* Call dummy info */ + set_gdbarch_push_dummy_frame (gdbarch, ns32k_push_dummy_frame); + set_gdbarch_pop_frame (gdbarch, ns32k_pop_frame); + set_gdbarch_call_dummy_location (gdbarch, ON_STACK); + set_gdbarch_call_dummy_p (gdbarch, 1); + set_gdbarch_call_dummy_words (gdbarch, ns32k_call_dummy_words); + set_gdbarch_sizeof_call_dummy_words (gdbarch, sizeof_ns32k_call_dummy_words); + set_gdbarch_fix_call_dummy (gdbarch, ns32k_fix_call_dummy); + set_gdbarch_call_dummy_start_offset (gdbarch, 3); + set_gdbarch_call_dummy_breakpoint_offset_p (gdbarch, 0); + set_gdbarch_use_generic_dummy_frames (gdbarch, 0); + set_gdbarch_pc_in_call_dummy (gdbarch, pc_in_call_dummy_on_stack); + set_gdbarch_call_dummy_stack_adjust_p (gdbarch, 0); + + /* Breakpoint info */ + set_gdbarch_decr_pc_after_break (gdbarch, 0); + set_gdbarch_breakpoint_from_pc (gdbarch, ns32k_breakpoint_from_pc); + + /* Misc info */ + set_gdbarch_function_start_offset (gdbarch, 0); + + /* Hook in OS ABI-specific overrides, if they have been registered. */ + gdbarch_init_osabi (info, gdbarch, osabi); + + return (gdbarch); +} + +static void +ns32k_dump_tdep (struct gdbarch *current_gdbarch, struct ui_file *file) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + + if (tdep == NULL) + return; + + fprintf_unfiltered (file, "ns32k_dump_tdep: OS ABI = %s\n", + gdbarch_osabi_name (tdep->osabi)); +} + +void +_initialize_ns32k_tdep (void) +{ + gdbarch_register (bfd_arch_ns32k, ns32k_gdbarch_init, ns32k_dump_tdep); + + tm_print_insn = print_insn_ns32k; +} diff --git a/gdb/sh3-rom.c b/gdb/sh3-rom.c new file mode 100644 index 0000000..f450ac9 --- /dev/null +++ b/gdb/sh3-rom.c @@ -0,0 +1,384 @@ +/* Remote target glue for the Hitachi SH-3 ROM monitor. + Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001 + Free Software Foundation, Inc. + + 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 "gdbcore.h" +#include "target.h" +#include "monitor.h" +#include "serial.h" +#include "srec.h" +#include "arch-utils.h" +#include "regcache.h" + +#include "sh-tdep.h" + +static struct serial *parallel; +static int parallel_in_use; + +static void sh3_open (char *args, int from_tty); + +static void +sh3_supply_register (char *regname, int regnamelen, char *val, int vallen) +{ + int numregs; + int regno; + + numregs = 1; + regno = -1; + + if (regnamelen == 2) + { + switch (regname[0]) + { + case 'S': + if (regname[1] == 'R') + regno = SR_REGNUM; + break; + case 'P': + if (regname[1] == 'C') + regno = PC_REGNUM; + else if (regname[1] == 'R') + regno = PR_REGNUM; + break; + } + } + else if (regnamelen == 3) + { + switch (regname[0]) + { + case 'G': + case 'V': + if (regname[1] == 'B' && regname[2] == 'R') + { + if (regname[0] == 'G') + regno = VBR_REGNUM; + else + regno = GBR_REGNUM; + } + break; + case 'S': + if (regname[1] == 'S' && regname[2] == 'R') + regno = gdbarch_tdep (current_gdbarch)->SSR_REGNUM; + else if (regname[1] == 'P' && regname[2] == 'C') + regno = gdbarch_tdep (current_gdbarch)->SPC_REGNUM; + break; + } + } + else if (regnamelen == 4) + { + switch (regname[0]) + { + case 'M': + if (regname[1] == 'A' && regname[2] == 'C') + { + if (regname[3] == 'H') + regno = MACH_REGNUM; + else if (regname[3] == 'L') + regno = MACL_REGNUM; + } + break; + case 'R': + if (regname[1] == '0' && regname[2] == '-' && regname[3] == '7') + { + regno = R0_REGNUM; + numregs = 8; + } + } + } + else if (regnamelen == 5) + { + if (regname[1] == '8' && regname[2] == '-' && regname[3] == '1' + && regname[4] == '5') + { + regno = R0_REGNUM + 8; + numregs = 8; + } + } + else if (regnamelen == 17) + { + } + + if (regno >= 0) + while (numregs-- > 0) + val = monitor_supply_register (regno++, val); +} + +static void +sh3_load (struct serial *desc, char *file, int hashmark) +{ + if (parallel_in_use) + { + monitor_printf ("pl;s\r"); + load_srec (parallel, file, 0, 80, SREC_ALL, hashmark, NULL); + monitor_expect_prompt (NULL, 0); + } + else + { + monitor_printf ("il;s:x\r"); + monitor_expect ("\005", NULL, 0); /* Look for ENQ */ + serial_write (desc, "\006", 1); /* Send ACK */ + monitor_expect ("LO x\r", NULL, 0); /* Look for filename */ + + load_srec (desc, file, 0, 80, SREC_ALL, hashmark, NULL); + + monitor_expect ("\005", NULL, 0); /* Look for ENQ */ + serial_write (desc, "\006", 1); /* Send ACK */ + monitor_expect_prompt (NULL, 0); + } +} + +/* This array of registers need to match the indexes used by GDB. + This exists because the various ROM monitors use different strings + than does GDB, and don't necessarily support all the registers + either. So, typing "info reg sp" becomes a "r30". */ + +static char *sh3_regnames[] = +{ + "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", + "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", + "PC", "PR", "GBR", "VBR", "MACH", "MACL", "SR", + NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + "SSR", "SPC", + "R0_BANK0", "R1_BANK0", "R2_BANK0", "R3_BANK0", + "R4_BANK0", "R5_BANK0", "R6_BANK0", "R7_BANK0", + "R0_BANK1", "R1_BANK1", "R2_BANK1", "R3_BANK1", + "R4_BANK1", "R5_BANK1", "R6_BANK1", "R7_BANK1" +}; + +static char *sh3e_regnames[] = +{ + "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", + "R8", "R9", "R10", "R11", "R12", "R13", "R14", "R15", + "PC", "PR", "GBR", "VBR", "MACH", "MACL", "SR", + "FPUL", "FPSCR", + "FR0", "FR1", "FR2", "FR3", "FR4", "FR5", "FR6", "FR7", + "FR8", "FR9", "FR10", "FR11", "FR12", "FR13", "FR14", "FR15", + "SSR", "SPC", + "R0_BANK0", "R1_BANK0", "R2_BANK0", "R3_BANK0", + "R4_BANK0", "R5_BANK0", "R6_BANK0", "R7_BANK0", + "R0_BANK1", "R1_BANK1", "R2_BANK1", "R3_BANK1", + "R4_BANK1", "R5_BANK1", "R6_BANK1", "R7_BANK1" +}; + +/* Define the monitor command strings. Since these are passed directly + through to a printf style function, we may include formatting + strings. We also need a CR or LF on the end. */ + +static struct target_ops sh3_ops, sh3e_ops; + +static char *sh3_inits[] = +{"\003", NULL}; /* Exits sub-command mode & download cmds */ + +static struct monitor_ops sh3_cmds; + +static void +init_sh3_cmds (void) +{ + sh3_cmds.flags = MO_CLR_BREAK_USES_ADDR | MO_GETMEM_READ_SINGLE; /* flags */ + sh3_cmds.init = sh3_inits; /* monitor init string */ + sh3_cmds.cont = "g\r"; /* continue command */ + sh3_cmds.step = "s\r"; /* single step */ + sh3_cmds.stop = "\003"; /* Interrupt program */ + sh3_cmds.set_break = "b %x\r"; /* set a breakpoint */ + sh3_cmds.clr_break = "b -%x\r"; /* clear a breakpoint */ + sh3_cmds.clr_all_break = "b -\r"; /* clear all breakpoints */ + sh3_cmds.fill = "f %x @%x %x\r"; /* fill (start len val) */ + sh3_cmds.setmem.cmdb = "m %x %x\r"; /* setmem.cmdb (addr, value) */ + sh3_cmds.setmem.cmdw = "m %x %x;w\r"; /* setmem.cmdw (addr, value) */ + sh3_cmds.setmem.cmdl = "m %x %x;l\r"; /* setmem.cmdl (addr, value) */ + sh3_cmds.setmem.cmdll = NULL; /* setmem.cmdll (addr, value) */ + sh3_cmds.setmem.resp_delim = NULL; /* setreg.resp_delim */ + sh3_cmds.setmem.term = NULL; /* setreg.term */ + sh3_cmds.setmem.term_cmd = NULL; /* setreg.term_cmd */ + sh3_cmds.getmem.cmdb = "m %x\r"; /* getmem.cmdb (addr, len) */ + sh3_cmds.getmem.cmdw = "m %x;w\r"; /* getmem.cmdw (addr, len) */ + sh3_cmds.getmem.cmdl = "m %x;l\r"; /* getmem.cmdl (addr, len) */ + sh3_cmds.getmem.cmdll = NULL; /* getmem.cmdll (addr, len) */ + sh3_cmds.getmem.resp_delim = "^ [0-9A-F]+ "; /* getmem.resp_delim */ + sh3_cmds.getmem.term = "? "; /* getmem.term */ + sh3_cmds.getmem.term_cmd = ".\r"; /* getmem.term_cmd */ + sh3_cmds.setreg.cmd = ".%s %x\r"; /* setreg.cmd (name, value) */ + sh3_cmds.setreg.resp_delim = NULL; /* setreg.resp_delim */ + sh3_cmds.setreg.term = NULL; /* setreg.term */ + sh3_cmds.setreg.term_cmd = NULL; /* setreg.term_cmd */ + sh3_cmds.getreg.cmd = ".%s\r"; /* getreg.cmd (name) */ + sh3_cmds.getreg.resp_delim = "="; /* getreg.resp_delim */ + sh3_cmds.getreg.term = "? "; /* getreg.term */ + sh3_cmds.getreg.term_cmd = ".\r"; /* getreg.term_cmd */ + sh3_cmds.dump_registers = "r\r"; /* dump_registers */ + sh3_cmds.register_pattern = "\\(\\w+\\)=\\([0-9a-fA-F]+\\( +[0-9a-fA-F]+\\b\\)*\\)"; + sh3_cmds.supply_register = sh3_supply_register; /* supply_register */ + sh3_cmds.load_routine = sh3_load; /* load_routine */ + sh3_cmds.load = NULL; /* download command */ + sh3_cmds.loadresp = NULL; /* Load response */ + sh3_cmds.prompt = "\n:"; /* monitor command prompt */ + sh3_cmds.line_term = "\r"; /* end-of-line terminator */ + sh3_cmds.cmd_end = ".\r"; /* optional command terminator */ + sh3_cmds.target = &sh3_ops; /* target operations */ + sh3_cmds.stopbits = SERIAL_1_STOPBITS; /* number of stop bits */ + sh3_cmds.regnames = sh3_regnames; /* registers names */ + sh3_cmds.magic = MONITOR_OPS_MAGIC; /* magic */ +} /* init_sh3_cmds */ + +/* This monitor structure is identical except for a couple slots, so + we will fill it in from the base structure when needed. */ + +static struct monitor_ops sh3e_cmds; + +static void +sh3_open (char *args, int from_tty) +{ + char *serial_port_name = args; + char *parallel_port_name = 0; + + if (args) + { + char *cursor = serial_port_name = xstrdup (args); + + while (*cursor && *cursor != ' ') + cursor++; + + if (*cursor) + *cursor++ = 0; + + while (*cursor == ' ') + cursor++; + + if (*cursor) + parallel_port_name = cursor; + } + + monitor_open (serial_port_name, &sh3_cmds, from_tty); + + if (parallel_port_name) + { + parallel = serial_open (parallel_port_name); + + if (!parallel) + perror_with_name ("Unable to open parallel port."); + + parallel_in_use = 1; + } + + /* If we connected successfully, we know the processor is an SH3. */ + set_architecture_from_arch_mach (bfd_arch_sh, bfd_mach_sh3); +} + + +static void +sh3e_open (char *args, int from_tty) +{ + char *serial_port_name = args; + char *parallel_port_name = 0; + + if (args) + { + char *cursor = serial_port_name = xstrdup (args); + + while (*cursor && *cursor != ' ') + cursor++; + + if (*cursor) + *cursor++ = 0; + + while (*cursor == ' ') + cursor++; + + if (*cursor) + parallel_port_name = cursor; + } + + /* Set up the SH-3E monitor commands structure. */ + + memcpy (&sh3e_cmds, &sh3_cmds, sizeof (struct monitor_ops)); + + sh3e_cmds.target = &sh3e_ops; + sh3e_cmds.regnames = sh3e_regnames; + + monitor_open (serial_port_name, &sh3e_cmds, from_tty); + + if (parallel_port_name) + { + parallel = serial_open (parallel_port_name); + + if (!parallel) + perror_with_name ("Unable to open parallel port."); + + parallel_in_use = 1; + } + + /* If we connected successfully, we know the processor is an SH3E. */ + set_architecture_from_arch_mach (bfd_arch_sh, bfd_mach_sh3); +} + +static void +sh3_close (int quitting) +{ + monitor_close (quitting); + if (parallel_in_use) + { + serial_close (parallel); + parallel_in_use = 0; + } +} + +void +_initialize_sh3_rom (void) +{ + init_sh3_cmds (); + init_monitor_ops (&sh3_ops); + + sh3_ops.to_shortname = "sh3"; + sh3_ops.to_longname = "Hitachi SH-3 rom monitor"; + + sh3_ops.to_doc = + /* We can download through the parallel port too. */ + "Debug on a Hitachi eval board running the SH-3E rom monitor.\n" + "Specify the serial device it is connected to.\n" + "If you want to use the parallel port to download to it, specify that\n" + "as an additional second argument."; + + sh3_ops.to_open = sh3_open; + sh3_ops.to_close = sh3_close; + + add_target (&sh3_ops); + + /* Setup the SH3e, which has float registers. */ + + init_monitor_ops (&sh3e_ops); + + sh3e_ops.to_shortname = "sh3e"; + sh3e_ops.to_longname = "Hitachi SH-3E rom monitor"; + + sh3e_ops.to_doc = + /* We can download through the parallel port too. */ + "Debug on a Hitachi eval board running the SH-3E rom monitor.\n" + "Specify the serial device it is connected to.\n" + "If you want to use the parallel port to download to it, specify that\n" + "as an additional second argument."; + + sh3e_ops.to_open = sh3e_open; + sh3e_ops.to_close = sh3_close; + + add_target (&sh3e_ops); +} diff --git a/gdb/vax-tdep.c b/gdb/vax-tdep.c new file mode 100644 index 0000000..709ef38 --- /dev/null +++ b/gdb/vax-tdep.c @@ -0,0 +1,702 @@ +/* Print VAX instructions for GDB, the GNU debugger. + Copyright 1986, 1989, 1991, 1992, 1995, 1996, 1998, 1999, 2000, 2002 + Free Software Foundation, Inc. + + 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 "symtab.h" +#include "opcode/vax.h" +#include "gdbcore.h" +#include "inferior.h" +#include "regcache.h" +#include "frame.h" +#include "value.h" +#include "arch-utils.h" + +#include "vax-tdep.h" + +static gdbarch_register_name_ftype vax_register_name; +static gdbarch_register_byte_ftype vax_register_byte; +static gdbarch_register_raw_size_ftype vax_register_raw_size; +static gdbarch_register_virtual_size_ftype vax_register_virtual_size; +static gdbarch_register_virtual_type_ftype vax_register_virtual_type; + +static gdbarch_skip_prologue_ftype vax_skip_prologue; +static gdbarch_saved_pc_after_call_ftype vax_saved_pc_after_call; +static gdbarch_frame_num_args_ftype vax_frame_num_args; +static gdbarch_frame_chain_ftype vax_frame_chain; +static gdbarch_frame_saved_pc_ftype vax_frame_saved_pc; +static gdbarch_frame_args_address_ftype vax_frame_args_address; +static gdbarch_frame_locals_address_ftype vax_frame_locals_address; +static gdbarch_frame_init_saved_regs_ftype vax_frame_init_saved_regs; + +static gdbarch_store_struct_return_ftype vax_store_struct_return; +static gdbarch_deprecated_extract_return_value_ftype vax_extract_return_value; +static gdbarch_deprecated_extract_struct_value_address_ftype + vax_extract_struct_value_address; + +static gdbarch_push_dummy_frame_ftype vax_push_dummy_frame; +static gdbarch_pop_frame_ftype vax_pop_frame; +static gdbarch_fix_call_dummy_ftype vax_fix_call_dummy; + +/* Return 1 if P points to an invalid floating point value. + LEN is the length in bytes -- not relevant on the Vax. */ + +/* FIXME: cagney/2002-01-19: The macro below was originally defined in + tm-vax.h and used in values.c. Two problems. Firstly this is a + very non-portable and secondly it is wrong. The VAX should be + using floatformat and associated methods to identify and handle + invalid floating-point values. Adding to the poor target's woes + there is no floatformat_vax_{f,d} and no TARGET_FLOAT_FORMAT + et.al.. */ + +/* FIXME: cagney/2002-01-19: It turns out that the only thing that + uses this macro is the vax disassembler code (so how old is this + target?). This target should instead be using the opcodes + disassembler. That allowing the macro to be eliminated. */ + +#define INVALID_FLOAT(p, len) ((*(short *) p & 0xff80) == 0x8000) + +/* Vax instructions are never longer than this. */ +#define MAXLEN 62 + +/* Number of elements in the opcode table. */ +#define NOPCODES (sizeof votstrs / sizeof votstrs[0]) + +static unsigned char *print_insn_arg (); + +static const char * +vax_register_name (int regno) +{ + static char *register_names[] = + { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "ap", "fp", "sp", "pc", + "ps", + }; + + if (regno < 0) + return (NULL); + if (regno >= (sizeof(register_names) / sizeof(*register_names))) + return (NULL); + return (register_names[regno]); +} + +static int +vax_register_byte (int regno) +{ + return (regno * 4); +} + +static int +vax_register_raw_size (int regno) +{ + return (4); +} + +static int +vax_register_virtual_size (int regno) +{ + return (4); +} + +static struct type * +vax_register_virtual_type (int regno) +{ + return (builtin_type_int); +} + +static void +vax_frame_init_saved_regs (struct frame_info *frame) +{ + int regnum, regmask; + CORE_ADDR next_addr; + + if (frame->saved_regs) + return; + + frame_saved_regs_zalloc (frame); + + regmask = read_memory_integer (frame->frame + 4, 4) >> 16; + + next_addr = frame->frame + 16; + + /* regmask's low bit is for register 0, which is the first one + what would be pushed. */ + for (regnum = 0; regnum < VAX_AP_REGNUM; regnum++) + { + if (regmask & (1 << regnum)) + frame->saved_regs[regnum] = next_addr += 4; + } + + frame->saved_regs[SP_REGNUM] = next_addr + 4; + if (regmask & (1 << FP_REGNUM)) + frame->saved_regs[SP_REGNUM] += + 4 + (4 * read_memory_integer (next_addr + 4, 4)); + + frame->saved_regs[PC_REGNUM] = frame->frame + 16; + frame->saved_regs[FP_REGNUM] = frame->frame + 12; + frame->saved_regs[VAX_AP_REGNUM] = frame->frame + 8; + frame->saved_regs[PS_REGNUM] = frame->frame + 4; +} + +static CORE_ADDR +vax_frame_saved_pc (struct frame_info *frame) +{ + if (frame->signal_handler_caller) + return (sigtramp_saved_pc (frame)); /* XXXJRT */ + + return (read_memory_integer (frame->frame + 16, 4)); +} + +CORE_ADDR +vax_frame_args_address_correct (struct frame_info *frame) +{ + /* Cannot find the AP register value directly from the FP value. Must + find it saved in the frame called by this one, or in the AP register + for the innermost frame. However, there is no way to tell the + difference between the innermost frame and a frame for which we + just don't know the frame that it called (e.g. "info frame 0x7ffec789"). + For the sake of argument, suppose that the stack is somewhat trashed + (which is one reason that "info frame" exists). So, return 0 (indicating + we don't know the address of the arglist) if we don't know what frame + this frame calls. */ + if (frame->next) + return (read_memory_integer (frame->next->frame + 8, 4)); + + return (0); +} + +static CORE_ADDR +vax_frame_args_address (struct frame_info *frame) +{ + /* In most of GDB, getting the args address is too important to + just say "I don't know". This is sometimes wrong for functions + that aren't on top of the stack, but c'est la vie. */ + if (frame->next) + return (read_memory_integer (frame->next->frame + 8, 4)); + + return (read_register (VAX_AP_REGNUM)); +} + +static CORE_ADDR +vax_frame_locals_address (struct frame_info *frame) +{ + return (frame->frame); +} + +static int +vax_frame_num_args (struct frame_info *fi) +{ + return (0xff & read_memory_integer (FRAME_ARGS_ADDRESS (fi), 1)); +} + +static CORE_ADDR +vax_frame_chain (struct frame_info *frame) +{ + /* In the case of the VAX, the frame's nominal address is the FP value, + and 12 bytes later comes the saved previous FP value as a 4-byte word. */ + if (inside_entry_file (frame->pc)) + return (0); + + return (read_memory_integer (frame->frame + 12, 4)); +} + +static void +vax_push_dummy_frame (void) +{ + CORE_ADDR sp = read_register (SP_REGNUM); + int regnum; + + sp = push_word (sp, 0); /* arglist */ + for (regnum = 11; regnum >= 0; regnum--) + sp = push_word (sp, read_register (regnum)); + sp = push_word (sp, read_register (PC_REGNUM)); + sp = push_word (sp, read_register (FP_REGNUM)); + sp = push_word (sp, read_register (VAX_AP_REGNUM)); + sp = push_word (sp, (read_register (PS_REGNUM) & 0xffef) + 0x2fff0000); + sp = push_word (sp, 0); + write_register (SP_REGNUM, sp); + write_register (FP_REGNUM, sp); + write_register (VAX_AP_REGNUM, sp + (17 * 4)); +} + +static void +vax_pop_frame (void) +{ + CORE_ADDR fp = read_register (FP_REGNUM); + int regnum; + int regmask = read_memory_integer (fp + 4, 4); + + write_register (PS_REGNUM, + (regmask & 0xffff) + | (read_register (PS_REGNUM) & 0xffff0000)); + write_register (PC_REGNUM, read_memory_integer (fp + 16, 4)); + write_register (FP_REGNUM, read_memory_integer (fp + 12, 4)); + write_register (VAX_AP_REGNUM, read_memory_integer (fp + 8, 4)); + fp += 16; + for (regnum = 0; regnum < 12; regnum++) + if (regmask & (0x10000 << regnum)) + write_register (regnum, read_memory_integer (fp += 4, 4)); + fp = fp + 4 + ((regmask >> 30) & 3); + if (regmask & 0x20000000) + { + regnum = read_memory_integer (fp, 4); + fp += (regnum + 1) * 4; + } + write_register (SP_REGNUM, fp); + flush_cached_frames (); +} + +/* The VAX call dummy sequence: + + calls #69, @#32323232 + bpt + + It is 8 bytes long. The address and argc are patched by + vax_fix_call_dummy(). */ +static LONGEST vax_call_dummy_words[] = { 0x329f69fb, 0x03323232 }; +static int sizeof_vax_call_dummy_words = sizeof(vax_call_dummy_words); + +static void +vax_fix_call_dummy (char *dummy, CORE_ADDR pc, CORE_ADDR fun, int nargs, + struct value **args, struct type *type, int gcc_p) +{ + dummy[1] = nargs; + store_unsigned_integer (dummy + 3, 4, fun); +} + +static void +vax_store_struct_return (CORE_ADDR addr, CORE_ADDR sp) +{ + write_register (1, addr); +} + +static void +vax_extract_return_value (struct type *valtype, char *regbuf, char *valbuf) +{ + memcpy (valbuf, regbuf + REGISTER_BYTE (0), TYPE_LENGTH (valtype)); +} + +static void +vax_store_return_value (struct type *valtype, char *valbuf) +{ + write_register_bytes (0, valbuf, TYPE_LENGTH (valtype)); +} + +static CORE_ADDR +vax_extract_struct_value_address (char *regbuf) +{ + return (extract_address (regbuf + REGISTER_BYTE (0), REGISTER_RAW_SIZE (0))); +} + +static const unsigned char * +vax_breakpoint_from_pc (CORE_ADDR *pcptr, int *lenptr) +{ + static const unsigned char vax_breakpoint[] = { 3 }; + + *lenptr = sizeof(vax_breakpoint); + return (vax_breakpoint); +} + +/* Advance PC across any function entry prologue instructions + to reach some "real" code. */ + +static CORE_ADDR +vax_skip_prologue (CORE_ADDR pc) +{ + register int op = (unsigned char) read_memory_integer (pc, 1); + if (op == 0x11) + pc += 2; /* skip brb */ + if (op == 0x31) + pc += 3; /* skip brw */ + if (op == 0xC2 + && ((unsigned char) read_memory_integer (pc + 2, 1)) == 0x5E) + pc += 3; /* skip subl2 */ + if (op == 0x9E + && ((unsigned char) read_memory_integer (pc + 1, 1)) == 0xAE + && ((unsigned char) read_memory_integer (pc + 3, 1)) == 0x5E) + pc += 4; /* skip movab */ + if (op == 0x9E + && ((unsigned char) read_memory_integer (pc + 1, 1)) == 0xCE + && ((unsigned char) read_memory_integer (pc + 4, 1)) == 0x5E) + pc += 5; /* skip movab */ + if (op == 0x9E + && ((unsigned char) read_memory_integer (pc + 1, 1)) == 0xEE + && ((unsigned char) read_memory_integer (pc + 6, 1)) == 0x5E) + pc += 7; /* skip movab */ + return pc; +} + +static CORE_ADDR +vax_saved_pc_after_call (struct frame_info *frame) +{ + return (FRAME_SAVED_PC(frame)); +} + +/* Print the vax instruction at address MEMADDR in debugged memory, + from disassembler info INFO. + Returns length of the instruction, in bytes. */ + +static int +vax_print_insn (CORE_ADDR memaddr, disassemble_info *info) +{ + unsigned char buffer[MAXLEN]; + register int i; + register unsigned char *p; + const char *d; + + int status = (*info->read_memory_func) (memaddr, buffer, MAXLEN, info); + if (status != 0) + { + (*info->memory_error_func) (status, memaddr, info); + return -1; + } + + for (i = 0; i < NOPCODES; i++) + if (votstrs[i].detail.code == buffer[0] + || votstrs[i].detail.code == *(unsigned short *) buffer) + break; + + /* Handle undefined instructions. */ + if (i == NOPCODES) + { + (*info->fprintf_func) (info->stream, "0%o", buffer[0]); + return 1; + } + + (*info->fprintf_func) (info->stream, "%s", votstrs[i].name); + + /* Point at first byte of argument data, + and at descriptor for first argument. */ + p = buffer + 1 + (votstrs[i].detail.code >= 0x100); + d = votstrs[i].detail.args; + + if (*d) + (*info->fprintf_func) (info->stream, " "); + + while (*d) + { + p = print_insn_arg (d, p, memaddr + (p - buffer), info); + d += 2; + if (*d) + (*info->fprintf_func) (info->stream, ","); + } + return p - buffer; +} + +static unsigned char * +print_insn_arg (char *d, register char *p, CORE_ADDR addr, + disassemble_info *info) +{ + register int regnum = *p & 0xf; + float floatlitbuf; + + if (*d == 'b') + { + if (d[1] == 'b') + (*info->fprintf_func) (info->stream, "0x%x", addr + *p++ + 1); + else + { + (*info->fprintf_func) (info->stream, "0x%x", addr + *(short *) p + 2); + p += 2; + } + } + else + switch ((*p++ >> 4) & 0xf) + { + case 0: + case 1: + case 2: + case 3: /* Literal mode */ + if (d[1] == 'd' || d[1] == 'f' || d[1] == 'g' || d[1] == 'h') + { + *(int *) &floatlitbuf = 0x4000 + ((p[-1] & 0x3f) << 4); + (*info->fprintf_func) (info->stream, "$%f", floatlitbuf); + } + else + (*info->fprintf_func) (info->stream, "$%d", p[-1] & 0x3f); + break; + + case 4: /* Indexed */ + p = (char *) print_insn_arg (d, p, addr + 1, info); + (*info->fprintf_func) (info->stream, "[%s]", REGISTER_NAME (regnum)); + break; + + case 5: /* Register */ + (*info->fprintf_func) (info->stream, REGISTER_NAME (regnum)); + break; + + case 7: /* Autodecrement */ + (*info->fprintf_func) (info->stream, "-"); + case 6: /* Register deferred */ + (*info->fprintf_func) (info->stream, "(%s)", REGISTER_NAME (regnum)); + break; + + case 9: /* Autoincrement deferred */ + (*info->fprintf_func) (info->stream, "@"); + if (regnum == PC_REGNUM) + { + (*info->fprintf_func) (info->stream, "#"); + info->target = *(long *) p; + (*info->print_address_func) (info->target, info); + p += 4; + break; + } + case 8: /* Autoincrement */ + if (regnum == PC_REGNUM) + { + (*info->fprintf_func) (info->stream, "#"); + switch (d[1]) + { + case 'b': + (*info->fprintf_func) (info->stream, "%d", *p++); + break; + + case 'w': + (*info->fprintf_func) (info->stream, "%d", *(short *) p); + p += 2; + break; + + case 'l': + (*info->fprintf_func) (info->stream, "%d", *(long *) p); + p += 4; + break; + + case 'q': + (*info->fprintf_func) (info->stream, "0x%x%08x", + ((long *) p)[1], ((long *) p)[0]); + p += 8; + break; + + case 'o': + (*info->fprintf_func) (info->stream, "0x%x%08x%08x%08x", + ((long *) p)[3], ((long *) p)[2], + ((long *) p)[1], ((long *) p)[0]); + p += 16; + break; + + case 'f': + if (INVALID_FLOAT (p, 4)) + (*info->fprintf_func) (info->stream, + "<<invalid float 0x%x>>", + *(int *) p); + else + (*info->fprintf_func) (info->stream, "%f", *(float *) p); + p += 4; + break; + + case 'd': + if (INVALID_FLOAT (p, 8)) + (*info->fprintf_func) (info->stream, + "<<invalid float 0x%x%08x>>", + ((long *) p)[1], ((long *) p)[0]); + else + (*info->fprintf_func) (info->stream, "%f", *(double *) p); + p += 8; + break; + + case 'g': + (*info->fprintf_func) (info->stream, "g-float"); + p += 8; + break; + + case 'h': + (*info->fprintf_func) (info->stream, "h-float"); + p += 16; + break; + + } + } + else + (*info->fprintf_func) (info->stream, "(%s)+", REGISTER_NAME (regnum)); + break; + + case 11: /* Byte displacement deferred */ + (*info->fprintf_func) (info->stream, "@"); + case 10: /* Byte displacement */ + if (regnum == PC_REGNUM) + { + info->target = addr + *p + 2; + (*info->print_address_func) (info->target, info); + } + else + (*info->fprintf_func) (info->stream, "%d(%s)", *p, REGISTER_NAME (regnum)); + p += 1; + break; + + case 13: /* Word displacement deferred */ + (*info->fprintf_func) (info->stream, "@"); + case 12: /* Word displacement */ + if (regnum == PC_REGNUM) + { + info->target = addr + *(short *) p + 3; + (*info->print_address_func) (info->target, info); + } + else + (*info->fprintf_func) (info->stream, "%d(%s)", + *(short *) p, REGISTER_NAME (regnum)); + p += 2; + break; + + case 15: /* Long displacement deferred */ + (*info->fprintf_func) (info->stream, "@"); + case 14: /* Long displacement */ + if (regnum == PC_REGNUM) + { + info->target = addr + *(short *) p + 5; + (*info->print_address_func) (info->target, info); + } + else + (*info->fprintf_func) (info->stream, "%d(%s)", + *(long *) p, REGISTER_NAME (regnum)); + p += 4; + } + + return (unsigned char *) p; +} + +/* Initialize the current architecture based on INFO. If possible, re-use an + architecture from ARCHES, which is a list of architectures already created + during this debugging session. + + Called e.g. at program startup, when reading a core file, and when reading + a binary file. */ + +static struct gdbarch * +vax_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) +{ + struct gdbarch_tdep *tdep; + struct gdbarch *gdbarch; + enum gdb_osabi osabi = GDB_OSABI_UNKNOWN; + + /* Try to determine the ABI of the object we are loading. */ + + if (info.abfd != NULL) + osabi = gdbarch_lookup_osabi (info.abfd); + + /* Find a candidate among extant architectures. */ + for (arches = gdbarch_list_lookup_by_info (arches, &info); + arches != NULL; + arches = gdbarch_list_lookup_by_info (arches->next, &info)) + { + /* Make sure the ABI selection matches. */ + tdep = gdbarch_tdep (arches->gdbarch); + if (tdep && tdep->osabi == osabi) + return arches->gdbarch; + } + + tdep = xmalloc (sizeof (struct gdbarch_tdep)); + gdbarch = gdbarch_alloc (&info, tdep); + + tdep->osabi = osabi; + + /* Register info */ + set_gdbarch_num_regs (gdbarch, VAX_NUM_REGS); + set_gdbarch_sp_regnum (gdbarch, VAX_SP_REGNUM); + set_gdbarch_fp_regnum (gdbarch, VAX_FP_REGNUM); + set_gdbarch_pc_regnum (gdbarch, VAX_PC_REGNUM); + set_gdbarch_ps_regnum (gdbarch, VAX_PS_REGNUM); + + set_gdbarch_register_name (gdbarch, vax_register_name); + set_gdbarch_register_size (gdbarch, VAX_REGISTER_SIZE); + set_gdbarch_register_bytes (gdbarch, VAX_REGISTER_BYTES); + set_gdbarch_register_byte (gdbarch, vax_register_byte); + set_gdbarch_register_raw_size (gdbarch, vax_register_raw_size); + set_gdbarch_max_register_raw_size (gdbarch, VAX_MAX_REGISTER_RAW_SIZE); + set_gdbarch_register_virtual_size (gdbarch, vax_register_virtual_size); + set_gdbarch_max_register_virtual_size (gdbarch, + VAX_MAX_REGISTER_VIRTUAL_SIZE); + set_gdbarch_register_virtual_type (gdbarch, vax_register_virtual_type); + + /* Frame and stack info */ + set_gdbarch_skip_prologue (gdbarch, vax_skip_prologue); + set_gdbarch_saved_pc_after_call (gdbarch, vax_saved_pc_after_call); + + set_gdbarch_frame_num_args (gdbarch, vax_frame_num_args); + set_gdbarch_frameless_function_invocation (gdbarch, + generic_frameless_function_invocation_not); + + set_gdbarch_frame_chain (gdbarch, vax_frame_chain); + set_gdbarch_frame_chain_valid (gdbarch, func_frame_chain_valid); + set_gdbarch_frame_saved_pc (gdbarch, vax_frame_saved_pc); + + set_gdbarch_frame_args_address (gdbarch, vax_frame_args_address); + set_gdbarch_frame_locals_address (gdbarch, vax_frame_locals_address); + + set_gdbarch_frame_init_saved_regs (gdbarch, vax_frame_init_saved_regs); + + set_gdbarch_frame_args_skip (gdbarch, 4); + + set_gdbarch_inner_than (gdbarch, core_addr_lessthan); + + /* Return value info */ + set_gdbarch_store_struct_return (gdbarch, vax_store_struct_return); + set_gdbarch_deprecated_extract_return_value (gdbarch, vax_extract_return_value); + set_gdbarch_deprecated_store_return_value (gdbarch, vax_store_return_value); + set_gdbarch_deprecated_extract_struct_value_address (gdbarch, vax_extract_struct_value_address); + + /* Call dummy info */ + set_gdbarch_push_dummy_frame (gdbarch, vax_push_dummy_frame); + set_gdbarch_pop_frame (gdbarch, vax_pop_frame); + set_gdbarch_call_dummy_location (gdbarch, ON_STACK); + set_gdbarch_call_dummy_p (gdbarch, 1); + set_gdbarch_call_dummy_words (gdbarch, vax_call_dummy_words); + set_gdbarch_sizeof_call_dummy_words (gdbarch, sizeof_vax_call_dummy_words); + set_gdbarch_fix_call_dummy (gdbarch, vax_fix_call_dummy); + set_gdbarch_call_dummy_start_offset (gdbarch, 0); + set_gdbarch_call_dummy_breakpoint_offset_p (gdbarch, 1); + set_gdbarch_call_dummy_breakpoint_offset (gdbarch, 7); + set_gdbarch_use_generic_dummy_frames (gdbarch, 0); + set_gdbarch_pc_in_call_dummy (gdbarch, pc_in_call_dummy_on_stack); + set_gdbarch_call_dummy_stack_adjust_p (gdbarch, 0); + + /* Breakpoint info */ + set_gdbarch_breakpoint_from_pc (gdbarch, vax_breakpoint_from_pc); + set_gdbarch_decr_pc_after_break (gdbarch, 0); + + /* Misc info */ + set_gdbarch_function_start_offset (gdbarch, 2); + set_gdbarch_believe_pcc_promotion (gdbarch, 1); + + /* Hook in ABI-specific overrides, if they have been registered. */ + gdbarch_init_osabi (info, gdbarch, osabi); + + return (gdbarch); +} + +static void +vax_dump_tdep (struct gdbarch *current_gdbarch, struct ui_file *file) +{ + struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch); + + if (tdep == NULL) + return; + + fprintf_unfiltered (file, "vax_dump_tdep: OS ABI = %s\n", + gdbarch_osabi_name (tdep->osabi)); +} + +void +_initialize_vax_tdep (void) +{ + gdbarch_register (bfd_arch_vax, vax_gdbarch_init, vax_dump_tdep); + + tm_print_insn = vax_print_insn; +} |