diff options
author | Steve Chamberlain <sac@cygnus> | 1993-04-27 01:17:32 +0000 |
---|---|---|
committer | Steve Chamberlain <sac@cygnus> | 1993-04-27 01:17:32 +0000 |
commit | 9faacb925f0b526a5c79c886c711f2826e1ecbb7 (patch) | |
tree | 06caa461dc95129a7b0918846d05c7bfbba0278b /gdb/sh-tdep.c | |
parent | ed173a7696deb9417b59db73e105bfae8a17ab7a (diff) | |
download | gdb-9faacb925f0b526a5c79c886c711f2826e1ecbb7.zip gdb-9faacb925f0b526a5c79c886c711f2826e1ecbb7.tar.gz gdb-9faacb925f0b526a5c79c886c711f2826e1ecbb7.tar.bz2 |
Support for Hitachi SH
Diffstat (limited to 'gdb/sh-tdep.c')
-rw-r--r-- | gdb/sh-tdep.c | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/gdb/sh-tdep.c b/gdb/sh-tdep.c new file mode 100644 index 0000000..74e746d --- /dev/null +++ b/gdb/sh-tdep.c @@ -0,0 +1,242 @@ +/* Target-machine dependent code for Hitachi Super-H, for GDB. + Copyright (C) 1993 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +/* + Contributed by Steve Chamberlain + sac@cygnus.com + */ + +#include "defs.h" +#include "frame.h" +#include "obstack.h" +#include "symtab.h" +#include "gdbtypes.h" +#include "gdbcmd.h" +#include "value.h" +#include "dis-asm.h" +#include "../opcodes/sh-opc.h" + + +/* Prologue looks like + [mov.l <regs>,@-r15]... + [sts.l pr,@-r15] + [mov.l r14,@-r15] + [mov r15,r14] +*/ + +#define IS_STS(x) ((x) == 0x4f22) +#define IS_PUSH(x) (((x) & 0xff0f) == 0x2f06) +#define GET_PUSHED_REG(x) (((x) >> 4) & 0xf) +#define IS_MOV_SP_FP(x) ((x) == 0x6ef3) +#define IS_ADD_SP(x) (((x) & 0xff00) == 0x7f00) + + +/* Skip any prologue before the guts of a function */ + +CORE_ADDR +sh_skip_prologue (start_pc) + CORE_ADDR start_pc; + +{ + int w; + + w = read_memory_integer (start_pc, 2); + while (IS_STS (w) + || IS_PUSH (w) + || IS_MOV_SP_FP (w)) + { + start_pc += 2; + w = read_memory_integer (start_pc, 2); + } + + return start_pc; +} + +/* Disassemble an instruction */ + +int +print_insn (memaddr, stream) + CORE_ADDR memaddr; + FILE *stream; +{ + disassemble_info info; + GDB_INIT_DISASSEMBLE_INFO (info, stream); + return print_insn_sh (memaddr, &info); +} + +/* 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. + + For us, the frame address is its stack pointer value, so we look up + the function prologue to determine the caller's sp value, and return it. */ + +FRAME_ADDR +sh_frame_chain (thisframe) + FRAME thisframe; +{ + if (!inside_entry_file (thisframe->pc)) + return (read_memory_integer (FRAME_FP (thisframe), 4)); + else + return 0; +} + +/* Put here the code to store, into a struct frame_saved_regs, + 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. */ + + +void +frame_find_saved_regs (fi, fsr) + struct frame_info *fi; + struct frame_saved_regs *fsr; +{ + int where[16]; + int rn; + int have_fp = 0; + int depth; + int pc; + int opc; + int insn; + + opc = pc = get_pc_function_start (fi->pc); + + insn = read_memory_integer (pc, 2); + + for (rn = 0; rn < NUM_REGS; rn++) + where[rn] = -1; + + depth = 0; + + /* Loop around examining the prologue insns, but give up + after 15 of them, since we're getting silly then */ + while (pc < opc + 15 * 2) + { + /* See where the registers will be saved to */ + if (IS_PUSH (insn)) + { + pc += 2; + rn = GET_PUSHED_REG (insn); + where[rn] = depth; + insn = read_memory_integer (pc, 2); + depth += 4; + } + else if (IS_STS (insn)) + { + pc += 2; + where[PR_REGNUM] = depth; + insn = read_memory_integer (pc, 2); + depth += 4; + } + else if (IS_ADD_SP (insn)) + { + pc += 2; + depth += -((char) (insn & 0xff)); + insn = read_memory_integer (pc, 2); + } + else break; + } + + /* Now we know how deep things are, we can work out their addresses */ + + for (rn = 0; rn < NUM_REGS; rn++) + { + if (where[rn] >= 0) + { + if (rn == FP_REGNUM) + have_fp = 1; + + fsr->regs[rn] = fi->frame - where[rn] + depth - 4; + } + else + { + fsr->regs[rn] = 0; + } + } + + if (have_fp) + { + + fsr->regs[SP_REGNUM] = read_memory_integer (fsr->regs[FP_REGNUM], 4); + } + else + { + fsr->regs[SP_REGNUM] = fi->frame - 4; + } + + /* Remember the address of the frame pointer */ + + /* Work out the return pc - either from the saved pr or the pr + value */ + + if (fsr->regs[PR_REGNUM]) + { + fi->return_pc = read_memory_integer (fsr->regs[PR_REGNUM], 4) + 4; + } + else + { + fi->return_pc = read_register (PR_REGNUM) + 4; + } +} + +/* initialize the extra info saved in a FRAME */ + +void +init_extra_frame_info (fromleaf, fi) + int fromleaf; + struct frame_info *fi; +{ + struct frame_saved_regs dummy; + frame_find_saved_regs (fi, &dummy); +} + + +/* Discard from the stack the innermost frame, + restoring all saved registers. */ + +void +pop_frame () +{ + register FRAME frame = get_current_frame (); + register CORE_ADDR fp; + register int regnum; + struct frame_saved_regs fsr; + struct frame_info *fi; + + fi = get_frame_info (frame); + fp = fi->frame; + get_frame_saved_regs (fi, &fsr); + + /* Copy regs from where they were saved in the frame */ + for (regnum = 0; regnum < NUM_REGS; regnum++) + { + if (fsr.regs[regnum]) + { + write_register (regnum, read_memory_integer (fsr.regs[regnum], 4)); + } + } + + write_register (PC_REGNUM, fi->return_pc); + write_register (SP_REGNUM, fp + 4); + flush_cached_frames (); + set_current_frame (create_new_frame (read_register (FP_REGNUM), + read_pc ())); +} |