+
+/* Given a return value in `regbuf' with a type `valtype',
+ extract and copy its value into `valbuf'. */
+void
+mips_extract_return_value (valtype, regbuf, valbuf)
+ struct type *valtype;
+ char regbuf[REGISTER_BYTES];
+ char *valbuf;
+{
+ int regnum;
+ int offset = 0;
+ int len = TYPE_LENGTH (valtype);
+
+ regnum = 2;
+ if (TYPE_CODE (valtype) == TYPE_CODE_FLT
+ && (mips_fpu == MIPS_FPU_DOUBLE
+ || (mips_fpu == MIPS_FPU_SINGLE && len <= MIPS_REGSIZE)))
+ regnum = FP0_REGNUM;
+
+ if (TARGET_BYTE_ORDER == BIG_ENDIAN)
+ { /* "un-left-justify" the value from the register */
+ if (len < REGISTER_RAW_SIZE (regnum))
+ offset = REGISTER_RAW_SIZE (regnum) - len;
+ if (len > REGISTER_RAW_SIZE (regnum) && /* odd-size structs */
+ len < REGISTER_RAW_SIZE (regnum) * 2 &&
+ (TYPE_CODE (valtype) == TYPE_CODE_STRUCT ||
+ TYPE_CODE (valtype) == TYPE_CODE_UNION))
+ offset = 2 * REGISTER_RAW_SIZE (regnum) - len;
+ }
+ memcpy (valbuf, regbuf + REGISTER_BYTE (regnum) + offset, len);
+ REGISTER_CONVERT_TO_TYPE (regnum, valtype, valbuf);
+}
+
+/* Given a return value in `regbuf' with a type `valtype',
+ write it's value into the appropriate register. */
+void
+mips_store_return_value (valtype, valbuf)
+ struct type *valtype;
+ char *valbuf;
+{
+ int regnum;
+ int offset = 0;
+ int len = TYPE_LENGTH (valtype);
+ char raw_buffer[MAX_REGISTER_RAW_SIZE];
+
+ regnum = 2;
+ if (TYPE_CODE (valtype) == TYPE_CODE_FLT
+ && (mips_fpu == MIPS_FPU_DOUBLE
+ || (mips_fpu == MIPS_FPU_SINGLE && len <= MIPS_REGSIZE)))
+ regnum = FP0_REGNUM;
+
+ if (TARGET_BYTE_ORDER == BIG_ENDIAN)
+ { /* "left-justify" the value in the register */
+ if (len < REGISTER_RAW_SIZE (regnum))
+ offset = REGISTER_RAW_SIZE (regnum) - len;
+ if (len > REGISTER_RAW_SIZE (regnum) && /* odd-size structs */
+ len < REGISTER_RAW_SIZE (regnum) * 2 &&
+ (TYPE_CODE (valtype) == TYPE_CODE_STRUCT ||
+ TYPE_CODE (valtype) == TYPE_CODE_UNION))
+ offset = 2 * REGISTER_RAW_SIZE (regnum) - len;
+ }
+ memcpy(raw_buffer + offset, valbuf, len);
+ REGISTER_CONVERT_FROM_TYPE(regnum, valtype, raw_buffer);
+ write_register_bytes(REGISTER_BYTE (regnum), raw_buffer,
+ len > REGISTER_RAW_SIZE (regnum) ?
+ len : REGISTER_RAW_SIZE (regnum));
+}
+
+/* Exported procedure: Is PC in the signal trampoline code */
+
+int
+in_sigtramp (pc, ignore)
+ CORE_ADDR pc;
+ char *ignore; /* function name */
+{
+ if (sigtramp_address == 0)
+ fixup_sigtramp ();
+ return (pc >= sigtramp_address && pc < sigtramp_end);
+}
+
+/* Command to set FPU type. mips_fpu_string will have been set to the
+ user's argument. Set mips_fpu based on mips_fpu_string, and then
+ canonicalize mips_fpu_string. */
+
+/*ARGSUSED*/
+static void
+mips_set_fpu_command (args, from_tty, c)
+ char *args;
+ int from_tty;
+ struct cmd_list_element *c;
+{
+ char *err = NULL;
+
+ if (mips_fpu_string == NULL || *mips_fpu_string == '\0')
+ mips_fpu = MIPS_FPU_DOUBLE;
+ else if (strcasecmp (mips_fpu_string, "double") == 0
+ || strcasecmp (mips_fpu_string, "on") == 0
+ || strcasecmp (mips_fpu_string, "1") == 0
+ || strcasecmp (mips_fpu_string, "yes") == 0)
+ mips_fpu = MIPS_FPU_DOUBLE;
+ else if (strcasecmp (mips_fpu_string, "none") == 0
+ || strcasecmp (mips_fpu_string, "off") == 0
+ || strcasecmp (mips_fpu_string, "0") == 0
+ || strcasecmp (mips_fpu_string, "no") == 0)
+ mips_fpu = MIPS_FPU_NONE;
+ else if (strcasecmp (mips_fpu_string, "single") == 0)
+ mips_fpu = MIPS_FPU_SINGLE;
+ else
+ err = strsave (mips_fpu_string);
+
+ if (mips_fpu_string != NULL)
+ free (mips_fpu_string);
+
+ switch (mips_fpu)
+ {
+ case MIPS_FPU_DOUBLE:
+ mips_fpu_string = strsave ("double");
+ break;
+ case MIPS_FPU_SINGLE:
+ mips_fpu_string = strsave ("single");
+ break;
+ case MIPS_FPU_NONE:
+ mips_fpu_string = strsave ("none");
+ break;
+ }
+
+ if (err != NULL)
+ {
+ struct cleanup *cleanups = make_cleanup (free, err);
+ error ("Unknown FPU type `%s'. Use `double', `none', or `single'.",
+ err);
+ do_cleanups (cleanups);
+ }
+}
+
+static void
+mips_show_fpu_command (args, from_tty, c)
+ char *args;
+ int from_tty;
+ struct cmd_list_element *c;
+{
+}
+
+/* Command to set the processor type. */
+
+void
+mips_set_processor_type_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+ int i;
+
+ if (tmp_mips_processor_type == NULL || *tmp_mips_processor_type == '\0')
+ {
+ printf_unfiltered ("The known MIPS processor types are as follows:\n\n");
+ for (i = 0; mips_processor_type_table[i].name != NULL; ++i)
+ printf_unfiltered ("%s\n", mips_processor_type_table[i].name);
+
+ /* Restore the value. */
+ tmp_mips_processor_type = strsave (mips_processor_type);
+
+ return;
+ }
+
+ if (!mips_set_processor_type (tmp_mips_processor_type))
+ {
+ error ("Unknown processor type `%s'.", tmp_mips_processor_type);
+ /* Restore its value. */
+ tmp_mips_processor_type = strsave (mips_processor_type);
+ }
+}
+
+static void
+mips_show_processor_type_command (args, from_tty)
+ char *args;
+ int from_tty;
+{
+}
+
+/* Modify the actual processor type. */
+
+int
+mips_set_processor_type (str)
+ char *str;
+{
+ int i, j;
+
+ if (str == NULL)
+ return 0;
+
+ for (i = 0; mips_processor_type_table[i].name != NULL; ++i)
+ {
+ if (strcasecmp (str, mips_processor_type_table[i].name) == 0)
+ {
+ mips_processor_type = str;
+
+ for (j = 0; j < NUM_REGS; ++j)
+ reg_names[j] = mips_processor_type_table[i].regnames[j];
+
+ return 1;
+
+ /* FIXME tweak fpu flag too */
+ }
+ }
+
+ return 0;
+}
+
+/* Attempt to identify the particular processor model by reading the
+ processor id. */
+
+char *
+mips_read_processor_type ()
+{
+ CORE_ADDR prid;
+
+ prid = read_register (PRID_REGNUM);
+
+ if ((prid & ~0xf) == 0x700)
+ return savestring ("r3041", strlen("r3041"));
+
+ return NULL;
+}
+
+/* Just like reinit_frame_cache, but with the right arguments to be
+ callable as an sfunc. */
+
+static void
+reinit_frame_cache_sfunc (args, from_tty, c)
+ char *args;
+ int from_tty;
+ struct cmd_list_element *c;
+{
+ reinit_frame_cache ();
+}
+
+static int
+gdb_print_insn_mips (memaddr, info)
+ bfd_vma memaddr;
+ disassemble_info *info;
+{
+ mips_extra_func_info_t proc_desc;
+
+ /* Search for the function containing this address. Set the low bit
+ of the address when searching, in case we were given an even address
+ that is the start of a 16-bit function. If we didn't do this,
+ the search would fail because the symbol table says the function
+ starts at an odd address, i.e. 1 byte past the given address. */
+ memaddr = ADDR_BITS_REMOVE (memaddr);
+ proc_desc = non_heuristic_proc_desc (MAKE_MIPS16_ADDR (memaddr), NULL);
+
+ /* Make an attempt to determine if this is a 16-bit function. If
+ the procedure descriptor exists and the address therein is odd,
+ it's definitely a 16-bit function. Otherwise, we have to just
+ guess that if the address passed in is odd, it's 16-bits. */
+ if (proc_desc)
+ info->mach = pc_is_mips16 (PROC_LOW_ADDR (proc_desc)) ? 16 : 0;
+ else
+ info->mach = pc_is_mips16 (memaddr) ? 16 : 0;
+
+ /* Round down the instruction address to the appropriate boundary. */
+ memaddr &= (info->mach == 16 ? ~1 : ~3);
+
+ /* Call the appropriate disassembler based on the target endian-ness. */
+ if (TARGET_BYTE_ORDER == BIG_ENDIAN)
+ return print_insn_big_mips (memaddr, info);
+ else
+ return print_insn_little_mips (memaddr, info);
+}
+
+/* This function implements the BREAKPOINT_FROM_PC macro. It uses the program
+ counter value to determine whether a 16- or 32-bit breakpoint should be
+ used. It returns a pointer to a string of bytes that encode a breakpoint
+ instruction, stores the length of the string to *lenptr, and adjusts pc
+ (if necessary) to point to the actual memory location where the
+ breakpoint should be inserted. */
+
+unsigned char *mips_breakpoint_from_pc (pcptr, lenptr)
+ CORE_ADDR *pcptr;
+ int *lenptr;
+{
+ if (TARGET_BYTE_ORDER == BIG_ENDIAN)
+ {
+ if (pc_is_mips16 (*pcptr))
+ {
+ static char mips16_big_breakpoint[] = MIPS16_BIG_BREAKPOINT;
+ *pcptr = UNMAKE_MIPS16_ADDR (*pcptr);
+ *lenptr = sizeof(mips16_big_breakpoint);
+ return mips16_big_breakpoint;
+ }
+ else
+ {
+ static char big_breakpoint[] = BIG_BREAKPOINT;
+ static char pmon_big_breakpoint[] = PMON_BIG_BREAKPOINT;
+ static char idt_big_breakpoint[] = IDT_BIG_BREAKPOINT;
+
+ *lenptr = sizeof(big_breakpoint);
+
+ if (strcmp (target_shortname, "mips") == 0)
+ return idt_big_breakpoint;
+ else if (strcmp (target_shortname, "ddb") == 0
+ || strcmp (target_shortname, "pmon") == 0
+ || strcmp (target_shortname, "lsi") == 0)
+ return pmon_big_breakpoint;
+ else
+ return big_breakpoint;
+ }
+ }
+ else
+ {
+ if (pc_is_mips16 (*pcptr))
+ {
+ static char mips16_little_breakpoint[] = MIPS16_LITTLE_BREAKPOINT;
+ *pcptr = UNMAKE_MIPS16_ADDR (*pcptr);
+ *lenptr = sizeof(mips16_little_breakpoint);
+ return mips16_little_breakpoint;
+ }
+ else
+ {
+ static char little_breakpoint[] = LITTLE_BREAKPOINT;
+ static char pmon_little_breakpoint[] = PMON_LITTLE_BREAKPOINT;
+ static char idt_little_breakpoint[] = IDT_LITTLE_BREAKPOINT;
+
+ *lenptr = sizeof(little_breakpoint);
+
+ if (strcmp (target_shortname, "mips") == 0)
+ return idt_little_breakpoint;
+ else if (strcmp (target_shortname, "ddb") == 0
+ || strcmp (target_shortname, "pmon") == 0
+ || strcmp (target_shortname, "lsi") == 0)
+ return pmon_little_breakpoint;
+ else
+ return little_breakpoint;
+ }
+ }
+}
+
+/* Test whether the PC points to the return instruction at the
+ end of a function. This implements the ABOUT_TO_RETURN macro. */
+
+int
+mips_about_to_return (pc)
+ CORE_ADDR pc;
+{
+ if (pc_is_mips16 (pc))
+ /* This mips16 case isn't necessarily reliable. Sometimes the compiler
+ generates a "jr $ra"; other times it generates code to load
+ the return address from the stack to an accessible register (such
+ as $a3), then a "jr" using that register. This second case
+ is almost impossible to distinguish from an indirect jump
+ used for switch statements, so we don't even try. */
+ return mips_fetch_instruction (pc) == 0xe820; /* jr $ra */
+ else
+ return mips_fetch_instruction (pc) == 0x3e00008; /* jr $ra */
+}
+
+
+/* If PC is in a mips16 call or return stub, return the address of the target
+ PC, which is either the callee or the caller. There are several
+ cases which must be handled:
+
+ * If the PC is in __mips16_ret_{d,s}f, this is a return stub and the
+ target PC is in $31 ($ra).
+ * If the PC is in __mips16_call_stub_{1..10}, this is a call stub
+ and the target PC is in $2.
+ * If the PC at the start of __mips16_call_stub_{s,d}f_{0..10}, i.e.
+ before the jal instruction, this is effectively a call stub
+ and the the target PC is in $2. Otherwise this is effectively
+ a return stub and the target PC is in $18.
+
+ See the source code for the stubs in gcc/config/mips/mips16.S for
+ gory details.
+
+ This function implements the SKIP_TRAMPOLINE_CODE macro.
+*/
+
+CORE_ADDR
+mips_skip_stub (pc)
+ CORE_ADDR pc;
+{
+ char *name;
+ CORE_ADDR start_addr;
+
+ /* Find the starting address and name of the function containing the PC. */
+ if (find_pc_partial_function (pc, &name, &start_addr, NULL) == 0)
+ return 0;
+
+ /* If the PC is in __mips16_ret_{d,s}f, this is a return stub and the
+ target PC is in $31 ($ra). */
+ if (strcmp (name, "__mips16_ret_sf") == 0
+ || strcmp (name, "__mips16_ret_df") == 0)
+ return read_register (RA_REGNUM);
+
+ if (strncmp (name, "__mips16_call_stub_", 19) == 0)
+ {
+ /* If the PC is in __mips16_call_stub_{1..10}, this is a call stub
+ and the target PC is in $2. */
+ if (name[19] >= '0' && name[19] <= '9')
+ return read_register (2);
+
+ /* If the PC at the start of __mips16_call_stub_{s,d}f_{0..10}, i.e.
+ before the jal instruction, this is effectively a call stub
+ and the the target PC is in $2. Otherwise this is effectively
+ a return stub and the target PC is in $18. */
+ else if (name[19] == 's' || name[19] == 'd')
+ {
+ if (pc == start_addr)
+ {
+ /* Check if the target of the stub is a compiler-generated
+ stub. Such a stub for a function bar might have a name
+ like __fn_stub_bar, and might look like this:
+ mfc1 $4,$f13
+ mfc1 $5,$f12
+ mfc1 $6,$f15
+ mfc1 $7,$f14
+ la $1,bar (becomes a lui/addiu pair)
+ jr $1
+ So scan down to the lui/addi and extract the target
+ address from those two instructions. */
+
+ CORE_ADDR target_pc = read_register (2);
+ t_inst inst;
+ int i;
+
+ /* See if the name of the target function is __fn_stub_*. */
+ if (find_pc_partial_function (target_pc, &name, NULL, NULL) == 0)
+ return target_pc;
+ if (strncmp (name, "__fn_stub_", 10) != 0
+ && strcmp (name, "etext") != 0
+ && strcmp (name, "_etext") != 0)
+ return target_pc;
+
+ /* Scan through this _fn_stub_ code for the lui/addiu pair.
+ The limit on the search is arbitrarily set to 20
+ instructions. FIXME. */
+ for (i = 0, pc = 0; i < 20; i++, target_pc += MIPS_INSTLEN)
+ {
+ inst = mips_fetch_instruction (target_pc);
+ if ((inst & 0xffff0000) == 0x3c010000) /* lui $at */
+ pc = (inst << 16) & 0xffff0000; /* high word */
+ else if ((inst & 0xffff0000) == 0x24210000) /* addiu $at */
+ return pc | (inst & 0xffff); /* low word */
+ }
+
+ /* Couldn't find the lui/addui pair, so return stub address. */
+ return target_pc;
+ }
+ else
+ /* This is the 'return' part of a call stub. The return
+ address is in $r18. */
+ return read_register (18);
+ }
+ }
+ return 0; /* not a stub */
+}
+
+
+/* Return non-zero if the PC is inside a call thunk (aka stub or trampoline).
+ This implements the IN_SOLIB_CALL_TRAMPOLINE macro. */
+
+int
+mips_in_call_stub (pc, name)
+ CORE_ADDR pc;
+ char *name;
+{
+ CORE_ADDR start_addr;
+
+ /* Find the starting address of the function containing the PC. If the
+ caller didn't give us a name, look it up at the same time. */
+ if (find_pc_partial_function (pc, name ? NULL : &name, &start_addr, NULL) == 0)
+ return 0;
+
+ if (strncmp (name, "__mips16_call_stub_", 19) == 0)
+ {
+ /* If the PC is in __mips16_call_stub_{1..10}, this is a call stub. */
+ if (name[19] >= '0' && name[19] <= '9')
+ return 1;
+ /* If the PC at the start of __mips16_call_stub_{s,d}f_{0..10}, i.e.
+ before the jal instruction, this is effectively a call stub. */
+ else if (name[19] == 's' || name[19] == 'd')
+ return pc == start_addr;
+ }
+
+ return 0; /* not a stub */
+}
+
+
+/* Return non-zero if the PC is inside a return thunk (aka stub or trampoline).
+ This implements the IN_SOLIB_RETURN_TRAMPOLINE macro. */
+
+int
+mips_in_return_stub (pc, name)
+ CORE_ADDR pc;
+ char *name;
+{
+ CORE_ADDR start_addr;
+
+ /* Find the starting address of the function containing the PC. */
+ if (find_pc_partial_function (pc, NULL, &start_addr, NULL) == 0)
+ return 0;
+
+ /* If the PC is in __mips16_ret_{d,s}f, this is a return stub. */
+ if (strcmp (name, "__mips16_ret_sf") == 0
+ || strcmp (name, "__mips16_ret_df") == 0)
+ return 1;
+
+ /* If the PC is in __mips16_call_stub_{s,d}f_{0..10} but not at the start,
+ i.e. after the jal instruction, this is effectively a return stub. */
+ if (strncmp (name, "__mips16_call_stub_", 19) == 0
+ && (name[19] == 's' || name[19] == 'd')
+ && pc != start_addr)
+ return 1;
+
+ return 0; /* not a stub */
+}
+
+
+/* Return non-zero if the PC is in a library helper function that should
+ be ignored. This implements the IGNORE_HELPER_CALL macro. */
+
+int
+mips_ignore_helper (pc)
+ CORE_ADDR pc;
+{
+ char *name;
+
+ /* Find the starting address and name of the function containing the PC. */
+ if (find_pc_partial_function (pc, &name, NULL, NULL) == 0)
+ return 0;
+
+ /* If the PC is in __mips16_ret_{d,s}f, this is a library helper function
+ that we want to ignore. */
+ return (strcmp (name, "__mips16_ret_sf") == 0
+ || strcmp (name, "__mips16_ret_df") == 0);