diff options
author | Stan Shebs <shebs@codesourcery.com> | 1996-04-05 04:14:11 +0000 |
---|---|---|
committer | Stan Shebs <shebs@codesourcery.com> | 1996-04-05 04:14:11 +0000 |
commit | 72c9954b87f955fa8a62ff70a29c2f8a3aa7ece4 (patch) | |
tree | d432ddbffa3050db7d3094a23241ab93d79b8640 /gdb/arm-tdep.c | |
parent | 67c1413d7f56cc04f7dba8b64cac364cbfa11e42 (diff) | |
download | gdb-72c9954b87f955fa8a62ff70a29c2f8a3aa7ece4.zip gdb-72c9954b87f955fa8a62ff70a29c2f8a3aa7ece4.tar.gz gdb-72c9954b87f955fa8a62ff70a29c2f8a3aa7ece4.tar.bz2 |
* arm-xdep.c: Move native-specific code to here from arm-tdep.c.
* arm-tdep.c (arm_apcs_32): New global.
(arm_addr_bits_remove, arm_saved_pc_after_call,
arm_push_dummy_frame, arm_pop_frame): New functions.
(arm_skip_prologue): Updated version from Richard Earnshaw.
(_initialize_arm_tdep): Add set/show "apcs32".
* config/arm/tm-arm.h (ADDR_BITS_REMOVE): Call
arm_addr_bits_remove.
(SAVED_PC_AFTER_CALL): Call arm_saved_pc_after_call.
(frame_find_saved_regs): Declare properly.
(PUSH_DUMMY_FRAME): Call arm_push_dummy_frame.
(POP_FRAME): Call arm_pop_frame, use ADDR_BITS_REMOVE instead of
explicit mask.
* config/arm/nm-arm.h: New file.
* config/arm/xm-arm.h (KERNEL_U_ADDR, FETCH_INFERIOR_REGISTERS):
Move definitions to nm-arm.h.
* config/arm/arm.mh (NAT_FILE): Define.
PR 8941
Diffstat (limited to 'gdb/arm-tdep.c')
-rw-r--r-- | gdb/arm-tdep.c | 825 |
1 files changed, 465 insertions, 360 deletions
diff --git a/gdb/arm-tdep.c b/gdb/arm-tdep.c index e45bf00..4a40e16 100644 --- a/gdb/arm-tdep.c +++ b/gdb/arm-tdep.c @@ -1,5 +1,6 @@ /* Target-dependent code for the Acorn Risc Machine, for GDB, the GNU Debugger. - Copyright 1988, 1989, 1991, 1992, 1993, 1995 Free Software Foundation, Inc. + Copyright (C) 1988, 1989, 1991, 1992, 1993, 1995, 1996 + Free Software Foundation, Inc. This file is part of GDB. @@ -15,315 +16,31 @@ 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. */ +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "defs.h" #include "frame.h" #include "inferior.h" +#include "gdbcmd.h" -#if 0 -#include "gdbcore.h" -#include <sys/param.h> -#include <sys/dir.h> -#include <signal.h> -#include <sys/ioctl.h> -#include <sys/ptrace.h> -#include <machine/reg.h> +/* Set to true if the 32-bit mode is in use. */ -#define N_TXTADDR(hdr) 0x8000 -#define N_DATADDR(hdr) (hdr.a_text + 0x8000) +int arm_apcs_32 = 1; -#include <sys/user.h> /* After a.out.h */ -#include <sys/file.h> -#include "gdb_stat.h" - -#include <errno.h> -#endif - - -#if 0 -/* Work with core dump and executable files, for GDB. - This code would be in core.c if it weren't machine-dependent. */ - -/* Structure to describe the chain of shared libraries used - by the execfile. - e.g. prog shares Xt which shares X11 which shares c. */ - -struct shared_library { - struct exec_header header; - char name[SHLIBLEN]; - CORE_ADDR text_start; /* CORE_ADDR of 1st byte of text, this file */ - long data_offset; /* offset of data section in file */ - int chan; /* file descriptor for the file */ - struct shared_library *shares; /* library this one shares */ -}; -static struct shared_library *shlib = 0; - -/* Hook for `exec_file_command' command to call. */ - -extern void (*exec_file_display_hook) (); - -static CORE_ADDR unshared_text_start; - -/* extended header from exec file (for shared library info) */ - -static struct exec_header exec_header; - -void -exec_file_command (filename, from_tty) - char *filename; - int from_tty; +CORE_ADDR +arm_addr_bits_remove (val) +CORE_ADDR val; { - int val; - - /* Eliminate all traces of old exec file. - Mark text segment as empty. */ - - if (execfile) - free (execfile); - execfile = 0; - data_start = 0; - data_end -= exec_data_start; - text_start = 0; - unshared_text_start = 0; - text_end = 0; - exec_data_start = 0; - exec_data_end = 0; - if (execchan >= 0) - close (execchan); - execchan = -1; - if (shlib) { - close_shared_library(shlib); - shlib = 0; - } - - /* Now open and digest the file the user requested, if any. */ - - if (filename) - { - filename = tilde_expand (filename); - make_cleanup (free, filename); - - execchan = openp (getenv ("PATH"), 1, filename, O_RDONLY, 0, - &execfile); - if (execchan < 0) - perror_with_name (filename); - - { - struct stat st_exec; - -#ifdef HEADER_SEEK_FD - HEADER_SEEK_FD (execchan); -#endif - - val = myread (execchan, &exec_header, sizeof exec_header); - exec_aouthdr = exec_header.a_exec; - - if (val < 0) - perror_with_name (filename); - - text_start = 0x8000; - - /* Look for shared library if needed */ - if (exec_header.a_exec.a_magic & MF_USES_SL) - shlib = open_shared_library(exec_header.a_shlibname, text_start); - - text_offset = N_TXTOFF (exec_aouthdr); - exec_data_offset = N_TXTOFF (exec_aouthdr) + exec_aouthdr.a_text; - - if (shlib) { - unshared_text_start = shared_text_end(shlib) & ~0x7fff; - stack_start = shlib->header.a_exec.a_sldatabase; - stack_end = STACK_END_ADDR; - } else - unshared_text_start = 0x8000; - text_end = unshared_text_start + exec_aouthdr.a_text; - - exec_data_start = unshared_text_start + exec_aouthdr.a_text; - exec_data_end = exec_data_start + exec_aouthdr.a_data; - - data_start = exec_data_start; - data_end += exec_data_start; - - fstat (execchan, &st_exec); - exec_mtime = st_exec.st_mtime; - } - - validate_files (); - } - else if (from_tty) - printf ("No exec file now.\n"); - - /* Tell display code (if any) about the changed file name. */ - if (exec_file_display_hook) - (*exec_file_display_hook) (filename); + return (val & (arm_apcs_32 ? 0xfffffffc : 0x03fffffc)); } -#endif - -#if 0 -/* Read from the program's memory (except for inferior processes). - This function is misnamed, since it only reads, never writes; and - since it will use the core file and/or executable file as necessary. - It should be extended to write as well as read, FIXME, for patching files. - - Return 0 if address could be read, EIO if addresss out of bounds. */ - -int -xfer_core_file (memaddr, myaddr, len) - CORE_ADDR memaddr; - char *myaddr; - int len; +CORE_ADDR +arm_saved_pc_after_call (frame) + struct frame_info *frame; { - register int i; - register int val; - int xferchan; - char **xferfile; - int fileptr; - int returnval = 0; - - while (len > 0) - { - xferfile = 0; - xferchan = 0; - - /* Determine which file the next bunch of addresses reside in, - and where in the file. Set the file's read/write pointer - to point at the proper place for the desired address - and set xferfile and xferchan for the correct file. - - If desired address is nonexistent, leave them zero. - - i is set to the number of bytes that can be handled - along with the next address. - - We put the most likely tests first for efficiency. */ - - /* Note that if there is no core file - data_start and data_end are equal. */ - if (memaddr >= data_start && memaddr < data_end) - { - i = min (len, data_end - memaddr); - fileptr = memaddr - data_start + data_offset; - xferfile = &corefile; - xferchan = corechan; - } - /* Note that if there is no core file - stack_start and stack_end define the shared library data. */ - else if (memaddr >= stack_start && memaddr < stack_end) - { - if (corechan < 0) { - struct shared_library *lib; - for (lib = shlib; lib; lib = lib->shares) - if (memaddr >= lib->header.a_exec.a_sldatabase && - memaddr < lib->header.a_exec.a_sldatabase + - lib->header.a_exec.a_data) - break; - if (lib) { - i = min (len, lib->header.a_exec.a_sldatabase + - lib->header.a_exec.a_data - memaddr); - fileptr = lib->data_offset + memaddr - - lib->header.a_exec.a_sldatabase; - xferfile = execfile; - xferchan = lib->chan; - } - } else { - i = min (len, stack_end - memaddr); - fileptr = memaddr - stack_start + stack_offset; - xferfile = &corefile; - xferchan = corechan; - } - } - else if (corechan < 0 - && memaddr >= exec_data_start && memaddr < exec_data_end) - { - i = min (len, exec_data_end - memaddr); - fileptr = memaddr - exec_data_start + exec_data_offset; - xferfile = &execfile; - xferchan = execchan; - } - else if (memaddr >= text_start && memaddr < text_end) - { - struct shared_library *lib; - for (lib = shlib; lib; lib = lib->shares) - if (memaddr >= lib->text_start && - memaddr < lib->text_start + lib->header.a_exec.a_text) - break; - if (lib) { - i = min (len, lib->header.a_exec.a_text + - lib->text_start - memaddr); - fileptr = memaddr - lib->text_start + text_offset; - xferfile = &execfile; - xferchan = lib->chan; - } else { - i = min (len, text_end - memaddr); - fileptr = memaddr - unshared_text_start + text_offset; - xferfile = &execfile; - xferchan = execchan; - } - } - else if (memaddr < text_start) - { - i = min (len, text_start - memaddr); - } - else if (memaddr >= text_end - && memaddr < (corechan >= 0? data_start : exec_data_start)) - { - i = min (len, data_start - memaddr); - } - else if (corechan >= 0 - && memaddr >= data_end && memaddr < stack_start) - { - i = min (len, stack_start - memaddr); - } - else if (corechan < 0 && memaddr >= exec_data_end) - { - i = min (len, - memaddr); - } - else if (memaddr >= stack_end && stack_end != 0) - { - i = min (len, - memaddr); - } - else - { - /* Address did not classify into one of the known ranges. - This shouldn't happen; we catch the endpoints. */ - fatal ("Internal: Bad case logic in xfer_core_file."); - } - - /* Now we know which file to use. - Set up its pointer and transfer the data. */ - if (xferfile) - { - if (*xferfile == 0) - if (xferfile == &execfile) - error ("No program file to examine."); - else - error ("No core dump file or running program to examine."); - val = lseek (xferchan, fileptr, 0); - if (val < 0) - perror_with_name (*xferfile); - val = myread (xferchan, myaddr, i); - if (val < 0) - perror_with_name (*xferfile); - } - /* If this address is for nonexistent memory, - read zeros if reading, or do nothing if writing. - Actually, we never right. */ - else - { - memset (myaddr, '\0', i); - returnval = EIO; - } - - memaddr += i; - myaddr += i; - len -= i; - } - return returnval; + return ADDR_BITS_REMOVE (read_register (LR_REGNUM)); } -#endif - + /* APCS (ARM procedure call standard) defines the following prologue: mov ip, sp @@ -337,50 +54,54 @@ xfer_core_file (memaddr, myaddr, len) */ CORE_ADDR -skip_prologue(pc) +arm_skip_prologue (pc) CORE_ADDR pc; { - CORE_ADDR skip_pc = pc; -#if 0 - union insn_fmt op; - - op.ins = read_memory_integer(skip_pc, 4); - /* look for the "mov ip,sp" */ - if (op.generic.type != TYPE_ARITHMETIC || - op.arith.opcode != OPCODE_MOV || - op.arith.dest != SPTEMP || - op.arith.operand2 != SP) return pc; - skip_pc += 4; - /* skip the "stmfd sp!,{a1,a2,a3,a4}" if its there */ - op.ins = read_memory_integer(skip_pc, 4); - if (op.generic.type == TYPE_BLOCK_BRANCH && - op.generic.subtype == SUBTYPE_BLOCK && - op.block.mask == 0xf && - op.block.base == SP && - op.block.is_load == 0 && - op.block.writeback == 1 && - op.block.increment == 0 && - op.block.before == 1) skip_pc += 4; - /* skip the "stmfd sp!,{...,fp,ip,lr,pc} */ - op.ins = read_memory_integer(skip_pc, 4); - if (op.generic.type != TYPE_BLOCK_BRANCH || - op.generic.subtype != SUBTYPE_BLOCK || - /* the mask should look like 110110xxxxxx0000 */ - (op.block.mask & 0xd800) != 0xd800 || - op.block.base != SP || - op.block.is_load != 0 || - op.block.writeback != 1 || - op.block.increment != 0 || - op.block.before != 1) return pc; + unsigned long inst; + CORE_ADDR skip_pc = pc; + + inst = read_memory_integer (skip_pc, 4); + if (inst != 0xe1a0c00d) /* mov ip, sp */ + return pc; + + skip_pc += 4; + inst = read_memory_integer (skip_pc, 4); + if ((inst & 0xfffffff0) == 0xe92d0000) /* stmfd sp!,{a1,a2,a3,a4} */ + { + skip_pc += 4; + inst = read_memory_integer (skip_pc, 4); + } + + if ((inst & 0xfffff800) != 0xe92dd800) /* stmfd sp!,{...,fp,ip,lr,pc} */ + return pc; + + skip_pc += 4; + inst = read_memory_integer (skip_pc, 4); + + /* Any insns after this point may float into the code, if it makes + for better instruction scheduling, so we skip them only if + we find them, but still consdier the function to be frame-ful */ + + /* We may have either one sfmfd instruction here, or several stfe insns, + depending on the version of floating point code we support. */ + if ((inst & 0xffbf0fff) == 0xec2d0200) /* sfmfd fn, <cnt>, [sp]! */ + { + skip_pc += 4; + inst = read_memory_integer (skip_pc, 4); + } + else + { + while ((inst & 0xffff8fff) == 0xed6d0103) /* stfe fn, [sp, #-12]! */ + { + skip_pc += 4; + inst = read_memory_integer (skip_pc, 4); + } + } + + if ((inst & 0xfffff000) == 0xe24cb000) /* sub fp, ip, #nn */ skip_pc += 4; - /* check for "sub fp,ip,#nn" */ - op.ins = read_memory_integer(skip_pc, 4); - if (op.generic.type != TYPE_ARITHMETIC || - op.arith.opcode != OPCODE_SUB || - op.arith.dest != FP || - op.arith.operand1 != SPTEMP) return pc; -#endif - return skip_pc + 4; + + return skip_pc; } void @@ -431,40 +152,88 @@ arm_frame_find_saved_regs (frame_info, saved_regs_addr) saved_regs_addr->regs[FP_REGNUM] = frame - 12; } +void +arm_push_dummy_frame () +{ + register CORE_ADDR sp = read_register (SP_REGNUM); + register int regnum; + + /* opcode for ldmdb fp,{v1-v6,fp,ip,lr,pc}^ */ + sp = push_word (sp, 0xe92dbf0); /* dummy return_data_save ins */ + /* push a pointer to the dummy instruction minus 12 */ + sp = push_word (sp, read_register (SP_REGNUM) - 16); + sp = push_word (sp, read_register (PS_REGNUM)); + sp = push_word (sp, read_register (SP_REGNUM)); + sp = push_word (sp, read_register (FP_REGNUM)); + for (regnum = 9; regnum >= 4; regnum --) + sp = push_word (sp, read_register (regnum)); + write_register (FP_REGNUM, read_register (SP_REGNUM) - 8); + write_register (SP_REGNUM, sp); +} + +void +arm_pop_frame () +{ + register CORE_ADDR fp = read_register (FP_REGNUM); + register unsigned long return_data_save = + read_memory_integer (ADDR_BITS_REMOVE (read_memory_integer (fp, 4)) - 12, + 4); + register int regnum; + + write_register (PS_REGNUM, read_memory_integer (fp - 4, 4)); + write_register (PC_REGNUM, ADDR_BITS_REMOVE (read_register (PS_REGNUM))); + write_register (SP_REGNUM, read_memory_integer (fp - 8, 4)); + write_register (FP_REGNUM, read_memory_integer (fp - 12, 4)); + fp -= 12; + for (regnum = 9; regnum >= 4; regnum--) + { + if (return_data_save & (1 << regnum)) + { + fp -= 4; + write_register (regnum, read_memory_integer (fp, 4)); + } + } + flush_cached_frames (); +} + static void -print_fpu_flags(flags) +print_fpu_flags (flags) int flags; { - if (flags & (1 << 0)) fputs("IVO ", stdout); - if (flags & (1 << 1)) fputs("DVZ ", stdout); - if (flags & (1 << 2)) fputs("OFL ", stdout); - if (flags & (1 << 3)) fputs("UFL ", stdout); - if (flags & (1 << 4)) fputs("INX ", stdout); - putchar('\n'); + if (flags & (1 << 0)) fputs ("IVO ", stdout); + if (flags & (1 << 1)) fputs ("DVZ ", stdout); + if (flags & (1 << 2)) fputs ("OFL ", stdout); + if (flags & (1 << 3)) fputs ("UFL ", stdout); + if (flags & (1 << 4)) fputs ("INX ", stdout); + putchar ('\n'); } void -arm_float_info() +arm_float_info () { - register unsigned long status = read_register(FPS_REGNUM); + register unsigned long status = read_register (FPS_REGNUM); int type; type = (status >> 24) & 127; - printf("%s FPU type %d\n", - (status & (1<<31)) ? "Hardware" : "Software", - type); - fputs("mask: ", stdout); - print_fpu_flags(status >> 16); - fputs("flags: ", stdout); - print_fpu_flags(status); + printf ("%s FPU type %d\n", + (status & (1<<31)) ? "Hardware" : "Software", + type); + fputs ("mask: ", stdout); + print_fpu_flags (status >> 16); + fputs ("flags: ", stdout); + print_fpu_flags (status); } -void -_initialize_arm_tdep () +static void +arm_othernames () { - tm_print_insn = print_insn_little_arm; -} + static int toggle; + static char *original[] = ORIGINAL_REGISTER_NAMES; + static char *extra_crispy[] = ADDITIONAL_REGISTER_NAMES; + memcpy (reg_names, toggle ? extra_crispy : original, sizeof(original)); + toggle = !toggle; +} /* FIXME: Fill in with the 'right thing', see asm template in arm-convert.s */ @@ -477,7 +246,6 @@ double *dbl; *dbl = *(double*)ptr; } - void convert_to_extended (dbl, ptr) void *ptr; @@ -486,3 +254,340 @@ double *dbl; *(double*)ptr = *dbl; } +int +arm_nullified_insn (inst) + unsigned long inst; +{ + unsigned long cond = inst & 0xf0000000; + unsigned long status_reg; + + if (cond == INST_AL || cond == INST_NV) + return 0; + + status_reg = read_register (PS_REGNUM); + + switch (cond) + { + case INST_EQ: + return ((status_reg & FLAG_Z) == 0); + case INST_NE: + return ((status_reg & FLAG_Z) != 0); + case INST_CS: + return ((status_reg & FLAG_C) == 0); + case INST_CC: + return ((status_reg & FLAG_C) != 0); + case INST_MI: + return ((status_reg & FLAG_N) == 0); + case INST_PL: + return ((status_reg & FLAG_N) != 0); + case INST_VS: + return ((status_reg & FLAG_V) == 0); + case INST_VC: + return ((status_reg & FLAG_V) != 0); + case INST_HI: + return ((status_reg & (FLAG_C | FLAG_Z)) != FLAG_C); + case INST_LS: + return (((status_reg & (FLAG_C | FLAG_Z)) ^ FLAG_C) == 0); + case INST_GE: + return (((status_reg & FLAG_N) == 0) != ((status_reg & FLAG_V) == 0)); + case INST_LT: + return (((status_reg & FLAG_N) == 0) == ((status_reg & FLAG_V) == 0)); + case INST_GT: + return (((status_reg & FLAG_Z) != 0) || + (((status_reg & FLAG_N) == 0) != ((status_reg & FLAG_V) == 0))); + case INST_LE: + return (((status_reg & FLAG_Z) == 0) && + (((status_reg & FLAG_N) == 0) == ((status_reg & FLAG_V) == 0))); + } + return 0; +} + +#define submask(x) ((1L << ((x) + 1)) - 1) +#define bit(obj,st) (((obj) & (1L << (st))) >> st) +#define bits(obj,st,fn) \ + (((obj) & submask (fn) & ~ submask ((st) - 1)) >> (st)) +#define sbits(obj,st,fn) \ + ((long) (bits(obj,st,fn) | ((long) bit(obj,fn) * ~ submask (fn - st)))) +#define BranchDest(addr,instr) \ + ((CORE_ADDR) (((long) (addr)) + 8 + (sbits (instr, 0, 23) << 2))) +#define ARM_PC_32 1 + +static unsigned long +shifted_reg_val (inst, carry, pc_val) + unsigned long inst; + int carry; + unsigned long pc_val; +{ + unsigned long res, shift; + int rm = bits (inst, 0, 3); + unsigned long shifttype = bits (inst, 5, 6); + + if (bit(inst, 4)) + { + int rs = bits (inst, 8, 11); + shift = (rs == 15 ? pc_val + 8 : read_register (rs)) & 0xFF; + } + else + shift = bits (inst, 7, 11); + + res = (rm == 15 + ? ((pc_val | (ARM_PC_32 ? 0 : read_register (PS_REGNUM))) + + (bit (inst, 4) ? 12 : 8)) + : read_register (rm)); + + switch (shifttype) + { + case 0: /* LSL */ + res = shift >= 32 ? 0 : res << shift; + break; + + case 1: /* LSR */ + res = shift >= 32 ? 0 : res >> shift; + break; + + case 2: /* ASR */ + if (shift >= 32) shift = 31; + res = ((res & 0x80000000L) + ? ~((~res) >> shift) : res >> shift); + break; + + case 3: /* ROR/RRX */ + shift &= 31; + if (shift == 0) + res = (res >> 1) | (carry ? 0x80000000L : 0); + else + res = (res >> shift) | (res << (32-shift)); + break; + } + + return res & 0xffffffff; +} + + +CORE_ADDR +arm_get_next_pc (pc) + CORE_ADDR pc; +{ + unsigned long pc_val = (unsigned long) pc; + unsigned long this_instr = read_memory_integer (pc, 4); + unsigned long status = read_register (PS_REGNUM); + CORE_ADDR nextpc = (CORE_ADDR) (pc_val + 4); /* Default case */ + + if (! arm_nullified_insn (this_instr)) + { + switch (bits(this_instr, 24, 27)) + { + case 0x0: case 0x1: /* data processing */ + case 0x2: case 0x3: + { + unsigned long operand1, operand2, result = 0; + unsigned long rn; + int c; + + if (bits(this_instr, 12, 15) != 15) + break; + + if (bits (this_instr, 22, 25) == 0 + && bits (this_instr, 4, 7) == 9) /* multiply */ + error ("Illegal update to pc in instruction"); + + /* Multiply into PC */ + c = (status & FLAG_C) ? 1 : 0; + rn = bits (this_instr, 16, 19); + operand1 = (rn == 15) ? pc_val + 8 : read_register (rn); + + if (bit (this_instr, 25)) + { + unsigned long immval = bits (this_instr, 0, 7); + unsigned long rotate = 2 * bits (this_instr, 8, 11); + operand2 = ((immval >> rotate) | (immval << (32-rotate)) + & 0xffffffff); + } + else /* operand 2 is a shifted register */ + operand2 = shifted_reg_val (this_instr, c, pc_val); + + switch (bits (this_instr, 21, 24)) + { + case 0x0: /*and*/ + result = operand1 & operand2; + break; + + case 0x1: /*eor*/ + result = operand1 ^ operand2; + break; + + case 0x2: /*sub*/ + result = operand1 - operand2; + break; + + case 0x3: /*rsb*/ + result = operand2 - operand1; + break; + + case 0x4: /*add*/ + result = operand1 + operand2; + break; + + case 0x5: /*adc*/ + result = operand1 + operand2 + c; + break; + + case 0x6: /*sbc*/ + result = operand1 - operand2 + c; + break; + + case 0x7: /*rsc*/ + result = operand2 - operand1 + c; + break; + + case 0x8: case 0x9: case 0xa: case 0xb: /* tst, teq, cmp, cmn */ + result = (unsigned long) nextpc; + break; + + case 0xc: /*orr*/ + result = operand1 | operand2; + break; + + case 0xd: /*mov*/ + /* Always step into a function. */ + result = operand2; + break; + + case 0xe: /*bic*/ + result = operand1 & ~operand2; + break; + + case 0xf: /*mvn*/ + result = ~operand2; + break; + } + nextpc = (CORE_ADDR) ADDR_BITS_REMOVE (result); + + if (nextpc == pc) + error ("Infinite loop detected"); + break; + } + + case 0x4: case 0x5: /* data transfer */ + case 0x6: case 0x7: + if (bit (this_instr, 20)) + { + /* load */ + if (bits (this_instr, 12, 15) == 15) + { + /* rd == pc */ + unsigned long rn; + unsigned long base; + + if (bit (this_instr, 22)) + error ("Illegal update to pc in instruction"); + + /* byte write to PC */ + rn = bits (this_instr, 16, 19); + base = (rn == 15) ? pc_val + 8 : read_register (rn); + if (bit (this_instr, 24)) + { + /* pre-indexed */ + int c = (status & FLAG_C) ? 1 : 0; + unsigned long offset = + (bit (this_instr, 25) + ? shifted_reg_val (this_instr, c, pc_val) + : bits (this_instr, 0, 11)); + + if (bit (this_instr, 23)) + base += offset; + else + base -= offset; + } + nextpc = (CORE_ADDR) read_memory_integer ((CORE_ADDR) base, + 4); + + nextpc = ADDR_BITS_REMOVE (nextpc); + + if (nextpc == pc) + error ("Infinite loop detected"); + } + } + break; + + case 0x8: case 0x9: /* block transfer */ + if (bit (this_instr, 20)) + { + /* LDM */ + if (bit (this_instr, 15)) + { + /* loading pc */ + int offset = 0; + + if (bit (this_instr, 23)) + { + /* up */ + unsigned long reglist = bits (this_instr, 0, 14); + unsigned long regbit; + + for (; reglist != 0; reglist &= ~regbit) + { + regbit = reglist & (-reglist); + offset += 4; + } + + if (bit (this_instr, 24)) /* pre */ + offset += 4; + } + else if (bit (this_instr, 24)) + offset = -4; + + { + unsigned long rn_val = + read_register (bits (this_instr, 16, 19)); + nextpc = + (CORE_ADDR) read_memory_integer ((CORE_ADDR) (rn_val + + offset), + 4); + } + nextpc = ADDR_BITS_REMOVE (nextpc); + if (nextpc == pc) + error ("Infinite loop detected"); + } + } + break; + + case 0xb: /* branch & link */ + case 0xa: /* branch */ + { + nextpc = BranchDest (pc, this_instr); + + nextpc = ADDR_BITS_REMOVE (nextpc); + if (nextpc == pc) + error ("Infinite loop detected"); + break; + } + + case 0xc: case 0xd: + case 0xe: /* coproc ops */ + case 0xf: /* SWI */ + break; + + default: + fprintf (stderr, "Bad bit-field extraction\n"); + return (pc); + } + } + + return nextpc; +} + +void +_initialize_arm_tdep () +{ + tm_print_insn = print_insn_little_arm; + + add_com ("othernames", class_obscure, arm_othernames, + "Switch to the other set of register names."); + + add_show_from_set (add_set_cmd ("apcs32", no_class, + var_integer, (char *)&arm_apcs_32, + "Set usage of ARM 32-bit mode.\n", &setlist), + &showlist); + +} |