#include "defs.h"
#include "frame.h"
#include "obstack.h"
#include "symtab.h"
#include "dis-asm.h"
#include "gdbcmd.h"
#include "gdbtypes.h"
#include "gdbcore.h"
#include "gdb_string.h"
#include "value.h"
#include "regcache.h"
extern int h8300hmode, h8300smode;
#undef NUM_REGS
#define NUM_REGS 11
#define UNSIGNED_SHORT(X) ((X) & 0xffff)
#define IS_PUSH(x) ((x & 0xfff0)==0x6df0)
#define IS_PUSH_FP(x) (x == 0x6df6)
#define IS_MOVE_FP(x) (x == 0x0d76 || x == 0x0ff6)
#define IS_MOV_SP_FP(x) (x == 0x0d76 || x == 0x0ff6)
#define IS_SUB2_SP(x) (x==0x1b87)
#define IS_SUB4_SP(x) (x==0x1b97)
#define IS_SUBL_SP(x) (x==0x7a37)
#define IS_MOVK_R5(x) (x==0x7905)
#define IS_SUB_R5SP(x) (x==0x1957)
static char *original_register_names[] = REGISTER_NAMES;
static char *h8300h_register_names[] =
{"er0", "er1", "er2", "er3", "er4", "er5", "er6",
"sp", "ccr", "pc", "cycles", "tick", "inst"};
char **h8300_register_names = original_register_names;
static CORE_ADDR examine_prologue ();
static void set_machine_hook (char *filename);
CORE_ADDR
h8300_skip_prologue (CORE_ADDR start_pc)
{
short int w;
int adjust = 0;
while (1)
{
w = read_memory_unsigned_integer (start_pc, 2);
if (w == 0x0100 || w == 0x0110 || w == 0x0120 || w == 0x0130)
{
w = read_memory_unsigned_integer (start_pc + 2, 2);
adjust = 2;
}
if (IS_PUSH (w))
{
start_pc += 2 + adjust;
w = read_memory_unsigned_integer (start_pc, 2);
continue;
}
adjust = 0;
break;
}
w = read_memory_unsigned_integer (start_pc, 2);
if (w == 0x0100)
{
w = read_memory_unsigned_integer (start_pc + 2, 2);
adjust += 2;
}
if (IS_MOVE_FP (w))
{
start_pc += 2 + adjust;
w = read_memory_unsigned_integer (start_pc, 2);
}
if (IS_MOVK_R5 (w))
{
start_pc += 2;
w = read_memory_unsigned_integer (start_pc, 2);
}
if (IS_SUB_R5SP (w))
{
start_pc += 2 + adjust;
w = read_memory_unsigned_integer (start_pc, 2);
}
while (IS_SUB2_SP (w) || IS_SUB4_SP (w))
{
start_pc += 2 + adjust;
w = read_memory_unsigned_integer (start_pc, 2);
}
if (IS_SUBL_SP (w))
start_pc += 6 + adjust;
return start_pc;
}
int
gdb_print_insn_h8300 (bfd_vma memaddr, disassemble_info *info)
{
if (h8300smode)
return print_insn_h8300s (memaddr, info);
else if (h8300hmode)
return print_insn_h8300h (memaddr, info);
else
return print_insn_h8300 (memaddr, info);
}
CORE_ADDR
h8300_frame_chain (struct frame_info *thisframe)
{
if (PC_IN_CALL_DUMMY (thisframe->pc, thisframe->frame, thisframe->frame))
{
thisframe->from_pc = generic_read_register_dummy (thisframe->pc,
thisframe->frame,
PC_REGNUM);
return thisframe->frame;
}
h8300_frame_find_saved_regs (thisframe, (struct frame_saved_regs *) 0);
return thisframe->fsr->regs[SP_REGNUM];
}
void
h8300_frame_find_saved_regs (struct frame_info *fi,
struct frame_saved_regs *fsr)
{
register struct frame_saved_regs *cache_fsr;
CORE_ADDR ip;
struct symtab_and_line sal;
CORE_ADDR limit;
if (!fi->fsr)
{
cache_fsr = (struct frame_saved_regs *)
frame_obstack_alloc (sizeof (struct frame_saved_regs));
memset (cache_fsr, '\0', sizeof (struct frame_saved_regs));
fi->fsr = cache_fsr;
if (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame))
{
if (fsr)
*fsr = *fi->fsr;
return;
}
ip = get_pc_function_start (fi->pc);
sal = find_pc_line (ip, 0);
limit = (sal.end && sal.end < fi->pc) ? sal.end : fi->pc;
examine_prologue (ip, limit, fi->frame, cache_fsr, fi);
}
if (fsr)
*fsr = *fi->fsr;
}
CORE_ADDR
NEXT_PROLOGUE_INSN (CORE_ADDR addr, CORE_ADDR lim, INSN_WORD *pword1)
{
char buf[2];
if (addr < lim + 8)
{
read_memory (addr, buf, 2);
*pword1 = extract_signed_integer (buf, 2);
return addr + 2;
}
return 0;
}
static CORE_ADDR
examine_prologue (register CORE_ADDR ip, register CORE_ADDR limit,
CORE_ADDR after_prolog_fp, struct frame_saved_regs *fsr,
struct frame_info *fi)
{
register CORE_ADDR next_ip;
int r;
int have_fp = 0;
INSN_WORD insn_word;
unsigned int reg_save_depth = h8300hmode ? 4 : 2;
unsigned int auto_depth = 0;
char in_frame[11];
int adjust = 0;
memset (in_frame, 1, 11);
for (r = 0; r < 8; r++)
{
fsr->regs[r] = 0;
}
if (after_prolog_fp == 0)
{
after_prolog_fp = read_register (SP_REGNUM);
}
if (ip == 0 || ip & (h8300hmode ? ~0xffffff : ~0xffff))
return 0;
next_ip = NEXT_PROLOGUE_INSN (ip, limit, &insn_word);
if (insn_word == 0x0100)
{
insn_word = read_memory_unsigned_integer (ip + 2, 2);
adjust = 2;
}
fsr->regs[6] = after_prolog_fp;
while (next_ip && IS_PUSH_FP (insn_word))
{
ip = next_ip + adjust;
in_frame[insn_word & 0x7] = reg_save_depth;
next_ip = NEXT_PROLOGUE_INSN (ip, limit, &insn_word);
reg_save_depth += 2 + adjust;
}
if (next_ip && IS_MOV_SP_FP (insn_word))
{
ip = next_ip;
next_ip = NEXT_PROLOGUE_INSN (ip, limit, &insn_word);
have_fp = 1;
}
if (next_ip && (IS_SUB2_SP (insn_word) || IS_SUB4_SP (insn_word)))
{
while (next_ip && (IS_SUB2_SP (insn_word) || IS_SUB4_SP (insn_word)))
{
auto_depth += IS_SUB2_SP (insn_word) ? 2 : 4;
ip = next_ip;
next_ip = NEXT_PROLOGUE_INSN (ip, limit, &insn_word);
}
}
else
{
if (next_ip && IS_MOVK_R5 (insn_word))
{
ip = next_ip;
next_ip = NEXT_PROLOGUE_INSN (ip, limit, &insn_word);
auto_depth += insn_word;
next_ip = NEXT_PROLOGUE_INSN (next_ip, limit, &insn_word);
auto_depth += insn_word;
}
if (next_ip && IS_SUBL_SP (insn_word))
{
ip = next_ip;
auto_depth += read_memory_unsigned_integer (ip, 4);
ip += 4;
next_ip = NEXT_PROLOGUE_INSN (ip, limit, &insn_word);
}
}
while (1)
{
adjust = 0;
if (!next_ip)
break;
if (insn_word == 0x0100)
{
ip = next_ip;
next_ip = NEXT_PROLOGUE_INSN (ip, limit, &insn_word);
adjust = 2;
}
if (IS_PUSH (insn_word))
{
ip = next_ip;
next_ip = NEXT_PROLOGUE_INSN (ip, limit, &insn_word);
fsr->regs[r] = after_prolog_fp + auto_depth;
auto_depth += 2 + adjust;
continue;
}
if (insn_word == 0x0110 || insn_word == 0x0120 || insn_word == 0x0130)
{
int count = ((insn_word >> 4) & 0xf) + 1;
int start, i;
ip = next_ip;
next_ip = NEXT_PROLOGUE_INSN (ip, limit, &insn_word);
start = insn_word & 0x7;
for (i = start; i <= start + count; i++)
{
fsr->regs[i] = after_prolog_fp + auto_depth;
auto_depth += 4;
}
}
break;
}
fi->args_pointer = after_prolog_fp;
fi->locals_pointer = after_prolog_fp;
fi->from_pc = read_memory_unsigned_integer (after_prolog_fp + BINWORD, BINWORD);
in_frame[PC_REGNUM] = 0;
if (have_fp)
fsr->regs[SP_REGNUM] = read_memory_unsigned_integer (fsr->regs[6], BINWORD);
else
fsr->regs[SP_REGNUM] = after_prolog_fp + auto_depth;
return (ip);
}
void
h8300_init_extra_frame_info (int fromleaf, struct frame_info *fi)
{
fi->fsr = 0;
fi->args_pointer = 0;
fi->locals_pointer = 0;
fi->from_pc = 0;
if (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame))
{
return;
}
}
CORE_ADDR
h8300_frame_saved_pc (struct frame_info *frame)
{
if (PC_IN_CALL_DUMMY (frame->pc, frame->frame, frame->frame))
return generic_read_register_dummy (frame->pc, frame->frame, PC_REGNUM);
else
return frame->from_pc;
}
CORE_ADDR
h8300_frame_locals_address (struct frame_info *fi)
{
if (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame))
return (CORE_ADDR) 0;
if (!fi->locals_pointer)
{
struct frame_saved_regs ignore;
get_frame_saved_regs (fi, &ignore);
}
return fi->locals_pointer;
}
CORE_ADDR
h8300_frame_args_address (struct frame_info *fi)
{
if (PC_IN_CALL_DUMMY (fi->pc, fi->frame, fi->frame))
return (CORE_ADDR) 0;
if (!fi->args_pointer)
{
struct frame_saved_regs ignore;
get_frame_saved_regs (fi, &ignore);
}
return fi->args_pointer;
}
CORE_ADDR
h8300_push_arguments (int nargs, struct value **args, CORE_ADDR sp,
unsigned char struct_return, CORE_ADDR struct_addr)
{
int stack_align, stack_alloc, stack_offset;
int wordsize;
int argreg;
int argnum;
struct type *type;
CORE_ADDR regval;
char *val;
char valbuf[4];
int len;
if (h8300hmode || h8300smode)
{
stack_align = 3;
wordsize = 4;
}
else
{
stack_align = 1;
wordsize = 2;
}
sp = sp & ~stack_align;
for (argnum = 0, stack_alloc = 0;
argnum < nargs; argnum++)
stack_alloc += ((TYPE_LENGTH (VALUE_TYPE (args[argnum])) + stack_align)
& ~stack_align);
sp -= stack_alloc;
argreg = ARG0_REGNUM;
if (struct_return)
{
write_register (argreg++, struct_addr);
}
for (argnum = 0, stack_offset = 0; argnum < nargs; argnum++)
{
type = VALUE_TYPE (args[argnum]);
len = TYPE_LENGTH (type);
memset (valbuf, 0, sizeof (valbuf));
if (len < wordsize)
{
memcpy (valbuf + (wordsize - len),
(char *) VALUE_CONTENTS (args[argnum]), len);
val = valbuf;
}
else
val = (char *) VALUE_CONTENTS (args[argnum]);
if (len > (ARGLAST_REGNUM + 1 - argreg) * REGISTER_RAW_SIZE (ARG0_REGNUM) ||
(len > wordsize && (len & stack_align) != 0))
{
write_memory (sp + stack_offset, val,
len < wordsize ? wordsize : len);
stack_offset += (len + stack_align) & ~stack_align;
}
if (len <= (ARGLAST_REGNUM + 1 - argreg) * REGISTER_RAW_SIZE (ARG0_REGNUM))
while (len > 0)
{
regval = extract_address (val, wordsize);
write_register (argreg, regval);
len -= wordsize;
val += wordsize;
argreg++;
}
}
return sp;
}
CORE_ADDR
h8300_push_return_address (CORE_ADDR pc, CORE_ADDR sp)
{
unsigned char buf[4];
int wordsize;
if (h8300hmode || h8300smode)
wordsize = 4;
else
wordsize = 2;
sp -= wordsize;
store_unsigned_integer (buf, wordsize, CALL_DUMMY_ADDRESS ());
write_memory (sp, buf, wordsize);
return sp;
}
void
h8300_pop_frame (void)
{
unsigned regnum;
struct frame_saved_regs fsr;
struct frame_info *frame = get_current_frame ();
if (PC_IN_CALL_DUMMY (frame->pc, frame->frame, frame->frame))
{
generic_pop_dummy_frame ();
}
else
{
get_frame_saved_regs (frame, &fsr);
for (regnum = 0; regnum < 8; regnum++)
{
if (fsr.regs[regnum] && regnum != SP_REGNUM)
write_register (regnum,
read_memory_integer (fsr.regs[regnum], BINWORD));
else if (fsr.regs[regnum] && regnum == SP_REGNUM)
write_register (regnum, frame->frame + 2 * BINWORD);
}
write_pc (frame->from_pc);
}
flush_cached_frames ();
}
void
h8300_extract_return_value (struct type *type, char *regbuf, char *valbuf)
{
int wordsize, len;
if (h8300smode || h8300hmode)
wordsize = 4;
else
wordsize = 2;
len = TYPE_LENGTH (type);
switch (len)
{
case 1:
case 2:
memcpy (valbuf, regbuf + REGISTER_BYTE (0) + (wordsize - len), len);
break;
case 4:
if (h8300smode || h8300hmode)
{
memcpy (valbuf, regbuf + REGISTER_BYTE (0), 4);
}
else
{
memcpy (valbuf, regbuf + REGISTER_BYTE (0), 2);
memcpy (valbuf + 2, regbuf + REGISTER_BYTE (1), 2);
}
break;
case 8:
error ("I don't know how a double is returned.");
break;
}
}
void
h8300_store_return_value (struct type *type, char *valbuf)
{
int wordsize, len, regval;
if (h8300hmode || h8300smode)
wordsize = 4;
else
wordsize = 2;
len = TYPE_LENGTH (type);
switch (len)
{
case 1:
case 2:
regval = extract_address (valbuf, len);
write_register (0, regval);
break;
case 4:
regval = extract_address (valbuf, len);
if (h8300smode || h8300hmode)
{
write_register (0, regval);
}
else
{
write_register (0, regval >> 16);
write_register (1, regval & 0xffff);
}
break;
case 8:
error ("I don't know how to return a double.");
break;
}
}
struct cmd_list_element *setmemorylist;
static void
set_register_names (void)
{
if (h8300hmode != 0)
h8300_register_names = h8300h_register_names;
else
h8300_register_names = original_register_names;
}
static void
h8300_command (char *args, int from_tty)
{
extern int h8300hmode;
h8300hmode = 0;
h8300smode = 0;
set_register_names ();
}
static void
h8300h_command (char *args, int from_tty)
{
extern int h8300hmode;
h8300hmode = 1;
h8300smode = 0;
set_register_names ();
}
static void
h8300s_command (char *args, int from_tty)
{
extern int h8300smode;
extern int h8300hmode;
h8300smode = 1;
h8300hmode = 1;
set_register_names ();
}
static void
set_machine (char *args, int from_tty)
{
printf_unfiltered ("\"set machine\" must be followed by h8300, h8300h");
printf_unfiltered ("or h8300s");
help_list (setmemorylist, "set memory ", -1, gdb_stdout);
}
static void
set_machine_hook (char *filename)
{
if (bfd_get_mach (exec_bfd) == bfd_mach_h8300s)
{
h8300smode = 1;
h8300hmode = 1;
}
else if (bfd_get_mach (exec_bfd) == bfd_mach_h8300h)
{
h8300smode = 0;
h8300hmode = 1;
}
else
{
h8300smode = 0;
h8300hmode = 0;
}
set_register_names ();
}
void
_initialize_h8300m (void)
{
add_prefix_cmd ("machine", no_class, set_machine,
"set the machine type",
&setmemorylist, "set machine ", 0,
&setlist);
add_cmd ("h8300", class_support, h8300_command,
"Set machine to be H8/300.", &setmemorylist);
add_cmd ("h8300h", class_support, h8300h_command,
"Set machine to be H8/300H.", &setmemorylist);
add_cmd ("h8300s", class_support, h8300s_command,
"Set machine to be H8/300S.", &setmemorylist);
specify_exec_file_hook (set_machine_hook);
}
void
h8300_print_register_hook (int regno)
{
if (regno == 8)
{
int C, Z, N, V;
unsigned char b[4];
unsigned char l;
read_relative_register_raw_bytes (regno, b);
l = b[REGISTER_VIRTUAL_SIZE (8) - 1];
printf_unfiltered ("\t");
printf_unfiltered ("I-%d - ", (l & 0x80) != 0);
printf_unfiltered ("H-%d - ", (l & 0x20) != 0);
N = (l & 0x8) != 0;
Z = (l & 0x4) != 0;
V = (l & 0x2) != 0;
C = (l & 0x1) != 0;
printf_unfiltered ("N-%d ", N);
printf_unfiltered ("Z-%d ", Z);
printf_unfiltered ("V-%d ", V);
printf_unfiltered ("C-%d ", C);
if ((C | Z) == 0)
printf_unfiltered ("u> ");
if ((C | Z) == 1)
printf_unfiltered ("u<= ");
if ((C == 0))
printf_unfiltered ("u>= ");
if (C == 1)
printf_unfiltered ("u< ");
if (Z == 0)
printf_unfiltered ("!= ");
if (Z == 1)
printf_unfiltered ("== ");
if ((N ^ V) == 0)
printf_unfiltered (">= ");
if ((N ^ V) == 1)
printf_unfiltered ("< ");
if ((Z | (N ^ V)) == 0)
printf_unfiltered ("> ");
if ((Z | (N ^ V)) == 1)
printf_unfiltered ("<= ");
}
}
void
_initialize_h8300_tdep (void)
{
tm_print_insn = gdb_print_insn_h8300;
}