#include "defs.h"
#include "arch-utils.h"
#include "dis-asm.h"
#include "gdbtypes.h"
#include "regcache.h"
#include "gdb_string.h"
#include "gdb_assert.h"
#include "gdbcore.h"
#include "value.h"
#include "gdbtypes.h"
#include "frame.h"
#include "frame-unwind.h"
#include "frame-base.h"
#include "trad-frame.h"
#include "symtab.h"
#include "dwarf2-frame.h"
#include "regcache.h"
#include "mn10300-tdep.h"
extern struct trad_frame_cache *mn10300_frame_unwind_cache (struct frame_info*,
void **);
static int
mn10300_type_align (struct type *type)
{
int i, align = 1;
switch (TYPE_CODE (type))
{
case TYPE_CODE_INT:
case TYPE_CODE_ENUM:
case TYPE_CODE_SET:
case TYPE_CODE_RANGE:
case TYPE_CODE_CHAR:
case TYPE_CODE_BOOL:
case TYPE_CODE_FLT:
case TYPE_CODE_PTR:
case TYPE_CODE_REF:
return TYPE_LENGTH (type);
case TYPE_CODE_COMPLEX:
return TYPE_LENGTH (type) / 2;
case TYPE_CODE_STRUCT:
case TYPE_CODE_UNION:
for (i = 0; i < TYPE_NFIELDS (type); i++)
{
int falign = mn10300_type_align (TYPE_FIELD_TYPE (type, i));
while (align < falign)
align <<= 1;
}
return align;
case TYPE_CODE_ARRAY:
return 256;
case TYPE_CODE_TYPEDEF:
return mn10300_type_align (check_typedef (type));
default:
internal_error (__FILE__, __LINE__, _("bad switch"));
}
}
static int
mn10300_use_struct_convention (int gcc_p, struct type *type)
{
if (TYPE_LENGTH (type) > 8)
return 1;
switch (TYPE_CODE (type))
{
case TYPE_CODE_STRUCT:
case TYPE_CODE_UNION:
if (TYPE_NFIELDS (type) == 1)
return mn10300_use_struct_convention (gcc_p,
TYPE_FIELD_TYPE (type, 0));
if (mn10300_type_align (type) >= 4)
return 0;
return 1;
case TYPE_CODE_ARRAY:
return 1;
case TYPE_CODE_TYPEDEF:
return mn10300_use_struct_convention (gcc_p, check_typedef (type));
default:
return 0;
}
}
static void
mn10300_store_return_value (struct type *type,
struct regcache *regcache, const void *valbuf)
{
struct gdbarch *gdbarch = get_regcache_arch (regcache);
int len = TYPE_LENGTH (type);
int reg, regsz;
if (TYPE_CODE (type) == TYPE_CODE_PTR)
reg = 4;
else
reg = 0;
regsz = register_size (gdbarch, reg);
if (len <= regsz)
regcache_raw_write_part (regcache, reg, 0, len, valbuf);
else if (len <= 2 * regsz)
{
regcache_raw_write (regcache, reg, valbuf);
gdb_assert (regsz == register_size (gdbarch, reg + 1));
regcache_raw_write_part (regcache, reg+1, 0,
len - regsz, (char *) valbuf + regsz);
}
else
internal_error (__FILE__, __LINE__,
_("Cannot store return value %d bytes long."), len);
}
static void
mn10300_extract_return_value (struct type *type,
struct regcache *regcache, void *valbuf)
{
struct gdbarch *gdbarch = get_regcache_arch (regcache);
char buf[MAX_REGISTER_SIZE];
int len = TYPE_LENGTH (type);
int reg, regsz;
if (TYPE_CODE (type) == TYPE_CODE_PTR)
reg = 4;
else
reg = 0;
regsz = register_size (gdbarch, reg);
if (len <= regsz)
{
regcache_raw_read (regcache, reg, buf);
memcpy (valbuf, buf, len);
}
else if (len <= 2 * regsz)
{
regcache_raw_read (regcache, reg, buf);
memcpy (valbuf, buf, regsz);
gdb_assert (regsz == register_size (gdbarch, reg + 1));
regcache_raw_read (regcache, reg + 1, buf);
memcpy ((char *) valbuf + regsz, buf, len - regsz);
}
else
internal_error (__FILE__, __LINE__,
_("Cannot extract return value %d bytes long."), len);
}
static char *
register_name (int reg, char **regs, long sizeof_regs)
{
if (reg < 0 || reg >= sizeof_regs / sizeof (regs[0]))
return NULL;
else
return regs[reg];
}
static const char *
mn10300_generic_register_name (int reg)
{
static char *regs[] =
{ "d0", "d1", "d2", "d3", "a0", "a1", "a2", "a3",
"sp", "pc", "mdr", "psw", "lir", "lar", "", "",
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "fp"
};
return register_name (reg, regs, sizeof regs);
}
static const char *
am33_register_name (int reg)
{
static char *regs[] =
{ "d0", "d1", "d2", "d3", "a0", "a1", "a2", "a3",
"sp", "pc", "mdr", "psw", "lir", "lar", "",
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"ssp", "msp", "usp", "mcrh", "mcrl", "mcvf", "", "", ""
};
return register_name (reg, regs, sizeof regs);
}
static struct type *
mn10300_register_type (struct gdbarch *gdbarch, int reg)
{
return builtin_type_int;
}
static CORE_ADDR
mn10300_read_pc (ptid_t ptid)
{
return read_register_pid (E_PC_REGNUM, ptid);
}
static void
mn10300_write_pc (CORE_ADDR val, ptid_t ptid)
{
return write_register_pid (E_PC_REGNUM, val, ptid);
}
const static unsigned char *
mn10300_breakpoint_from_pc (CORE_ADDR *bp_addr, int *bp_size)
{
static char breakpoint[] = {0xff};
*bp_size = 1;
return breakpoint;
}
static void
my_frame_is_in_sp (struct frame_info *fi, void **this_cache)
{
struct trad_frame_cache *cache = mn10300_frame_unwind_cache (fi, this_cache);
trad_frame_set_this_base (cache,
frame_unwind_register_unsigned (fi,
E_SP_REGNUM));
}
static void
my_frame_is_in_fp (struct frame_info *fi, void **this_cache)
{
struct trad_frame_cache *cache = mn10300_frame_unwind_cache (fi, this_cache);
trad_frame_set_this_base (cache,
frame_unwind_register_unsigned (fi,
E_A3_REGNUM));
}
static void
my_frame_is_last (struct frame_info *fi)
{
}
static int
is_my_frame_in_sp (struct frame_info *fi)
{
return 0;
}
static int
is_my_frame_in_fp (struct frame_info *fi)
{
return 0;
}
static int
is_my_frame_last (struct frame_info *fi)
{
return 0;
}
static void
set_my_stack_size (struct frame_info *fi, CORE_ADDR size)
{
}
static void
set_movm_offsets (struct frame_info *fi,
void **this_cache,
int movm_args)
{
struct trad_frame_cache *cache;
int offset = 0;
CORE_ADDR base;
if (fi == NULL || this_cache == NULL)
return;
cache = mn10300_frame_unwind_cache (fi, this_cache);
if (cache == NULL)
return;
base = trad_frame_get_this_base (cache);
if (movm_args & movm_other_bit)
{
trad_frame_set_reg_addr (cache, E_LAR_REGNUM, base + offset + 4);
trad_frame_set_reg_addr (cache, E_LIR_REGNUM, base + offset + 8);
trad_frame_set_reg_addr (cache, E_MDR_REGNUM, base + offset + 12);
trad_frame_set_reg_addr (cache, E_A0_REGNUM + 1, base + offset + 16);
trad_frame_set_reg_addr (cache, E_A0_REGNUM, base + offset + 20);
trad_frame_set_reg_addr (cache, E_D0_REGNUM + 1, base + offset + 24);
trad_frame_set_reg_addr (cache, E_D0_REGNUM, base + offset + 28);
offset += 32;
}
if (movm_args & movm_a3_bit)
{
trad_frame_set_reg_addr (cache, E_A3_REGNUM, base + offset);
offset += 4;
}
if (movm_args & movm_a2_bit)
{
trad_frame_set_reg_addr (cache, E_A2_REGNUM, base + offset);
offset += 4;
}
if (movm_args & movm_d3_bit)
{
trad_frame_set_reg_addr (cache, E_D3_REGNUM, base + offset);
offset += 4;
}
if (movm_args & movm_d2_bit)
{
trad_frame_set_reg_addr (cache, E_D2_REGNUM, base + offset);
offset += 4;
}
if (AM33_MODE)
{
if (movm_args & movm_exother_bit)
{
trad_frame_set_reg_addr (cache, E_MCVF_REGNUM, base + offset);
trad_frame_set_reg_addr (cache, E_MCRL_REGNUM, base + offset + 4);
trad_frame_set_reg_addr (cache, E_MCRH_REGNUM, base + offset + 8);
trad_frame_set_reg_addr (cache, E_MDRQ_REGNUM, base + offset + 12);
trad_frame_set_reg_addr (cache, E_E1_REGNUM, base + offset + 16);
trad_frame_set_reg_addr (cache, E_E0_REGNUM, base + offset + 20);
offset += 24;
}
if (movm_args & movm_exreg1_bit)
{
trad_frame_set_reg_addr (cache, E_E7_REGNUM, base + offset);
trad_frame_set_reg_addr (cache, E_E6_REGNUM, base + offset + 4);
trad_frame_set_reg_addr (cache, E_E5_REGNUM, base + offset + 8);
trad_frame_set_reg_addr (cache, E_E4_REGNUM, base + offset + 12);
offset += 16;
}
if (movm_args & movm_exreg0_bit)
{
trad_frame_set_reg_addr (cache, E_E3_REGNUM, base + offset);
trad_frame_set_reg_addr (cache, E_E2_REGNUM, base + offset + 4);
offset += 8;
}
}
trad_frame_set_reg_addr (cache, E_PC_REGNUM, base + offset);
trad_frame_set_reg_value (cache, E_SP_REGNUM, base + offset);
}
static CORE_ADDR
mn10300_analyze_prologue (struct frame_info *fi,
void **this_cache,
CORE_ADDR pc)
{
CORE_ADDR func_addr, func_end, addr, stop;
long stack_size;
int imm_size;
unsigned char buf[4];
int status, movm_args = 0;
char *name;
if (fi)
{
pc = (pc ? pc : get_frame_pc (fi));
my_frame_is_in_sp (fi, this_cache);
}
status = find_pc_partial_function (pc, &name, &func_addr, &func_end);
if (status == 0)
{
return pc;
}
if (strcmp (name, "start") == 0)
{
if (fi != NULL)
my_frame_is_last (fi);
return pc;
}
#if 0
status = deprecated_read_memory_nobpt (pc, buf, 2);
if (status != 0)
return pc;
if (fi && buf[0] == 0xf0 && buf[1] == 0xfc)
{
if (get_next_frame (fi) == NULL)
deprecated_update_frame_base_hack (fi, read_sp ());
return get_frame_pc (fi);
}
if (fi && get_frame_pc (fi) == func_addr)
{
if (get_next_frame (fi) == NULL)
deprecated_update_frame_base_hack (fi, read_sp ());
return get_frame_pc (fi);
}
#endif
stop = fi ? pc : func_end;
stop = stop > func_end ? func_end : stop;
addr = func_addr;
if (addr + 2 >= stop
|| (status = deprecated_read_memory_nobpt (addr, buf, 2)) != 0)
goto finish_prologue;
if (buf[0] == 0xf2 && (buf[1] & 0xf3) == 0xf0)
{
if (fi)
my_frame_is_last (fi);
goto finish_prologue;
}
if (buf[0] == 0xcf)
{
movm_args = buf[1];
addr += 2;
if (addr >= stop)
goto finish_prologue;
status = deprecated_read_memory_nobpt (addr, buf, 2);
if (status != 0)
goto finish_prologue;
}
if (buf[0] == 0x3f)
{
addr += 1;
if (fi)
{
my_frame_is_in_fp (fi, this_cache);
}
if (addr >= stop)
goto finish_prologue;
status = deprecated_read_memory_nobpt (addr, buf, 2);
if (status != 0)
goto finish_prologue;
}
imm_size = 0;
if (buf[0] == 0xf8 && buf[1] == 0xfe)
imm_size = 1;
else if (buf[0] == 0xfa && buf[1] == 0xfe)
imm_size = 2;
else if (buf[0] == 0xfc && buf[1] == 0xfe)
imm_size = 4;
if (imm_size != 0)
{
status = deprecated_read_memory_nobpt (addr + 2, buf, imm_size);
if (status != 0)
goto finish_prologue;
stack_size = extract_signed_integer (buf, imm_size);
if (fi)
set_my_stack_size (fi, stack_size);
addr += 2 + imm_size;
goto finish_prologue;
}
finish_prologue:
if (fi)
set_movm_offsets (fi, this_cache, movm_args);
return addr;
}
static CORE_ADDR
mn10300_skip_prologue (CORE_ADDR pc)
{
return mn10300_analyze_prologue (NULL, NULL, pc);
}
struct trad_frame_cache *
mn10300_frame_unwind_cache (struct frame_info *next_frame,
void **this_prologue_cache)
{
struct trad_frame_cache *cache;
CORE_ADDR pc, start, end;
if (*this_prologue_cache)
return (*this_prologue_cache);
cache = trad_frame_cache_zalloc (next_frame);
pc = gdbarch_unwind_pc (current_gdbarch, next_frame);
mn10300_analyze_prologue (next_frame, (void **) &cache, pc);
if (find_pc_partial_function (pc, NULL, &start, &end))
trad_frame_set_id (cache,
frame_id_build (trad_frame_get_this_base (cache),
start));
else
trad_frame_set_id (cache,
frame_id_build (trad_frame_get_this_base (cache),
frame_func_unwind (next_frame)));
(*this_prologue_cache) = cache;
return cache;
}
static struct frame_id
mn10300_unwind_dummy_id (struct gdbarch *gdbarch,
struct frame_info *next_frame)
{
return frame_id_build (frame_sp_unwind (next_frame),
frame_pc_unwind (next_frame));
}
static void
mn10300_frame_this_id (struct frame_info *next_frame,
void **this_prologue_cache,
struct frame_id *this_id)
{
struct trad_frame_cache *cache =
mn10300_frame_unwind_cache (next_frame, this_prologue_cache);
trad_frame_get_id (cache, this_id);
}
static void
mn10300_frame_prev_register (struct frame_info *next_frame,
void **this_prologue_cache,
int regnum, enum opt_state *optimizedp,
enum lval_type *lvalp, CORE_ADDR *addrp,
int *realnump, void *bufferp)
{
struct trad_frame_cache *cache =
mn10300_frame_unwind_cache (next_frame, this_prologue_cache);
trad_frame_get_register (cache, next_frame, regnum, optimizedp,
lvalp, addrp, realnump, bufferp);
}
static const struct frame_unwind mn10300_frame_unwind = {
NORMAL_FRAME,
mn10300_frame_this_id,
mn10300_frame_prev_register
};
static CORE_ADDR
mn10300_frame_base_address (struct frame_info *next_frame,
void **this_prologue_cache)
{
struct trad_frame_cache *cache =
mn10300_frame_unwind_cache (next_frame, this_prologue_cache);
return trad_frame_get_this_base (cache);
}
static const struct frame_unwind *
mn10300_frame_sniffer (struct frame_info *next_frame)
{
return &mn10300_frame_unwind;
}
static const struct frame_base mn10300_frame_base = {
&mn10300_frame_unwind,
mn10300_frame_base_address,
mn10300_frame_base_address,
mn10300_frame_base_address
};
static CORE_ADDR
mn10300_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
{
ULONGEST pc;
frame_unwind_unsigned_register (next_frame, E_PC_REGNUM, &pc);
return pc;
}
static CORE_ADDR
mn10300_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
{
ULONGEST sp;
frame_unwind_unsigned_register (next_frame, E_SP_REGNUM, &sp);
return sp;
}
static void
mn10300_frame_unwind_init (struct gdbarch *gdbarch)
{
frame_unwind_append_sniffer (gdbarch, dwarf2_frame_sniffer);
frame_unwind_append_sniffer (gdbarch, mn10300_frame_sniffer);
frame_base_set_default (gdbarch, &mn10300_frame_base);
set_gdbarch_unwind_dummy_id (gdbarch, mn10300_unwind_dummy_id);
set_gdbarch_unwind_pc (gdbarch, mn10300_unwind_pc);
set_gdbarch_unwind_sp (gdbarch, mn10300_unwind_sp);
}
static CORE_ADDR
mn10300_push_dummy_call (struct gdbarch *gdbarch,
struct value *target_func,
struct regcache *regcache,
CORE_ADDR bp_addr,
int nargs, struct value **args,
CORE_ADDR sp,
int struct_return,
CORE_ADDR struct_addr)
{
const int push_size = register_size (gdbarch, E_PC_REGNUM);
int regs_used;
int len, arg_len;
int stack_offset = 0;
int argnum;
char *val, valbuf[MAX_REGISTER_SIZE];
sp &= ~3;
regs_used = struct_return ? 1 : 0;
for (len = 0, argnum = 0; argnum < nargs; argnum++)
{
arg_len = (TYPE_LENGTH (value_type (args[argnum])) + 3) & ~3;
while (regs_used < 2 && arg_len > 0)
{
regs_used++;
arg_len -= push_size;
}
len += arg_len;
}
sp -= len;
if (struct_return)
{
regs_used = 1;
write_register (E_D0_REGNUM, struct_addr);
}
else
regs_used = 0;
for (argnum = 0; argnum < nargs; argnum++)
{
if (TYPE_CODE (value_type (*args)) == TYPE_CODE_STRUCT
&& TYPE_LENGTH (value_type (*args)) > 8)
{
arg_len = push_size;
store_unsigned_integer (valbuf, push_size,
VALUE_ADDRESS (*args));
val = &valbuf[0];
}
else
{
arg_len = TYPE_LENGTH (value_type (*args));
val = (char *) value_contents (*args);
}
while (regs_used < 2 && arg_len > 0)
{
write_register (regs_used,
extract_unsigned_integer (val, push_size));
val += push_size;
arg_len -= push_size;
regs_used++;
}
while (arg_len > 0)
{
write_memory (sp + stack_offset, val, push_size);
arg_len -= push_size;
val += push_size;
stack_offset += push_size;
}
args++;
}
sp -= 8;
sp -= 4;
write_memory_unsigned_integer (sp, push_size, bp_addr);
regcache_cooked_write_unsigned (regcache, E_SP_REGNUM, sp);
return sp;
}
static struct gdbarch *
mn10300_gdbarch_init (struct gdbarch_info info,
struct gdbarch_list *arches)
{
struct gdbarch *gdbarch;
struct gdbarch_tdep *tdep;
arches = gdbarch_list_lookup_by_info (arches, &info);
if (arches != NULL)
return arches->gdbarch;
tdep = xmalloc (sizeof (struct gdbarch_tdep));
gdbarch = gdbarch_alloc (&info, tdep);
switch (info.bfd_arch_info->mach)
{
case 0:
case bfd_mach_mn10300:
set_gdbarch_register_name (gdbarch, mn10300_generic_register_name);
tdep->am33_mode = 0;
break;
case bfd_mach_am33:
set_gdbarch_register_name (gdbarch, am33_register_name);
tdep->am33_mode = 1;
break;
default:
internal_error (__FILE__, __LINE__,
_("mn10300_gdbarch_init: Unknown mn10300 variant"));
break;
}
set_gdbarch_num_regs (gdbarch, E_NUM_REGS);
set_gdbarch_register_type (gdbarch, mn10300_register_type);
set_gdbarch_skip_prologue (gdbarch, mn10300_skip_prologue);
set_gdbarch_read_pc (gdbarch, mn10300_read_pc);
set_gdbarch_write_pc (gdbarch, mn10300_write_pc);
set_gdbarch_pc_regnum (gdbarch, E_PC_REGNUM);
set_gdbarch_sp_regnum (gdbarch, E_SP_REGNUM);
set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
set_gdbarch_breakpoint_from_pc (gdbarch, mn10300_breakpoint_from_pc);
set_gdbarch_print_insn (gdbarch, print_insn_mn10300);
set_gdbarch_deprecated_use_struct_convention (gdbarch,
mn10300_use_struct_convention);
set_gdbarch_store_return_value (gdbarch, mn10300_store_return_value);
set_gdbarch_extract_return_value (gdbarch, mn10300_extract_return_value);
set_gdbarch_push_dummy_call (gdbarch, mn10300_push_dummy_call);
mn10300_frame_unwind_init (gdbarch);
return gdbarch;
}
static void
mn10300_dump_tdep (struct gdbarch *current_gdbarch, struct ui_file *file)
{
struct gdbarch_tdep *tdep = gdbarch_tdep (current_gdbarch);
fprintf_unfiltered (file, "mn10300_dump_tdep: am33_mode = %d\n",
tdep->am33_mode);
}
void
_initialize_mn10300_tdep (void)
{
gdbarch_register (bfd_arch_mn10300, mn10300_gdbarch_init, mn10300_dump_tdep);
}