| /* Target-dependent code for Morpho mt processor, for GDB. |
| |
| Copyright (C) 2005, 2007-2012 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 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
| |
| /* Contributed by Michael Snyder, msnyder@redhat.com. */ |
| |
| #include "defs.h" |
| #include "frame.h" |
| #include "frame-unwind.h" |
| #include "frame-base.h" |
| #include "symtab.h" |
| #include "dis-asm.h" |
| #include "arch-utils.h" |
| #include "gdbtypes.h" |
| #include "gdb_string.h" |
| #include "regcache.h" |
| #include "reggroups.h" |
| #include "gdbcore.h" |
| #include "trad-frame.h" |
| #include "inferior.h" |
| #include "dwarf2-frame.h" |
| #include "infcall.h" |
| #include "gdb_assert.h" |
| #include "language.h" |
| #include "valprint.h" |
| |
| enum mt_arch_constants |
| { |
| MT_MAX_STRUCT_SIZE = 16 |
| }; |
| |
| enum mt_gdb_regnums |
| { |
| MT_R0_REGNUM, /* 32 bit regs. */ |
| MT_R1_REGNUM, |
| MT_1ST_ARGREG = MT_R1_REGNUM, |
| MT_R2_REGNUM, |
| MT_R3_REGNUM, |
| MT_R4_REGNUM, |
| MT_LAST_ARGREG = MT_R4_REGNUM, |
| MT_R5_REGNUM, |
| MT_R6_REGNUM, |
| MT_R7_REGNUM, |
| MT_R8_REGNUM, |
| MT_R9_REGNUM, |
| MT_R10_REGNUM, |
| MT_R11_REGNUM, |
| MT_R12_REGNUM, |
| MT_FP_REGNUM = MT_R12_REGNUM, |
| MT_R13_REGNUM, |
| MT_SP_REGNUM = MT_R13_REGNUM, |
| MT_R14_REGNUM, |
| MT_RA_REGNUM = MT_R14_REGNUM, |
| MT_R15_REGNUM, |
| MT_IRA_REGNUM = MT_R15_REGNUM, |
| MT_PC_REGNUM, |
| |
| /* Interrupt Enable pseudo-register, exported by SID. */ |
| MT_INT_ENABLE_REGNUM, |
| /* End of CPU regs. */ |
| |
| MT_NUM_CPU_REGS, |
| |
| /* Co-processor registers. */ |
| MT_COPRO_REGNUM = MT_NUM_CPU_REGS, /* 16 bit regs. */ |
| MT_CPR0_REGNUM, |
| MT_CPR1_REGNUM, |
| MT_CPR2_REGNUM, |
| MT_CPR3_REGNUM, |
| MT_CPR4_REGNUM, |
| MT_CPR5_REGNUM, |
| MT_CPR6_REGNUM, |
| MT_CPR7_REGNUM, |
| MT_CPR8_REGNUM, |
| MT_CPR9_REGNUM, |
| MT_CPR10_REGNUM, |
| MT_CPR11_REGNUM, |
| MT_CPR12_REGNUM, |
| MT_CPR13_REGNUM, |
| MT_CPR14_REGNUM, |
| MT_CPR15_REGNUM, |
| MT_BYPA_REGNUM, /* 32 bit regs. */ |
| MT_BYPB_REGNUM, |
| MT_BYPC_REGNUM, |
| MT_FLAG_REGNUM, |
| MT_CONTEXT_REGNUM, /* 38 bits (treat as array of |
| six bytes). */ |
| MT_MAC_REGNUM, /* 32 bits. */ |
| MT_Z1_REGNUM, /* 16 bits. */ |
| MT_Z2_REGNUM, /* 16 bits. */ |
| MT_ICHANNEL_REGNUM, /* 32 bits. */ |
| MT_ISCRAMB_REGNUM, /* 32 bits. */ |
| MT_QSCRAMB_REGNUM, /* 32 bits. */ |
| MT_OUT_REGNUM, /* 16 bits. */ |
| MT_EXMAC_REGNUM, /* 32 bits (8 used). */ |
| MT_QCHANNEL_REGNUM, /* 32 bits. */ |
| MT_ZI2_REGNUM, /* 16 bits. */ |
| MT_ZQ2_REGNUM, /* 16 bits. */ |
| MT_CHANNEL2_REGNUM, /* 32 bits. */ |
| MT_ISCRAMB2_REGNUM, /* 32 bits. */ |
| MT_QSCRAMB2_REGNUM, /* 32 bits. */ |
| MT_QCHANNEL2_REGNUM, /* 32 bits. */ |
| |
| /* Number of real registers. */ |
| MT_NUM_REGS, |
| |
| /* Pseudo-registers. */ |
| MT_COPRO_PSEUDOREG_REGNUM = MT_NUM_REGS, |
| MT_MAC_PSEUDOREG_REGNUM, |
| MT_COPRO_PSEUDOREG_ARRAY, |
| |
| MT_COPRO_PSEUDOREG_DIM_1 = 2, |
| MT_COPRO_PSEUDOREG_DIM_2 = 8, |
| /* The number of pseudo-registers for each coprocessor. These |
| include the real coprocessor registers, the pseudo-registe for |
| the coprocessor number, and the pseudo-register for the MAC. */ |
| MT_COPRO_PSEUDOREG_REGS = MT_NUM_REGS - MT_NUM_CPU_REGS + 2, |
| /* The register number of the MAC, relative to a given coprocessor. */ |
| MT_COPRO_PSEUDOREG_MAC_REGNUM = MT_COPRO_PSEUDOREG_REGS - 1, |
| |
| /* Two pseudo-regs ('coprocessor' and 'mac'). */ |
| MT_NUM_PSEUDO_REGS = 2 + (MT_COPRO_PSEUDOREG_REGS |
| * MT_COPRO_PSEUDOREG_DIM_1 |
| * MT_COPRO_PSEUDOREG_DIM_2) |
| }; |
| |
| /* The tdep structure. */ |
| struct gdbarch_tdep |
| { |
| /* ISA-specific types. */ |
| struct type *copro_type; |
| }; |
| |
| |
| /* Return name of register number specified by REGNUM. */ |
| |
| static const char * |
| mt_register_name (struct gdbarch *gdbarch, int regnum) |
| { |
| static const char *const register_names[] = { |
| /* CPU regs. */ |
| "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", |
| "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", |
| "pc", "IE", |
| /* Co-processor regs. */ |
| "", /* copro register. */ |
| "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7", |
| "cr8", "cr9", "cr10", "cr11", "cr12", "cr13", "cr14", "cr15", |
| "bypa", "bypb", "bypc", "flag", "context", "" /* mac. */ , "z1", "z2", |
| "Ichannel", "Iscramb", "Qscramb", "out", "" /* ex-mac. */ , "Qchannel", |
| "zi2", "zq2", "Ichannel2", "Iscramb2", "Qscramb2", "Qchannel2", |
| /* Pseudo-registers. */ |
| "coprocessor", "MAC" |
| }; |
| static const char *array_names[MT_COPRO_PSEUDOREG_REGS |
| * MT_COPRO_PSEUDOREG_DIM_1 |
| * MT_COPRO_PSEUDOREG_DIM_2]; |
| |
| if (regnum < 0) |
| return ""; |
| if (regnum < ARRAY_SIZE (register_names)) |
| return register_names[regnum]; |
| if (array_names[regnum - MT_COPRO_PSEUDOREG_ARRAY]) |
| return array_names[regnum - MT_COPRO_PSEUDOREG_ARRAY]; |
| |
| { |
| char *name; |
| const char *stub; |
| unsigned dim_1; |
| unsigned dim_2; |
| unsigned index; |
| |
| regnum -= MT_COPRO_PSEUDOREG_ARRAY; |
| index = regnum % MT_COPRO_PSEUDOREG_REGS; |
| dim_2 = (regnum / MT_COPRO_PSEUDOREG_REGS) % MT_COPRO_PSEUDOREG_DIM_2; |
| dim_1 = ((regnum / MT_COPRO_PSEUDOREG_REGS / MT_COPRO_PSEUDOREG_DIM_2) |
| % MT_COPRO_PSEUDOREG_DIM_1); |
| |
| if (index == MT_COPRO_PSEUDOREG_MAC_REGNUM) |
| stub = register_names[MT_MAC_PSEUDOREG_REGNUM]; |
| else if (index >= MT_NUM_REGS - MT_CPR0_REGNUM) |
| stub = ""; |
| else |
| stub = register_names[index + MT_CPR0_REGNUM]; |
| if (!*stub) |
| { |
| array_names[regnum] = stub; |
| return stub; |
| } |
| name = xmalloc (30); |
| sprintf (name, "copro_%d_%d_%s", dim_1, dim_2, stub); |
| array_names[regnum] = name; |
| return name; |
| } |
| } |
| |
| /* Return the type of a coprocessor register. */ |
| |
| static struct type * |
| mt_copro_register_type (struct gdbarch *arch, int regnum) |
| { |
| switch (regnum) |
| { |
| case MT_INT_ENABLE_REGNUM: |
| case MT_ICHANNEL_REGNUM: |
| case MT_QCHANNEL_REGNUM: |
| case MT_ISCRAMB_REGNUM: |
| case MT_QSCRAMB_REGNUM: |
| return builtin_type (arch)->builtin_int32; |
| case MT_BYPA_REGNUM: |
| case MT_BYPB_REGNUM: |
| case MT_BYPC_REGNUM: |
| case MT_Z1_REGNUM: |
| case MT_Z2_REGNUM: |
| case MT_OUT_REGNUM: |
| case MT_ZI2_REGNUM: |
| case MT_ZQ2_REGNUM: |
| return builtin_type (arch)->builtin_int16; |
| case MT_EXMAC_REGNUM: |
| case MT_MAC_REGNUM: |
| return builtin_type (arch)->builtin_uint32; |
| case MT_CONTEXT_REGNUM: |
| return builtin_type (arch)->builtin_long_long; |
| case MT_FLAG_REGNUM: |
| return builtin_type (arch)->builtin_unsigned_char; |
| default: |
| if (regnum >= MT_CPR0_REGNUM && regnum <= MT_CPR15_REGNUM) |
| return builtin_type (arch)->builtin_int16; |
| else if (regnum == MT_CPR0_REGNUM + MT_COPRO_PSEUDOREG_MAC_REGNUM) |
| { |
| if (gdbarch_bfd_arch_info (arch)->mach == bfd_mach_mrisc2 |
| || gdbarch_bfd_arch_info (arch)->mach == bfd_mach_ms2) |
| return builtin_type (arch)->builtin_uint64; |
| else |
| return builtin_type (arch)->builtin_uint32; |
| } |
| else |
| return builtin_type (arch)->builtin_uint32; |
| } |
| } |
| |
| /* Given ARCH and a register number specified by REGNUM, return the |
| type of that register. */ |
| |
| static struct type * |
| mt_register_type (struct gdbarch *arch, int regnum) |
| { |
| struct gdbarch_tdep *tdep = gdbarch_tdep (arch); |
| |
| if (regnum >= 0 && regnum < MT_NUM_REGS + MT_NUM_PSEUDO_REGS) |
| { |
| switch (regnum) |
| { |
| case MT_PC_REGNUM: |
| case MT_RA_REGNUM: |
| case MT_IRA_REGNUM: |
| return builtin_type (arch)->builtin_func_ptr; |
| case MT_SP_REGNUM: |
| case MT_FP_REGNUM: |
| return builtin_type (arch)->builtin_data_ptr; |
| case MT_COPRO_REGNUM: |
| case MT_COPRO_PSEUDOREG_REGNUM: |
| if (tdep->copro_type == NULL) |
| { |
| struct type *elt = builtin_type (arch)->builtin_int16; |
| tdep->copro_type = lookup_array_range_type (elt, 0, 1); |
| } |
| return tdep->copro_type; |
| case MT_MAC_PSEUDOREG_REGNUM: |
| return mt_copro_register_type (arch, |
| MT_CPR0_REGNUM |
| + MT_COPRO_PSEUDOREG_MAC_REGNUM); |
| default: |
| if (regnum >= MT_R0_REGNUM && regnum <= MT_R15_REGNUM) |
| return builtin_type (arch)->builtin_int32; |
| else if (regnum < MT_COPRO_PSEUDOREG_ARRAY) |
| return mt_copro_register_type (arch, regnum); |
| else |
| { |
| regnum -= MT_COPRO_PSEUDOREG_ARRAY; |
| regnum %= MT_COPRO_PSEUDOREG_REGS; |
| regnum += MT_CPR0_REGNUM; |
| return mt_copro_register_type (arch, regnum); |
| } |
| } |
| } |
| internal_error (__FILE__, __LINE__, |
| _("mt_register_type: illegal register number %d"), regnum); |
| } |
| |
| /* Return true if register REGNUM is a member of the register group |
| specified by GROUP. */ |
| |
| static int |
| mt_register_reggroup_p (struct gdbarch *gdbarch, int regnum, |
| struct reggroup *group) |
| { |
| /* Groups of registers that can be displayed via "info reg". */ |
| if (group == all_reggroup) |
| return (regnum >= 0 |
| && regnum < MT_NUM_REGS + MT_NUM_PSEUDO_REGS |
| && mt_register_name (gdbarch, regnum)[0] != '\0'); |
| |
| if (group == general_reggroup) |
| return (regnum >= MT_R0_REGNUM && regnum <= MT_R15_REGNUM); |
| |
| if (group == float_reggroup) |
| return 0; /* No float regs. */ |
| |
| if (group == vector_reggroup) |
| return 0; /* No vector regs. */ |
| |
| /* For any that are not handled above. */ |
| return default_register_reggroup_p (gdbarch, regnum, group); |
| } |
| |
| /* Return the return value convention used for a given type TYPE. |
| Optionally, fetch or set the return value via READBUF or |
| WRITEBUF respectively using REGCACHE for the register |
| values. */ |
| |
| static enum return_value_convention |
| mt_return_value (struct gdbarch *gdbarch, struct value *function, |
| struct type *type, struct regcache *regcache, |
| gdb_byte *readbuf, const gdb_byte *writebuf) |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| |
| if (TYPE_LENGTH (type) > 4) |
| { |
| /* Return values > 4 bytes are returned in memory, |
| pointed to by R11. */ |
| if (readbuf) |
| { |
| ULONGEST addr; |
| |
| regcache_cooked_read_unsigned (regcache, MT_R11_REGNUM, &addr); |
| read_memory (addr, readbuf, TYPE_LENGTH (type)); |
| } |
| |
| if (writebuf) |
| { |
| ULONGEST addr; |
| |
| regcache_cooked_read_unsigned (regcache, MT_R11_REGNUM, &addr); |
| write_memory (addr, writebuf, TYPE_LENGTH (type)); |
| } |
| |
| return RETURN_VALUE_ABI_RETURNS_ADDRESS; |
| } |
| else |
| { |
| if (readbuf) |
| { |
| ULONGEST temp; |
| |
| /* Return values of <= 4 bytes are returned in R11. */ |
| regcache_cooked_read_unsigned (regcache, MT_R11_REGNUM, &temp); |
| store_unsigned_integer (readbuf, TYPE_LENGTH (type), |
| byte_order, temp); |
| } |
| |
| if (writebuf) |
| { |
| if (TYPE_LENGTH (type) < 4) |
| { |
| gdb_byte buf[4]; |
| /* Add leading zeros to the value. */ |
| memset (buf, 0, sizeof (buf)); |
| memcpy (buf + sizeof (buf) - TYPE_LENGTH (type), |
| writebuf, TYPE_LENGTH (type)); |
| regcache_cooked_write (regcache, MT_R11_REGNUM, buf); |
| } |
| else /* (TYPE_LENGTH (type) == 4 */ |
| regcache_cooked_write (regcache, MT_R11_REGNUM, writebuf); |
| } |
| |
| return RETURN_VALUE_REGISTER_CONVENTION; |
| } |
| } |
| |
| /* If the input address, PC, is in a function prologue, return the |
| address of the end of the prologue, otherwise return the input |
| address. |
| |
| Note: PC is likely to be the function start, since this function |
| is mainly used for advancing a breakpoint to the first line, or |
| stepping to the first line when we have stepped into a function |
| call. */ |
| |
| static CORE_ADDR |
| mt_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR pc) |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| CORE_ADDR func_addr = 0, func_end = 0; |
| const char *func_name; |
| unsigned long instr; |
| |
| if (find_pc_partial_function (pc, &func_name, &func_addr, &func_end)) |
| { |
| struct symtab_and_line sal; |
| struct symbol *sym; |
| |
| /* Found a function. */ |
| sym = lookup_symbol (func_name, NULL, VAR_DOMAIN, NULL); |
| if (sym && SYMBOL_LANGUAGE (sym) != language_asm) |
| { |
| /* Don't use this trick for assembly source files. */ |
| sal = find_pc_line (func_addr, 0); |
| |
| if (sal.end && sal.end < func_end) |
| { |
| /* Found a line number, use it as end of prologue. */ |
| return sal.end; |
| } |
| } |
| } |
| |
| /* No function symbol, or no line symbol. Use prologue scanning method. */ |
| for (;; pc += 4) |
| { |
| instr = read_memory_unsigned_integer (pc, 4, byte_order); |
| if (instr == 0x12000000) /* nop */ |
| continue; |
| if (instr == 0x12ddc000) /* copy sp into fp */ |
| continue; |
| instr >>= 16; |
| if (instr == 0x05dd) /* subi sp, sp, imm */ |
| continue; |
| if (instr >= 0x43c0 && instr <= 0x43df) /* push */ |
| continue; |
| /* Not an obvious prologue instruction. */ |
| break; |
| } |
| |
| return pc; |
| } |
| |
| /* The breakpoint instruction must be the same size as the smallest |
| instruction in the instruction set. |
| |
| The BP for ms1 is defined as 0x68000000 (BREAK). |
| The BP for ms2 is defined as 0x69000000 (illegal). */ |
| |
| static const gdb_byte * |
| mt_breakpoint_from_pc (struct gdbarch *gdbarch, CORE_ADDR *bp_addr, |
| int *bp_size) |
| { |
| static gdb_byte ms1_breakpoint[] = { 0x68, 0, 0, 0 }; |
| static gdb_byte ms2_breakpoint[] = { 0x69, 0, 0, 0 }; |
| |
| *bp_size = 4; |
| if (gdbarch_bfd_arch_info (gdbarch)->mach == bfd_mach_ms2) |
| return ms2_breakpoint; |
| |
| return ms1_breakpoint; |
| } |
| |
| /* Select the correct coprocessor register bank. Return the pseudo |
| regnum we really want to read. */ |
| |
| static int |
| mt_select_coprocessor (struct gdbarch *gdbarch, |
| struct regcache *regcache, int regno) |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| unsigned index, base; |
| gdb_byte copro[4]; |
| |
| /* Get the copro pseudo regnum. */ |
| regcache_raw_read (regcache, MT_COPRO_REGNUM, copro); |
| base = ((extract_signed_integer (&copro[0], 2, byte_order) |
| * MT_COPRO_PSEUDOREG_DIM_2) |
| + extract_signed_integer (&copro[2], 2, byte_order)); |
| |
| regno -= MT_COPRO_PSEUDOREG_ARRAY; |
| index = regno % MT_COPRO_PSEUDOREG_REGS; |
| regno /= MT_COPRO_PSEUDOREG_REGS; |
| if (base != regno) |
| { |
| /* Select the correct coprocessor register bank. Invalidate the |
| coprocessor register cache. */ |
| unsigned ix; |
| |
| store_signed_integer (&copro[0], 2, byte_order, |
| regno / MT_COPRO_PSEUDOREG_DIM_2); |
| store_signed_integer (&copro[2], 2, byte_order, |
| regno % MT_COPRO_PSEUDOREG_DIM_2); |
| regcache_raw_write (regcache, MT_COPRO_REGNUM, copro); |
| |
| /* We must flush the cache, as it is now invalid. */ |
| for (ix = MT_NUM_CPU_REGS; ix != MT_NUM_REGS; ix++) |
| regcache_invalidate (regcache, ix); |
| } |
| |
| return index; |
| } |
| |
| /* Fetch the pseudo registers: |
| |
| There are two regular pseudo-registers: |
| 1) The 'coprocessor' pseudo-register (which mirrors the |
| "real" coprocessor register sent by the target), and |
| 2) The 'MAC' pseudo-register (which represents the union |
| of the original 32 bit target MAC register and the new |
| 8-bit extended-MAC register). |
| |
| Additionally there is an array of coprocessor registers which track |
| the coprocessor registers for each coprocessor. */ |
| |
| static enum register_status |
| mt_pseudo_register_read (struct gdbarch *gdbarch, |
| struct regcache *regcache, int regno, gdb_byte *buf) |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| |
| switch (regno) |
| { |
| case MT_COPRO_REGNUM: |
| case MT_COPRO_PSEUDOREG_REGNUM: |
| return regcache_raw_read (regcache, MT_COPRO_REGNUM, buf); |
| case MT_MAC_REGNUM: |
| case MT_MAC_PSEUDOREG_REGNUM: |
| if (gdbarch_bfd_arch_info (gdbarch)->mach == bfd_mach_mrisc2 |
| || gdbarch_bfd_arch_info (gdbarch)->mach == bfd_mach_ms2) |
| { |
| enum register_status status; |
| ULONGEST oldmac = 0, ext_mac = 0; |
| ULONGEST newmac; |
| |
| status = regcache_cooked_read_unsigned (regcache, MT_MAC_REGNUM, &oldmac); |
| if (status != REG_VALID) |
| return status; |
| |
| regcache_cooked_read_unsigned (regcache, MT_EXMAC_REGNUM, &ext_mac); |
| if (status != REG_VALID) |
| return status; |
| |
| newmac = |
| (oldmac & 0xffffffff) | ((long long) (ext_mac & 0xff) << 32); |
| store_signed_integer (buf, 8, byte_order, newmac); |
| |
| return REG_VALID; |
| } |
| else |
| return regcache_raw_read (regcache, MT_MAC_REGNUM, buf); |
| break; |
| default: |
| { |
| unsigned index = mt_select_coprocessor (gdbarch, regcache, regno); |
| |
| if (index == MT_COPRO_PSEUDOREG_MAC_REGNUM) |
| return mt_pseudo_register_read (gdbarch, regcache, |
| MT_MAC_PSEUDOREG_REGNUM, buf); |
| else if (index < MT_NUM_REGS - MT_CPR0_REGNUM) |
| return regcache_raw_read (regcache, index + MT_CPR0_REGNUM, buf); |
| else |
| /* ??? */ |
| return REG_VALID; |
| } |
| break; |
| } |
| } |
| |
| /* Write the pseudo registers: |
| |
| Mt pseudo-registers are stored directly to the target. The |
| 'coprocessor' register is special, because when it is modified, all |
| the other coprocessor regs must be flushed from the reg cache. */ |
| |
| static void |
| mt_pseudo_register_write (struct gdbarch *gdbarch, |
| struct regcache *regcache, |
| int regno, const gdb_byte *buf) |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| int i; |
| |
| switch (regno) |
| { |
| case MT_COPRO_REGNUM: |
| case MT_COPRO_PSEUDOREG_REGNUM: |
| regcache_raw_write (regcache, MT_COPRO_REGNUM, buf); |
| for (i = MT_NUM_CPU_REGS; i < MT_NUM_REGS; i++) |
| regcache_invalidate (regcache, i); |
| break; |
| case MT_MAC_REGNUM: |
| case MT_MAC_PSEUDOREG_REGNUM: |
| if (gdbarch_bfd_arch_info (gdbarch)->mach == bfd_mach_mrisc2 |
| || gdbarch_bfd_arch_info (gdbarch)->mach == bfd_mach_ms2) |
| { |
| /* The 8-byte MAC pseudo-register must be broken down into two |
| 32-byte registers. */ |
| unsigned int oldmac, ext_mac; |
| ULONGEST newmac; |
| |
| newmac = extract_unsigned_integer (buf, 8, byte_order); |
| oldmac = newmac & 0xffffffff; |
| ext_mac = (newmac >> 32) & 0xff; |
| regcache_cooked_write_unsigned (regcache, MT_MAC_REGNUM, oldmac); |
| regcache_cooked_write_unsigned (regcache, MT_EXMAC_REGNUM, ext_mac); |
| } |
| else |
| regcache_raw_write (regcache, MT_MAC_REGNUM, buf); |
| break; |
| default: |
| { |
| unsigned index = mt_select_coprocessor (gdbarch, regcache, regno); |
| |
| if (index == MT_COPRO_PSEUDOREG_MAC_REGNUM) |
| mt_pseudo_register_write (gdbarch, regcache, |
| MT_MAC_PSEUDOREG_REGNUM, buf); |
| else if (index < MT_NUM_REGS - MT_CPR0_REGNUM) |
| regcache_raw_write (regcache, index + MT_CPR0_REGNUM, buf); |
| } |
| break; |
| } |
| } |
| |
| static CORE_ADDR |
| mt_frame_align (struct gdbarch *gdbarch, CORE_ADDR sp) |
| { |
| /* Register size is 4 bytes. */ |
| return align_down (sp, 4); |
| } |
| |
| /* Implements the "info registers" command. When ``all'' is non-zero, |
| the coprocessor registers will be printed in addition to the rest |
| of the registers. */ |
| |
| static void |
| mt_registers_info (struct gdbarch *gdbarch, |
| struct ui_file *file, |
| struct frame_info *frame, int regnum, int all) |
| { |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| |
| if (regnum == -1) |
| { |
| int lim; |
| |
| lim = all ? MT_NUM_REGS : MT_NUM_CPU_REGS; |
| |
| for (regnum = 0; regnum < lim; regnum++) |
| { |
| /* Don't display the Qchannel register since it will be displayed |
| along with Ichannel. (See below.) */ |
| if (regnum == MT_QCHANNEL_REGNUM) |
| continue; |
| |
| mt_registers_info (gdbarch, file, frame, regnum, all); |
| |
| /* Display the Qchannel register immediately after Ichannel. */ |
| if (regnum == MT_ICHANNEL_REGNUM) |
| mt_registers_info (gdbarch, file, frame, MT_QCHANNEL_REGNUM, all); |
| } |
| } |
| else |
| { |
| if (regnum == MT_EXMAC_REGNUM) |
| return; |
| else if (regnum == MT_CONTEXT_REGNUM) |
| { |
| /* Special output handling for 38-bit context register. */ |
| unsigned char *buff; |
| unsigned int *bytes, i, regsize; |
| |
| regsize = register_size (gdbarch, regnum); |
| |
| buff = alloca (regsize); |
| bytes = alloca (regsize * sizeof (*bytes)); |
| |
| frame_register_read (frame, regnum, buff); |
| |
| fputs_filtered (gdbarch_register_name |
| (gdbarch, regnum), file); |
| print_spaces_filtered (15 - strlen (gdbarch_register_name |
| (gdbarch, regnum)), |
| file); |
| fputs_filtered ("0x", file); |
| |
| for (i = 0; i < regsize; i++) |
| fprintf_filtered (file, "%02x", (unsigned int) |
| extract_unsigned_integer (buff + i, 1, byte_order)); |
| fputs_filtered ("\t", file); |
| print_longest (file, 'd', 0, |
| extract_unsigned_integer (buff, regsize, byte_order)); |
| fputs_filtered ("\n", file); |
| } |
| else if (regnum == MT_COPRO_REGNUM |
| || regnum == MT_COPRO_PSEUDOREG_REGNUM) |
| { |
| /* Special output handling for the 'coprocessor' register. */ |
| gdb_byte *buf; |
| struct value_print_options opts; |
| |
| buf = alloca (register_size (gdbarch, MT_COPRO_REGNUM)); |
| frame_register_read (frame, MT_COPRO_REGNUM, buf); |
| /* And print. */ |
| regnum = MT_COPRO_PSEUDOREG_REGNUM; |
| fputs_filtered (gdbarch_register_name (gdbarch, regnum), |
| file); |
| print_spaces_filtered (15 - strlen (gdbarch_register_name |
| (gdbarch, regnum)), |
| file); |
| get_raw_print_options (&opts); |
| opts.deref_ref = 1; |
| val_print (register_type (gdbarch, regnum), buf, |
| 0, 0, file, 0, NULL, |
| &opts, current_language); |
| fputs_filtered ("\n", file); |
| } |
| else if (regnum == MT_MAC_REGNUM || regnum == MT_MAC_PSEUDOREG_REGNUM) |
| { |
| ULONGEST oldmac, ext_mac, newmac; |
| gdb_byte buf[3 * sizeof (LONGEST)]; |
| |
| /* Get the two "real" mac registers. */ |
| frame_register_read (frame, MT_MAC_REGNUM, buf); |
| oldmac = extract_unsigned_integer |
| (buf, register_size (gdbarch, MT_MAC_REGNUM), byte_order); |
| if (gdbarch_bfd_arch_info (gdbarch)->mach == bfd_mach_mrisc2 |
| || gdbarch_bfd_arch_info (gdbarch)->mach == bfd_mach_ms2) |
| { |
| frame_register_read (frame, MT_EXMAC_REGNUM, buf); |
| ext_mac = extract_unsigned_integer |
| (buf, register_size (gdbarch, MT_EXMAC_REGNUM), byte_order); |
| } |
| else |
| ext_mac = 0; |
| |
| /* Add them together. */ |
| newmac = (oldmac & 0xffffffff) + ((ext_mac & 0xff) << 32); |
| |
| /* And print. */ |
| regnum = MT_MAC_PSEUDOREG_REGNUM; |
| fputs_filtered (gdbarch_register_name (gdbarch, regnum), |
| file); |
| print_spaces_filtered (15 - strlen (gdbarch_register_name |
| (gdbarch, regnum)), |
| file); |
| fputs_filtered ("0x", file); |
| print_longest (file, 'x', 0, newmac); |
| fputs_filtered ("\t", file); |
| print_longest (file, 'u', 0, newmac); |
| fputs_filtered ("\n", file); |
| } |
| else |
| default_print_registers_info (gdbarch, file, frame, regnum, all); |
| } |
| } |
| |
| /* Set up the callee's arguments for an inferior function call. The |
| arguments are pushed on the stack or are placed in registers as |
| appropriate. It also sets up the return address (which points to |
| the call dummy breakpoint). |
| |
| Returns the updated (and aligned) stack pointer. */ |
| |
| static CORE_ADDR |
| mt_push_dummy_call (struct gdbarch *gdbarch, struct value *function, |
| struct regcache *regcache, CORE_ADDR bp_addr, |
| int nargs, struct value **args, CORE_ADDR sp, |
| int struct_return, CORE_ADDR struct_addr) |
| { |
| #define wordsize 4 |
| enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); |
| gdb_byte buf[MT_MAX_STRUCT_SIZE]; |
| int argreg = MT_1ST_ARGREG; |
| int split_param_len = 0; |
| int stack_dest = sp; |
| int slacklen; |
| int typelen; |
| int i, j; |
| |
| /* First handle however many args we can fit into MT_1ST_ARGREG thru |
| MT_LAST_ARGREG. */ |
| for (i = 0; i < nargs && argreg <= MT_LAST_ARGREG; i++) |
| { |
| const gdb_byte *val; |
| typelen = TYPE_LENGTH (value_type (args[i])); |
| switch (typelen) |
| { |
| case 1: |
| case 2: |
| case 3: |
| case 4: |
| regcache_cooked_write_unsigned (regcache, argreg++, |
| extract_unsigned_integer |
| (value_contents (args[i]), |
| wordsize, byte_order)); |
| break; |
| case 8: |
| case 12: |
| case 16: |
| val = value_contents (args[i]); |
| while (typelen > 0) |
| { |
| if (argreg <= MT_LAST_ARGREG) |
| { |
| /* This word of the argument is passed in a register. */ |
| regcache_cooked_write_unsigned (regcache, argreg++, |
| extract_unsigned_integer |
| (val, wordsize, byte_order)); |
| typelen -= wordsize; |
| val += wordsize; |
| } |
| else |
| { |
| /* Remainder of this arg must be passed on the stack |
| (deferred to do later). */ |
| split_param_len = typelen; |
| memcpy (buf, val, typelen); |
| break; /* No more args can be handled in regs. */ |
| } |
| } |
| break; |
| default: |
| /* By reverse engineering of gcc output, args bigger than |
| 16 bytes go on the stack, and their address is passed |
| in the argreg. */ |
| stack_dest -= typelen; |
| write_memory (stack_dest, value_contents (args[i]), typelen); |
| regcache_cooked_write_unsigned (regcache, argreg++, stack_dest); |
| break; |
| } |
| } |
| |
| /* Next, the rest of the arguments go onto the stack, in reverse order. */ |
| for (j = nargs - 1; j >= i; j--) |
| { |
| gdb_byte *val; |
| |
| /* Right-justify the value in an aligned-length buffer. */ |
| typelen = TYPE_LENGTH (value_type (args[j])); |
| slacklen = (wordsize - (typelen % wordsize)) % wordsize; |
| val = alloca (typelen + slacklen); |
| memcpy (val, value_contents (args[j]), typelen); |
| memset (val + typelen, 0, slacklen); |
| /* Now write this data to the stack. */ |
| stack_dest -= typelen + slacklen; |
| write_memory (stack_dest, val, typelen + slacklen); |
| } |
| |
| /* Finally, if a param needs to be split between registers and stack, |
| write the second half to the stack now. */ |
| if (split_param_len != 0) |
| { |
| stack_dest -= split_param_len; |
| write_memory (stack_dest, buf, split_param_len); |
| } |
| |
| /* Set up return address (provided to us as bp_addr). */ |
| regcache_cooked_write_unsigned (regcache, MT_RA_REGNUM, bp_addr); |
| |
| /* Store struct return address, if given. */ |
| if (struct_return && struct_addr != 0) |
| regcache_cooked_write_unsigned (regcache, MT_R11_REGNUM, struct_addr); |
| |
| /* Set aside 16 bytes for the callee to save regs 1-4. */ |
| stack_dest -= 16; |
| |
| /* Update the stack pointer. */ |
| regcache_cooked_write_unsigned (regcache, MT_SP_REGNUM, stack_dest); |
| |
| /* And that should do it. Return the new stack pointer. */ |
| return stack_dest; |
| } |
| |
| |
| /* The 'unwind_cache' data structure. */ |
| |
| struct mt_unwind_cache |
| { |
| /* The previous frame's inner most stack address. |
| Used as this frame ID's stack_addr. */ |
| CORE_ADDR prev_sp; |
| CORE_ADDR frame_base; |
| int framesize; |
| int frameless_p; |
| |
| /* Table indicating the location of each and every register. */ |
| struct trad_frame_saved_reg *saved_regs; |
| }; |
| |
| /* Initialize an unwind_cache. Build up the saved_regs table etc. for |
| the frame. */ |
| |
| static struct mt_unwind_cache * |
| mt_frame_unwind_cache (struct frame_info *this_frame, |
| void **this_prologue_cache) |
| { |
| struct gdbarch *gdbarch; |
| struct mt_unwind_cache *info; |
| CORE_ADDR next_addr, start_addr, end_addr, prologue_end_addr; |
| unsigned long instr, upper_half, delayed_store = 0; |
| int regnum, offset; |
| ULONGEST sp, fp; |
| |
| if ((*this_prologue_cache)) |
| return (*this_prologue_cache); |
| |
| gdbarch = get_frame_arch (this_frame); |
| info = FRAME_OBSTACK_ZALLOC (struct mt_unwind_cache); |
| (*this_prologue_cache) = info; |
| |
| info->prev_sp = 0; |
| info->framesize = 0; |
| info->frame_base = 0; |
| info->frameless_p = 1; |
| info->saved_regs = trad_frame_alloc_saved_regs (this_frame); |
| |
| /* Grab the frame-relative values of SP and FP, needed below. |
| The frame_saved_register function will find them on the |
| stack or in the registers as appropriate. */ |
| sp = get_frame_register_unsigned (this_frame, MT_SP_REGNUM); |
| fp = get_frame_register_unsigned (this_frame, MT_FP_REGNUM); |
| |
| start_addr = get_frame_func (this_frame); |
| |
| /* Return early if GDB couldn't find the function. */ |
| if (start_addr == 0) |
| return info; |
| |
| end_addr = get_frame_pc (this_frame); |
| prologue_end_addr = skip_prologue_using_sal (gdbarch, start_addr); |
| if (end_addr == 0) |
| for (next_addr = start_addr; next_addr < end_addr; next_addr += 4) |
| { |
| instr = get_frame_memory_unsigned (this_frame, next_addr, 4); |
| if (delayed_store) /* Previous instr was a push. */ |
| { |
| upper_half = delayed_store >> 16; |
| regnum = upper_half & 0xf; |
| offset = delayed_store & 0xffff; |
| switch (upper_half & 0xfff0) |
| { |
| case 0x43c0: /* push using frame pointer. */ |
| info->saved_regs[regnum].addr = offset; |
| break; |
| case 0x43d0: /* push using stack pointer. */ |
| info->saved_regs[regnum].addr = offset; |
| break; |
| default: /* lint */ |
| break; |
| } |
| delayed_store = 0; |
| } |
| |
| switch (instr) |
| { |
| case 0x12000000: /* NO-OP */ |
| continue; |
| case 0x12ddc000: /* copy sp into fp */ |
| info->frameless_p = 0; /* Record that the frame |
| pointer is in use. */ |
| continue; |
| default: |
| upper_half = instr >> 16; |
| if (upper_half == 0x05dd || /* subi sp, sp, imm */ |
| upper_half == 0x07dd) /* subui sp, sp, imm */ |
| { |
| /* Record the frame size. */ |
| info->framesize = instr & 0xffff; |
| continue; |
| } |
| if ((upper_half & 0xfff0) == 0x43c0 || /* frame push */ |
| (upper_half & 0xfff0) == 0x43d0) /* stack push */ |
| { |
| /* Save this instruction, but don't record the |
| pushed register as 'saved' until we see the |
| next instruction. That's because of deferred stores |
| on this target -- GDB won't be able to read the register |
| from the stack until one instruction later. */ |
| delayed_store = instr; |
| continue; |
| } |
| /* Not a prologue instruction. Is this the end of the prologue? |
| This is the most difficult decision; when to stop scanning. |
| |
| If we have no line symbol, then the best thing we can do |
| is to stop scanning when we encounter an instruction that |
| is not likely to be a part of the prologue. |
| |
| But if we do have a line symbol, then we should |
| keep scanning until we reach it (or we reach end_addr). */ |
| |
| if (prologue_end_addr && (prologue_end_addr > (next_addr + 4))) |
| continue; /* Keep scanning, recording saved_regs etc. */ |
| else |
| break; /* Quit scanning: breakpoint can be set here. */ |
| } |
| } |
| |
| /* Special handling for the "saved" address of the SP: |
| The SP is of course never saved on the stack at all, so |
| by convention what we put here is simply the previous |
| _value_ of the SP (as opposed to an address where the |
| previous value would have been pushed). This will also |
| give us the frame base address. */ |
| |
| if (info->frameless_p) |
| { |
| info->frame_base = sp + info->framesize; |
| info->prev_sp = sp + info->framesize; |
| } |
| else |
| { |
| info->frame_base = fp + info->framesize; |
| info->prev_sp = fp + info->framesize; |
| } |
| /* Save prev_sp in saved_regs as a value, not as an address. */ |
| trad_frame_set_value (info->saved_regs, MT_SP_REGNUM, info->prev_sp); |
| |
| /* Now convert frame offsets to actual addresses (not offsets). */ |
| for (regnum = 0; regnum < MT_NUM_REGS; regnum++) |
| if (trad_frame_addr_p (info->saved_regs, regnum)) |
| info->saved_regs[regnum].addr += info->frame_base - info->framesize; |
| |
| /* The call instruction moves the caller's PC in the callee's RA reg. |
| Since this is an unwind, do the reverse. Copy the location of RA |
| into PC (the address / regnum) so that a request for PC will be |
| converted into a request for the RA. */ |
| info->saved_regs[MT_PC_REGNUM] = info->saved_regs[MT_RA_REGNUM]; |
| |
| return info; |
| } |
| |
| static CORE_ADDR |
| mt_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame) |
| { |
| ULONGEST pc; |
| |
| pc = frame_unwind_register_unsigned (next_frame, MT_PC_REGNUM); |
| return pc; |
| } |
| |
| static CORE_ADDR |
| mt_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame) |
| { |
| ULONGEST sp; |
| |
| sp = frame_unwind_register_unsigned (next_frame, MT_SP_REGNUM); |
| return sp; |
| } |
| |
| /* Assuming THIS_FRAME is a dummy, return the frame ID of that dummy |
| frame. The frame ID's base needs to match the TOS value saved by |
| save_dummy_frame_tos(), and the PC match the dummy frame's breakpoint. */ |
| |
| static struct frame_id |
| mt_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame) |
| { |
| CORE_ADDR sp = get_frame_register_unsigned (this_frame, MT_SP_REGNUM); |
| return frame_id_build (sp, get_frame_pc (this_frame)); |
| } |
| |
| /* Given a GDB frame, determine the address of the calling function's |
| frame. This will be used to create a new GDB frame struct. */ |
| |
| static void |
| mt_frame_this_id (struct frame_info *this_frame, |
| void **this_prologue_cache, struct frame_id *this_id) |
| { |
| struct mt_unwind_cache *info = |
| mt_frame_unwind_cache (this_frame, this_prologue_cache); |
| |
| if (!(info == NULL || info->prev_sp == 0)) |
| (*this_id) = frame_id_build (info->prev_sp, get_frame_func (this_frame)); |
| |
| return; |
| } |
| |
| static struct value * |
| mt_frame_prev_register (struct frame_info *this_frame, |
| void **this_prologue_cache, int regnum) |
| { |
| struct mt_unwind_cache *info = |
| mt_frame_unwind_cache (this_frame, this_prologue_cache); |
| |
| return trad_frame_get_prev_register (this_frame, info->saved_regs, regnum); |
| } |
| |
| static CORE_ADDR |
| mt_frame_base_address (struct frame_info *this_frame, |
| void **this_prologue_cache) |
| { |
| struct mt_unwind_cache *info = |
| mt_frame_unwind_cache (this_frame, this_prologue_cache); |
| |
| return info->frame_base; |
| } |
| |
| /* This is a shared interface: the 'frame_unwind' object is what's |
| returned by the 'sniffer' function, and in turn specifies how to |
| get a frame's ID and prev_regs. |
| |
| This exports the 'prev_register' and 'this_id' methods. */ |
| |
| static const struct frame_unwind mt_frame_unwind = { |
| NORMAL_FRAME, |
| default_frame_unwind_stop_reason, |
| mt_frame_this_id, |
| mt_frame_prev_register, |
| NULL, |
| default_frame_sniffer |
| }; |
| |
| /* Another shared interface: the 'frame_base' object specifies how to |
| unwind a frame and secure the base addresses for frame objects |
| (locals, args). */ |
| |
| static struct frame_base mt_frame_base = { |
| &mt_frame_unwind, |
| mt_frame_base_address, |
| mt_frame_base_address, |
| mt_frame_base_address |
| }; |
| |
| static struct gdbarch * |
| mt_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches) |
| { |
| struct gdbarch *gdbarch; |
| struct gdbarch_tdep *tdep; |
| |
| /* Find a candidate among the list of pre-declared architectures. */ |
| arches = gdbarch_list_lookup_by_info (arches, &info); |
| if (arches != NULL) |
| return arches->gdbarch; |
| |
| /* None found, create a new architecture from the information |
| provided. */ |
| tdep = XCALLOC (1, struct gdbarch_tdep); |
| gdbarch = gdbarch_alloc (&info, tdep); |
| |
| set_gdbarch_float_format (gdbarch, floatformats_ieee_single); |
| set_gdbarch_double_format (gdbarch, floatformats_ieee_double); |
| set_gdbarch_long_double_format (gdbarch, floatformats_ieee_double); |
| |
| set_gdbarch_register_name (gdbarch, mt_register_name); |
| set_gdbarch_num_regs (gdbarch, MT_NUM_REGS); |
| set_gdbarch_num_pseudo_regs (gdbarch, MT_NUM_PSEUDO_REGS); |
| set_gdbarch_pc_regnum (gdbarch, MT_PC_REGNUM); |
| set_gdbarch_sp_regnum (gdbarch, MT_SP_REGNUM); |
| set_gdbarch_pseudo_register_read (gdbarch, mt_pseudo_register_read); |
| set_gdbarch_pseudo_register_write (gdbarch, mt_pseudo_register_write); |
| set_gdbarch_skip_prologue (gdbarch, mt_skip_prologue); |
| set_gdbarch_inner_than (gdbarch, core_addr_lessthan); |
| set_gdbarch_breakpoint_from_pc (gdbarch, mt_breakpoint_from_pc); |
| set_gdbarch_decr_pc_after_break (gdbarch, 0); |
| set_gdbarch_frame_args_skip (gdbarch, 0); |
| set_gdbarch_print_insn (gdbarch, print_insn_mt); |
| set_gdbarch_register_type (gdbarch, mt_register_type); |
| set_gdbarch_register_reggroup_p (gdbarch, mt_register_reggroup_p); |
| |
| set_gdbarch_return_value (gdbarch, mt_return_value); |
| set_gdbarch_sp_regnum (gdbarch, MT_SP_REGNUM); |
| |
| set_gdbarch_frame_align (gdbarch, mt_frame_align); |
| |
| set_gdbarch_print_registers_info (gdbarch, mt_registers_info); |
| |
| set_gdbarch_push_dummy_call (gdbarch, mt_push_dummy_call); |
| |
| /* Target builtin data types. */ |
| set_gdbarch_short_bit (gdbarch, 16); |
| set_gdbarch_int_bit (gdbarch, 32); |
| set_gdbarch_long_bit (gdbarch, 32); |
| set_gdbarch_long_long_bit (gdbarch, 64); |
| set_gdbarch_float_bit (gdbarch, 32); |
| set_gdbarch_double_bit (gdbarch, 64); |
| set_gdbarch_long_double_bit (gdbarch, 64); |
| set_gdbarch_ptr_bit (gdbarch, 32); |
| |
| /* Register the DWARF 2 sniffer first, and then the traditional prologue |
| based sniffer. */ |
| dwarf2_append_unwinders (gdbarch); |
| frame_unwind_append_unwinder (gdbarch, &mt_frame_unwind); |
| frame_base_set_default (gdbarch, &mt_frame_base); |
| |
| /* Register the 'unwind_pc' method. */ |
| set_gdbarch_unwind_pc (gdbarch, mt_unwind_pc); |
| set_gdbarch_unwind_sp (gdbarch, mt_unwind_sp); |
| |
| /* Methods for saving / extracting a dummy frame's ID. |
| The ID's stack address must match the SP value returned by |
| PUSH_DUMMY_CALL, and saved by generic_save_dummy_frame_tos. */ |
| set_gdbarch_dummy_id (gdbarch, mt_dummy_id); |
| |
| return gdbarch; |
| } |
| |
| /* Provide a prototype to silence -Wmissing-prototypes. */ |
| extern initialize_file_ftype _initialize_mt_tdep; |
| |
| void |
| _initialize_mt_tdep (void) |
| { |
| register_gdbarch_init (bfd_arch_mt, mt_gdbarch_init); |
| } |