#import "gdb.h"
#import <mach/hppa/thread_status.h>

extern thread_t current_thread;
extern RegionManager *regionManager;
int catch_alignment_traps;

#define HPPA_GEN_REGNUM 37
#define HPPA_FP_REGNUM 61
#define HPPA_FRAME_REGNUM 42
#define HPPA_NUM_REGS 125

void
copy_registers_integer_in(regsState)
     struct hp_pa_integer_thread_state *regsState;
{
  unsigned int *r = (unsigned int *) registers;
  unsigned int *rs = (unsigned int *) regsState;
  int i;

  for (i = 1; i < HPPA_GEN_REGNUM;  i++)
    r[i] = rs[i-1];	/* reg 0 is not in the regsState */
  return;
}

void
copy_registers_frame_in(regsState)
     struct hp_pa_frame_thread_state *regsState;
{
  unsigned int *r = (unsigned int *) registers;
  unsigned int *rs = (unsigned int *) regsState;
  int i;

  for (i = HPPA_GEN_REGNUM; i < HPPA_FRAME_REGNUM; i++) {
    r[i] = rs[i-HPPA_GEN_REGNUM];
    CLEAN_UP_REGISTER_VALUE(i,(char *)&r[i]);
  }
  return;
}

void
copy_registers_integer_out(regsState)
     struct hp_pa_integer_thread_state *regsState;
{
  unsigned int *r = (unsigned int *) registers;
  unsigned int *rs = (unsigned int *) regsState;
  int i;

  for (i = 1; i < HPPA_GEN_REGNUM; i++)
    rs[i-1] = r[i];
  return;
}

void
copy_registers_frame_out(regsState)
     struct hp_pa_frame_thread_state *regsState;
{
  unsigned int *r = (unsigned int *) registers;
  unsigned int *rs = (unsigned int *) regsState;
  int i;

  bzero(regsState,sizeof(*regsState));
  for (i = HPPA_GEN_REGNUM; i < HPPA_FRAME_REGNUM; i++)
    rs[i-HPPA_GEN_REGNUM] = r[i];
  regsState->ts_alignment_trap_reflect = catch_alignment_traps;
  return;
}

void
copy_fp_registers_in(fpState)
     struct hp_pa_fp_thread_state *fpState;
{
  unsigned int *r = (unsigned int *) registers;
  unsigned int *rs = (unsigned int *) fpState;
  int i;

  for (i = HPPA_FP_REGNUM; i < HPPA_NUM_REGS; i++)
    r[i] = rs[i-HPPA_FP_REGNUM];
  return;
}


void
copy_fp_registers_out(fpState)
     struct hp_pa_fp_thread_state *fpState;
{
  unsigned int *r = (unsigned int *) registers;
  unsigned int *rs = (unsigned int *) fpState;
  int i;

  for (i = HPPA_FP_REGNUM; i < HPPA_NUM_REGS; i++)
    rs[i-HPPA_FP_REGNUM] = r[i];
  return;
}

void
fetch_inferior_registers (regno)
{
  int i;
  extern char registers[];
  extern char register_valid[];

  if (current_thread) {
    if (regno < HPPA_GEN_REGNUM || regno == -1) {
      struct hp_pa_integer_thread_state int_regsState;
      unsigned int_regsCount = HPPA_INTEGER_THREAD_STATE_COUNT;

      mach_call(thread_get_state(current_thread,
				 HPPA_INTEGER_THREAD_STATE,
				 (thread_state_t)&int_regsState, 
				 &int_regsCount),
		"thread_get_state", "fetch_inferior_registers");

      copy_registers_integer_in(&int_regsState);
      for (i = 0; i < HPPA_GEN_REGNUM; i++)
	register_valid[i] = 1;

      for (i = HPPA_FRAME_REGNUM; i < HPPA_FP_REGNUM; i++)
	register_valid[i] = 1;	/* since we never get them... */
    }
    if (((regno >= HPPA_GEN_REGNUM) && (regno < HPPA_FRAME_REGNUM)) ||
	  regno == -1) {
      struct hp_pa_frame_thread_state frame_regsState;
      unsigned frame_regsCount = HPPA_FRAME_THREAD_STATE_COUNT;

      mach_call(thread_get_state(current_thread,
				 HPPA_FRAME_THREAD_STATE,
				 (thread_state_t)&frame_regsState, 
				 &frame_regsCount),
		"thread_get_state", "fetch_inferior_registers");

      copy_registers_frame_in(&frame_regsState);
      for (i = HPPA_GEN_REGNUM; i < HPPA_FRAME_REGNUM; i++)
	register_valid[i] = 1;
    }
	/* get FP registers only if we have to */
    if (regno >= FP0_REGNUM || regno == -1) {
      struct hp_pa_fp_thread_state fpState;
      unsigned fpCount = HPPA_FP_THREAD_STATE_COUNT;
      mach_call(thread_get_state(current_thread,
				 HPPA_FP_THREAD_STATE,
				 (thread_state_t)&fpState, 
				 &fpCount),
		"thread_get_state", "fetch_inferior_registers");
      copy_fp_registers_in(&fpState);
      /* NOT HPPA_NUM_REGS since these end with doubles */
      for (i = HPPA_FP_REGNUM; i < NUM_REGS; i++)
	register_valid[i] = 1;
    }
  }
}

/* Store our register values back into the inferior.
   If REGNO is -1, do this for all registers.
   Otherwise, REGNO specifies which register (so we can save time).  */
void
store_inferior_registers(regno)
{
  struct hp_pa_frame_thread_state frame_regsState;
  struct hp_pa_integer_thread_state int_regsState;
  struct hp_pa_fp_thread_state fpState;
  unsigned frame_regsCount = HPPA_FRAME_THREAD_STATE_COUNT;
  unsigned integer_regsCount = HPPA_INTEGER_THREAD_STATE_COUNT;
  unsigned fpCount = HPPA_FP_THREAD_STATE_COUNT;
  extern char registers[];

  if (current_thread) {
    validateRegister(regno);
    if (!stopped_in_ptrace)
      mach_call(thread_abort(current_thread),
		"thread_abort", "store_inferior_registers");
    if (regno < HPPA_GEN_REGNUM || regno == -1) {
      struct hp_pa_integer_thread_state int_regsState;
      unsigned int_regsCount = HPPA_INTEGER_THREAD_STATE_COUNT;

      copy_registers_integer_out(&int_regsState);
      mach_call(thread_set_state(current_thread,
				 HPPA_INTEGER_THREAD_STATE,
				 (thread_state_t)&int_regsState, 
				 int_regsCount),
		"thread_set_state", "store_inferior_registers");
    }
    if (((regno >= HPPA_GEN_REGNUM) && (regno < HPPA_FRAME_REGNUM)) ||
	  regno == -1) {
      struct hp_pa_frame_thread_state frame_regsState;
      unsigned frame_regsCount = HPPA_FRAME_THREAD_STATE_COUNT;

      copy_registers_frame_out(&frame_regsState);
      mach_call(thread_set_state(current_thread,
				 HPPA_FRAME_THREAD_STATE,
				 (thread_state_t)&frame_regsState, 
				 frame_regsCount),
		"thread_set_state", "store_inferior_registers");
    }
    /* set FP registers only if we have to */
    if (regno >= FP0_REGNUM || regno == -1) {
      struct hp_pa_fp_thread_state fpState;
      unsigned fpCount = HPPA_FP_THREAD_STATE_COUNT;
      copy_fp_registers_out(&fpState);
      mach_call(thread_set_state(current_thread,
				 HPPA_FP_THREAD_STATE,
				 (thread_state_t)&fpState, 
				 fpCount),
		"thread_set_state", "store_inferior_registers");
    }
  }
}

int getFirstTwoIntArgs(first, second)
     unsigned *first, *second;
{
  *first = (unsigned)read_register(ARG0_REGNUM);
  *second = (unsigned)read_register(ARG1_REGNUM);
  return YES;
}


static unsigned long assemble_21(x)
     unsigned int x;
{
  unsigned long temp;

  temp = ( ( x &        1 ) << 20 ) |
	 ( ( x &    0xffe ) <<  8 ) |
	 ( ( x &   0xc000 ) >>  7 ) |
	 ( ( x & 0x1f0000 ) >> 14 ) |
	 ( ( x & 0x003000 ) >> 12 );
  return temp & 0x1fffff;
}

static unsigned long assemble_17(x, y, z)
     unsigned int x, y, z;
{
  unsigned long temp;

  temp = ( ( z &     1 ) << 16 ) |
	 ( ( x &  0x1f ) << 11 ) |
	 ( ( y &     1 ) << 10 ) |
 	 ( ( y & 0x7fe ) >>  1);
  return temp & 0x1ffff;
}

static unsigned long sign_ext(x, len)
     unsigned int x, len;
{
  unsigned int sign;
  unsigned int result;
  unsigned int len_ones;
  int i;

  i = 0;
  len_ones = 0;
  while ( i < len ) {
    len_ones = (len_ones << 1) | 1;
    i++;
  }

  sign = (x >> (len-1)) & 1;

  if ( sign )
    result = ( ~0 ^ len_ones ) | ( len_ones & x );
  else
    result = len_ones & x;

  return result;
}

#define JMPTABLEOFFSET 0x54

typedef struct _JmpSlot {
  unsigned long opcode[2];
} JmpSlot;

CORE_ADDR getBranch(pc)
     CORE_ADDR pc;
{
  JmpSlot *jmpSlot;
  unsigned high,low;

  jmpSlot = WARP((JmpSlot *)pc);
  high = assemble_21(jmpSlot->opcode[0]&0x1fffff)<<11;
  low =  assemble_17((jmpSlot->opcode[1] & 0x1f0000) >> 16,
		     (jmpSlot->opcode[1] & 0x1ffc) >> 2,
		      jmpSlot->opcode[1] & 1);
   low = sign_ext(low,17);
   low <<= 2;
   return high+low;
}

CORE_ADDR *
jmpTableEnd(sectAddr, sect)
     CORE_ADDR sectAddr;
     struct section *sect;
{
  JmpSlot *jmpSlot;
  void *endOfText;
  BOOL foundEnd = NO;

  for (jmpSlot = (struct _JmpSlot *)(sectAddr + JMPTABLEOFFSET),
       endOfText = (void *)(sectAddr + sect->size);
       !foundEnd && (jmpSlot < (struct _JmpSlot *)endOfText);
       jmpSlot++) {
    if (((jmpSlot->opcode[0] != 0) && (jmpSlot->opcode[1] != 0)) &&
	((jmpSlot->opcode[0] & 0xffe00000 != 0x20200000) &&
	 (jmpSlot->opcode[1] & 0xffe0e002 != 0xe4208002)))
      {
	foundEnd = YES;
      }
  }
  return (CORE_ADDR *) (sect->addr + ((void *) (jmpSlot - 1) - sectAddr));
}

cpu_type_t cpuType() 
{
  return CPU_TYPE_HPPA;
}

/* We use this hook to turn on alignment exception trapping based on the
 * value of the "set alignment-traps" variable
 */
void set_hppa_threadstate(thread)
     port_t thread;
{
  struct hp_pa_frame_thread_state frame_regsState;
  unsigned frame_regsCount = HPPA_FRAME_THREAD_STATE_COUNT;

  mach_call(thread_get_state(thread,
			     HPPA_FRAME_THREAD_STATE,
			     (thread_state_t)&frame_regsState, 
			     &frame_regsCount),
	    "thread_get_state", "set_hppa_threadstate");

  frame_regsState.ts_alignment_trap_reflect = catch_alignment_traps;

  mach_call(thread_set_state(thread,
			     HPPA_FRAME_THREAD_STATE,
			     (thread_state_t)&frame_regsState, 
			     frame_regsCount),
	    "thread_set_state", "set_alignment_trapping");
}
