diff options
Diffstat (limited to 'gdb/m88k-tdep.c')
-rw-r--r-- | gdb/m88k-tdep.c | 600 |
1 files changed, 600 insertions, 0 deletions
diff --git a/gdb/m88k-tdep.c b/gdb/m88k-tdep.c new file mode 100644 index 0000000..ee78ca1 --- /dev/null +++ b/gdb/m88k-tdep.c @@ -0,0 +1,600 @@ +/* Copyright (C) 1988, 1990 Free Software Foundation, Inc. + +This file is part of GDB. + +GDB 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 1, or (at your option) +any later version. + +GDB 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 GDB; see the file COPYING. If not, write to +the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <stdio.h> +#include "defs.h" +#include "param.h" +#include "frame.h" +#include "inferior.h" +#include "value.h" + +#ifdef USG +#include <sys/types.h> +#endif + +#include <sys/param.h> +#include <sys/dir.h> +#include <signal.h> +#include "gdbcore.h" +#include <sys/user.h> +#ifndef USER /* added to support BCS ptrace_user */ + +#define USER ptrace_user +#endif +#include <sys/ioctl.h> +#include <fcntl.h> + +#ifdef COFF_ENCAPSULATE +#include "a.out.encap.h" +#else +#include <a.out.h> +#endif + +#include <sys/file.h> +#include <sys/stat.h> + +#include "symtab.h" +#include "setjmp.h" +#include "value.h" + +int stack_error; +jmp_buf stack_jmp; + +void +tdesc_error_function (environment, continuable, message) +dc_word_t environment; +dc_boolean_t continuable; +char *message; +{ + if (stack_error) longjmp (stack_jmp, 1); + if (!continuable) + { + printf("%s\n",message); + abort(); + } +} + + +void +tdesc_read_function (environment, memory, length, buffer) +dc_word_t environment; +dc_word_t memory; +int length; +char *buffer; +{ + int ptrace_code; + errno = 0; + if (memory < 2048) +#if 0 + /* This is a no-op! It sets buffer, but doesn't do anything to + what buffer points to. What does this function do anyway? + And this is wrong for cross-debugging. */ + buffer = ptrace (3, inferior_pid, memory, 0); +#else + return; +#endif + else + read_memory (memory, buffer, length); +} + +/* Map function for tdesc */ +void +tdesc_map_function (map_env, loc, map_info_in, map_info_out) +dc_word_t map_env; +dc_word_t loc; +dc_map_info_in_t map_info_in; +dc_map_info_out_t *map_info_out; +{ +int map_flags = DC_MIO_ENTRY_POINT | DC_MIO_IMPLICIT_PROLOGUE_END; +int entry_point = get_pc_function_start(loc); +map_info_out->flags = map_flags; +map_info_out->entry_point = entry_point; +} + +dc_handle_t tdesc_handle; + +extern int debug_info; + +void +init_tdesc () +{ + tdesc_handle = dc_initiate (debug_info, tdesc_error_function, + 0,tdesc_read_function,0,0,0,0,0,tdesc_map_function,0); +} +dc_dcontext_t current_context; + +/* setup current context, called from wait_for_inferior */ + +dc_dcontext_t +init_dcontext() +{ + dc_word_t reg_info[DC_NUM_REG]; + dc_word_t reg_flags[2] = {0,-1}; + dc_word_t aux_info[DC_NUM_AUX]; + dc_word_t aux_flags[2] = {0,-1}; + dc_exactness_t loc_exact = DC_NO; + dc_word_t psr_info; + dc_boolean_t psr_ind = 0; + dc_word_t psr_flags[2] = {0,-1}; + + bcopy (®isters, reg_info, DC_NUM_REG * 4); + aux_info[DC_AUX_LOC] = read_register(SXIP_REGNUM); + aux_info[DC_AUX_SXIP] = read_register(SXIP_REGNUM); + aux_info[DC_AUX_SNIP] = read_register(SNIP_REGNUM); + aux_info[DC_AUX_SFIP] = read_register(SFIP_REGNUM); + aux_info[DC_AUX_FPSR] = read_register(FPSR_REGNUM); + aux_info[DC_AUX_FPCR] = read_register(FPCR_REGNUM); + + psr_info = read_register(PSR_REGNUM); + + return dc_make_dcontext (tdesc_handle, reg_info, reg_flags, aux_info, + aux_flags, loc_exact, psr_info, psr_ind, psr_flags); +} + + +dc_dcontext_t +get_prev_context (context) + dc_dcontext_t context; +{ + return current_context = dc_previous_dcontext (context); +} + + + + +/* Determine frame base for this file's frames. This will be either + the CFA or the old style FP_REGNUM; the symtab for the current pc's + file has the information */ + +CORE_ADDR +get_frame_base(pc) +CORE_ADDR pc; +{ + struct symtab *this_file = find_pc_symtab(pc); + int coffsem_frame_position; + + /* If this_file is null, there's a good chance the file was compiled + without -g. If that's the case, use CFA (canonical frame addr) + as the default frame pointer. */ + + if (this_file) + { + coffsem_frame_position = this_file->coffsem & 3; + if (coffsem_frame_position == 1) + return (CORE_ADDR) dc_general_register (current_context, FP_REGNUM); + else + /* default is CFA, as well as if coffsem==2 */ + return (CORE_ADDR) dc_frame_address (current_context); + } + + return (CORE_ADDR) dc_frame_address (current_context); +} + +#if TARGET_BYTE_ORDER != HOST_BYTE_ORDER +you lose +#else /* Host and target byte order the same. */ +#define SINGLE_EXP_BITS 8 +#define DOUBLE_EXP_BITS 11 +int +IEEE_isNAN(fp, len) + int *fp, len; + /* fp points to a single precision OR double precision + * floating point value; len is the number of bytes, either 4 or 8. + * Returns 1 iff fp points to a valid IEEE floating point number. + * Returns 0 if fp points to a denormalized number or a NaN + */ +{ + int exponent; + if (len == 4) + { + exponent = *fp; + exponent = exponent << 1 >> (32 - SINGLE_EXP_BITS - 1); + return ((exponent == -1) || (! exponent && *fp)); + } + else if (len == 8) + { + exponent = *(fp+1); + exponent = exponent << 1 >> (32 - DOUBLE_EXP_BITS - 1); + return ((exponent == -1) || (! exponent && *fp * *(fp+1))); + } + else return 1; +} +#endif /* Host and target byte order the same. */ + +#define FIRST_PRESERVED_REGNUM 14 +#define LAST_PRESERVED_REGNUM 25 +#define FIRST_PARM_REGNUM 2 +#define LAST_PARM_REGNUM 9 + +#define MAX_REG_PARMS (LAST_PARM_REGNUM - FIRST_PARM_REGNUM + 1) + +void +frame_find_saved_regs (fi, fsr) + struct frame_info *fi; + struct frame_saved_regs *fsr; +{ + register int regnum; + + error ("Feature not implemented for the 88k yet."); + return; + +#if 0 + for (regnum = FIRST_PARM_REGNUM; regnum <= LAST_PARM_REGNUM; regnum++) + fsr->regs[regnum] + = (unsigned) fi->frame - ((regnum - FIRST_PARM_REGNUM) * 4); + + fsr->regs[SP_REGNUM] = 0; /* SP not saved in frames */ + fsr->regs[FP_REGNUM] = fi->frame; + fsr->regs[PC_REGNUM] = fi->frame + 4; +#endif + } + + static int + pushed_size (prev_words, v) + int prev_words; + struct value *v; + { + switch (TYPE_CODE (VALUE_TYPE (v))) + { + case TYPE_CODE_VOID: /* Void type (values zero length) */ + + return 0; /* That was easy! */ + + case TYPE_CODE_PTR: /* Pointer type */ + case TYPE_CODE_ENUM: /* Enumeration type */ + case TYPE_CODE_INT: /* Integer type */ + case TYPE_CODE_REF: /* C++ Reference types */ + case TYPE_CODE_ARRAY: /* Array type, lower bound zero */ + + return 1; + + case TYPE_CODE_FLT: /* Floating type */ + + if (TYPE_LENGTH (VALUE_TYPE (v)) == 4) + return 1; + else + /* Assume that it must be a double. */ + if (prev_words & 1) /* at an odd-word boundary */ + return 3; /* round to 8-byte boundary */ + else + return 2; + + case TYPE_CODE_STRUCT: /* C struct or Pascal record */ + case TYPE_CODE_UNION: /* C union or Pascal variant part */ + + return (((TYPE_LENGTH (VALUE_TYPE (v)) + 3) / 4) * 4); + + case TYPE_CODE_FUNC: /* Function type */ + case TYPE_CODE_SET: /* Pascal sets */ + case TYPE_CODE_RANGE: /* Range (integers within bounds) */ + case TYPE_CODE_PASCAL_ARRAY: /* Array with explicit type of index */ + case TYPE_CODE_MEMBER: /* Member type */ + case TYPE_CODE_METHOD: /* Method type */ + /* Don't know how to pass these yet. */ + + case TYPE_CODE_UNDEF: /* Not used; catches errors */ + default: + abort (); + } + } + + static void + store_parm_word (address, val) + CORE_ADDR address; + int val; + { + write_memory (address, &val, 4); + } + + static int + store_parm (prev_words, left_parm_addr, v) + unsigned int prev_words; + CORE_ADDR left_parm_addr; + struct value *v; + { + CORE_ADDR start = left_parm_addr + (prev_words * 4); + int *val_addr = (int *)VALUE_CONTENTS(v); + + switch (TYPE_CODE (VALUE_TYPE (v))) + { + case TYPE_CODE_VOID: /* Void type (values zero length) */ + + return 0; + + case TYPE_CODE_PTR: /* Pointer type */ + case TYPE_CODE_ENUM: /* Enumeration type */ + case TYPE_CODE_INT: /* Integer type */ + case TYPE_CODE_ARRAY: /* Array type, lower bound zero */ + case TYPE_CODE_REF: /* C++ Reference types */ + + store_parm_word (start, *val_addr); + return 1; + + case TYPE_CODE_FLT: /* Floating type */ + + if (TYPE_LENGTH (VALUE_TYPE (v)) == 4) + { + store_parm_word (start, *val_addr); + return 1; + } + else + { + store_parm_word (start + ((prev_words & 1) * 4), val_addr[0]); + store_parm_word (start + ((prev_words & 1) * 4) + 4, val_addr[1]); + return 2 + (prev_words & 1); + } + + case TYPE_CODE_STRUCT: /* C struct or Pascal record */ + case TYPE_CODE_UNION: /* C union or Pascal variant part */ + + { + unsigned int words = (((TYPE_LENGTH (VALUE_TYPE (v)) + 3) / 4) * 4); + unsigned int word; + + for (word = 0; word < words; word++) + store_parm_word (start + (word * 4), val_addr[word]); + return words; + } + + default: + abort (); + } + } + + /* This routine sets up all of the parameter values needed to make a pseudo + call. The name "push_parameters" is a misnomer on some archs, + because (on the m88k) most parameters generally end up being passed in + registers rather than on the stack. In this routine however, we do + end up storing *all* parameter values onto the stack (even if we will + realize later that some of these stores were unnecessary). */ + +void +push_parameters (return_type, struct_conv, nargs, args) + struct type *return_type; + int struct_conv; + int nargs; + value *args; + { + int parm_num; + unsigned int p_words = 0; + CORE_ADDR left_parm_addr; + + /* Start out by creating a space for the return value (if need be). We + only need to do this if the return value is a struct or union. If we + do make a space for a struct or union return value, then we must also + arrange for the base address of that space to go into r12, which is the + standard place to pass the address of the return value area to the + callee. Note that only structs and unions are returned in this fashion. + Ints, enums, pointers, and floats are returned into r2. Doubles are + returned into the register pair {r2,r3}. Note also that the space + reserved for a struct or union return value only has to be word aligned + (not double-word) but it is double-word aligned here anyway (just in + case that becomes important someday). */ + + switch (TYPE_CODE (return_type)) + { + case TYPE_CODE_STRUCT: + case TYPE_CODE_UNION: + { + int return_bytes = ((TYPE_LENGTH (return_type) + 7) / 8) * 8; + CORE_ADDR rv_addr; + + rv_addr = read_register (SP_REGNUM) - return_bytes; + + write_register (SP_REGNUM, rv_addr); /* push space onto the stack */ + write_register (SRA_REGNUM, rv_addr);/* set return value register */ + } + } + + /* Here we make a pre-pass on the whole parameter list to figure out exactly + how many words worth of stuff we are going to pass. */ + + for (p_words = 0, parm_num = 0; parm_num < nargs; parm_num++) + p_words += pushed_size (p_words, value_arg_coerce (args[parm_num])); + + /* Now, check to see if we have to round up the number of parameter words + to get up to the next 8-bytes boundary. This may be necessary because + of the software convention to always keep the stack aligned on an 8-byte + boundary. */ + + if (p_words & 1) + p_words++; /* round to 8-byte boundary */ + + /* Now figure out the absolute address of the leftmost parameter, and update + the stack pointer to point at that address. */ + + left_parm_addr = read_register (SP_REGNUM) - (p_words * 4); + write_register (SP_REGNUM, left_parm_addr); + + /* Now we can go through all of the parameters (in left-to-right order) + and write them to their parameter stack slots. Note that we are not + really "pushing" the parameter values. The stack space for these values + was already allocated above. Now we are just filling it up. */ + + for (p_words = 0, parm_num = 0; parm_num < nargs; parm_num++) + p_words += + store_parm (p_words, left_parm_addr, value_arg_coerce (args[parm_num])); + + /* Now that we are all done storing the parameter values into the stack, we + must go back and load up the parameter registers with the values from the + corresponding stack slots. Note that in the two cases of (a) gaps in the + parameter word sequence causes by (otherwise) misaligned doubles, and (b) + slots correcponding to structs or unions, the work we do here in loading + some parameter registers may be unnecessary, but who cares? */ + + for (p_words = 0; p_words < 8; p_words++) + { + write_register (FIRST_PARM_REGNUM + p_words, + read_memory_integer (left_parm_addr + (p_words * 4), 4)); + } +} + +void +pop_frame () +{ + error ("Feature not implemented for the m88k yet."); + return; +} + + void + collect_returned_value (rval, value_type, struct_return, nargs, args) + value *rval; + struct type *value_type; + int struct_return; + int nargs; + value *args; + { + char retbuf[REGISTER_BYTES]; + + bcopy (registers, retbuf, REGISTER_BYTES); + *rval = value_being_returned (value_type, retbuf, struct_return); + return; + } + +#if 0 +/* Now handled in a machine independent way with CALL_DUMMY_LOCATION. */ + /* Stuff a breakpoint instruction onto the stack (or elsewhere if the stack + is not a good place for it). Return the address at which the instruction + got stuffed, or zero if we were unable to stuff it anywhere. */ + + CORE_ADDR + push_breakpoint () + { + static char breakpoint_insn[] = BREAKPOINT; + extern CORE_ADDR text_end; /* of inferior */ + static char readback_buffer[] = BREAKPOINT; + int i; + + /* With a little bit of luck, we can just stash the breakpoint instruction + in the word just beyond the end of normal text space. For systems on + which the hardware will not allow us to execute out of the stack segment, + we have to hope that we *are* at least allowed to effectively extend the + text segment by one word. If the actual end of user's the text segment + happens to fall right at a page boundary this trick may fail. Note that + we check for this by reading after writing, and comparing in order to + be sure that the write worked. */ + + write_memory (text_end, &breakpoint_insn, 4); + + /* Fill the readback buffer with some garbage which is certain to be + unequal to the breakpoint insn. That way we can tell if the + following read doesn't actually succeed. */ + + for (i = 0; i < sizeof (readback_buffer); i++) + readback_buffer[i] = ~ readback_buffer[i]; /* Invert the bits */ + + /* Now check that the breakpoint insn was successfully installed. */ + + read_memory (text_end, readback_buffer, sizeof (readback_buffer)); + for (i = 0; i < sizeof (readback_buffer); i++) + if (readback_buffer[i] != breakpoint_insn[i]) + return 0; /* Failed to install! */ + + return text_end; + } +#endif + +/* Like dc_psr_register but takes an extra int arg. */ +static dc_word_t +psr_register (context, dummy) + dc_dcontext_t context; + int dummy; +{ + return dc_psr_register (context); +} + +/* Same functionality as get_saved_register in findvar.c, but implemented + to use tdesc. */ +void +get_saved_register (raw_buffer, optim, addrp, frame, regnum, lvalp) + char *raw_buffer; + int *optim; + CORE_ADDR *addrp; + FRAME frame; + int regnum; + enum lval_type *lvalp; +{ + struct frame_info *fi = get_frame_info (frame); + + /* Functions to say whether a register is optimized out, and + if not, to get the value. Take as args a context and the + value of get_reg_arg. */ + int (*get_reg_state) (); + dc_word_t (*get_reg) (); + int get_reg_arg; + + /* Because tdesc doesn't tell us whether it got it from a register + or memory, always say we don't have an address for it. */ + if (addrp != NULL) + *addrp = 0; + + if (regnum < DC_NUM_REG) + { + get_reg_state = dc_general_register_state; + get_reg = dc_general_register; + get_reg_arg = regnum; + } + else + { + get_reg_state = dc_auxiliary_register_state; + get_reg = dc_auxiliary_register; + switch (regnum) + { + case SXIP_REGNUM: + get_reg_arg = DC_AUX_SXIP; + break; + case SNIP_REGNUM: + get_reg_arg = DC_AUX_SNIP; + break; + case FPSR_REGNUM: + get_reg_arg = DC_AUX_FPSR; + break; + case FPCR_REGNUM: + get_reg_arg = DC_AUX_FPCR; + break; + case PSR_REGNUM: + get_reg_state = dc_psr_register_bit_state; + get_reg = psr_register; + get_reg_arg = 0; + break; + default: + if (optim != NULL) + *optim = 1; + return; + } + } + + if ((*get_reg_state) (fi->frame_context, get_reg_arg)) + { + if (raw_buffer != NULL) + *(int *)raw_buffer = (*get_reg) (fi->frame_context, get_reg_arg); + if (optim != NULL) + *optim = 0; + return; + } + else + { + if (optim != NULL) + *optim = 1; + return; + } + + /* Well, the caller can't treat it as a register or memory... */ + if (lvalp != NULL) + *lvalp = not_lval; +} |