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

extern thread_t current_thread;
extern RegionManager *regionManager;

#define sizeofA(a) (sizeof(a)/sizeof(a[0]))

static int thread_reg_offsets[] = {
  PS_REGNUM, 
  PC_REGNUM, 
  NPC_REGNUM, 
  Y_REGNUM, 
  G0_REGNUM + 1,
  G0_REGNUM + 2,
  G0_REGNUM + 3,
  G0_REGNUM + 4,
  G0_REGNUM + 5,
  G0_REGNUM + 6,
  G0_REGNUM + 7,
  O0_REGNUM,
  O0_REGNUM + 1,
  O0_REGNUM + 2,
  O0_REGNUM + 3,
  O0_REGNUM + 4,
  O0_REGNUM + 5,
  O0_REGNUM + 6,
  O0_REGNUM + 7
};
		
void
copy_registers_in(regsState)
     struct sparc_thread_state_regs *regsState;
{
  int i;
  unsigned int *r = (unsigned int *) registers;
  unsigned int *rs = (unsigned int *) regsState;

  r[G0_REGNUM] = 0;
  register_valid[G0_REGNUM] = 1;

  for (i = 0; i < sizeofA(thread_reg_offsets);  i++) {
    r[thread_reg_offsets[i]] = rs[i];
    register_valid[thread_reg_offsets[i]] = 1;
  }
}


void
copy_registers_out(regsState)
     struct sparc_thread_state_regs *regsState;
{
  int i;
  unsigned int *r = (unsigned int *) registers;    
  unsigned int *rs = (unsigned int *) regsState;

  for (i = 0; i < sizeofA(thread_reg_offsets); i++)
    rs[i] = r[thread_reg_offsets[i]];
}

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

  r[FPS_REGNUM] = fpState->fpu.Fpu_fsr;
  register_valid[FPS_REGNUM] = 1;
  r[FQ0_REGNUM] = (unsigned int) fpState->fpu.Fpu_q[0].FQu.fpq.addr;
  register_valid[FQ0_REGNUM] = 1;
  r[FQ1_REGNUM] = fpState->fpu.Fpu_q[0].FQu.fpq.instr;
  register_valid[FQ1_REGNUM] = 1;

  memmove(r + FP0_REGNUM,
	  &fpState->fpu.fpu_fr.Fpu_regs, 
	  REGISTER_RAW_SIZE(FP0_REGNUM) * 32);
  for (i = 0; i < 32; i++)
    register_valid[FP0_REGNUM + i] = 1;
}

void
copy_fp_registers_out(fpState)
     struct sparc_thread_state_fpu *fpState;
{
  unsigned int *r = (unsigned int *) registers;

  memmove(&fpState->fpu.fpu_fr.Fpu_regs, 
	  r + FP0_REGNUM, 
	  REGISTER_RAW_SIZE(FP0_REGNUM) * 32);
  fpState->fpu.Fpu_fsr = registers[FPS_REGNUM];
}

void
fetch_inferior_registers (regno)
{
  int i;

  if (! current_thread)
    return;

  /* Global and Out regs are fetched directly, as well as the 
   * control registers.  If we're getting one of the in or 
   * local regs, and the stack pointer has not yet been fetched, 
   * we have to do that first, since they're found in memory 
   * relative to the stack pointer.  
   * Note that we ignore the WIM,TBR, & CSR since they're not
   * in any regs state.
   */

  if (regno < O7_REGNUM  /* including -1 */
      || regno == Y_REGNUM
      || regno == PS_REGNUM
      || regno == PC_REGNUM
      || regno == NPC_REGNUM
      || (!register_valid[SP_REGNUM] && regno <= I7_REGNUM)) {

    struct sparc_thread_state_regs int_regsState;
    unsigned int_regsCount = SPARC_THREAD_STATE_REGS_COUNT;

    mach_call(thread_get_state(current_thread,
			       SPARC_THREAD_STATE_REGS,
			       (thread_state_t)&int_regsState, 
			       &int_regsCount),
	      "thread_get_state", "fetch_inferior_registers");
    copy_registers_in(&int_regsState);
  }

  /* Get the fp registers if needed. */

  if ((regno == -1) 
      || ((regno >= FP0_REGNUM) && (regno < FP0_REGNUM + 32))) {
    struct sparc_thread_state_fpu fpu_State;
    unsigned fpu_regsCount = SPARC_THREAD_STATE_FPU_COUNT;

    mach_call(thread_get_state(current_thread,
			       SPARC_THREAD_STATE_FPU,
			       (thread_state_t)&fpu_State, 
			       &fpu_regsCount),
	      "thread_get_state", "fetch_inferior_registers");
    copy_fp_registers_in(&fpu_State);
  }

  /* These regs are saved on the stack by the kernel.  Only read 
   * them all if we really need them.  
   */
      
  if (regno == -1) {
    target_xfer_memory (*(CORE_ADDR*)&registers[REGISTER_BYTE (SP_REGNUM)],
			&registers[REGISTER_BYTE (L0_REGNUM)],
			16*REGISTER_RAW_SIZE (L0_REGNUM), 0);
    for (i = L0_REGNUM; i <= I7_REGNUM; i++)
      register_valid[i] = 1;
  } else if (regno >= L0_REGNUM && regno <= I7_REGNUM) {
    CORE_ADDR sp = *(CORE_ADDR*)&registers[REGISTER_BYTE (SP_REGNUM)];
    i = REGISTER_BYTE (regno);
    if (register_valid[regno])
      fprintf(stderr, "register %d valid and read\n", regno);
    target_xfer_memory (sp + i - REGISTER_BYTE (L0_REGNUM),
			&registers[i], REGISTER_RAW_SIZE (regno), 0);
    register_valid[regno] = 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)
{
  if (current_thread) {
    validateRegister(regno);
    if (!stopped_in_ptrace)
      mach_call(thread_abort(current_thread),
		"thread_abort", "store_inferior_registers");
    if ((regno < FP0_REGNUM) ||
	((regno >= Y_REGNUM) && (regno <=NPC_REGNUM)) || 
	(regno == -1)) {
      struct sparc_thread_state_regs intState;
      unsigned intCount = SPARC_THREAD_STATE_REGS_COUNT;

      copy_registers_out(&intState);
      mach_call(thread_set_state(current_thread,
				 SPARC_THREAD_STATE_REGS,
				 (thread_state_t)&intState, 
				 intCount),
		"thread_set_state", "store_inferior_registers");
    }

    /* set FP registers only if we have to */
    if (((regno >= FP0_REGNUM) && (regno < Y_REGNUM)) || regno == -1) {
      struct sparc_thread_state_fpu fpState;
      unsigned fpCount = SPARC_THREAD_STATE_FPU_COUNT;
      copy_fp_registers_out(&fpState);
      mach_call(thread_set_state(current_thread,
				 SPARC_THREAD_STATE_FPU,
				 (thread_state_t)&fpState, 
				 fpCount),
		"thread_set_state", "store_inferior_registers");
    }

   /* The following regs (%l0 - %l7 & %i0 - %i7) are on the stack.
    * We have to write them to the inferior's stack in order to
    * change them.
    * Also, if the stack pointer was changed, make sure that
    * these registers are re-written to new location on stack.
    */

    if (regno == -1 || regno == SP_REGNUM) {
      target_xfer_memory (*(CORE_ADDR*)&registers[REGISTER_BYTE (SP_REGNUM)],
			  &registers[REGISTER_BYTE (L0_REGNUM)],
			  16*REGISTER_RAW_SIZE (L0_REGNUM),1);

    } else if (regno >= L0_REGNUM && regno <= I7_REGNUM) {
      CORE_ADDR sp = *(CORE_ADDR*)&registers[REGISTER_BYTE (SP_REGNUM)];
      int i = REGISTER_BYTE (regno);
      target_xfer_memory (sp + i - REGISTER_BYTE (L0_REGNUM),
			  &registers[i], REGISTER_RAW_SIZE (regno),1);
    }
  }
}

int getFirstTwoIntArgs(first, second)
     unsigned *first, *second;
{
  *first = (unsigned)read_register(O0_REGNUM);
  *second = (unsigned)read_register(O0_REGNUM+1);
  return YES;
}

typedef unsigned long JmpSlot;

#define JMPTABLEOFFSET 0x54
#define TRAPOPCODE_SPARC	0x91d02000
#define JMPOPCODE_SPARC		0x30800000
#define OPCODE_MASK_SPARC	0xffc00000

CORE_ADDR getBranch(pc)
     CORE_ADDR pc;
{
  JmpSlot *jmpSlot;
  long offset;

  jmpSlot = WARP((JmpSlot *)pc);
  offset = (*jmpSlot & 0x3fffff);
  if (offset & 0x200000) offset |= 0xffe00000;
  return pc + 4*offset;
}

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

  for (jmpSlot = (JmpSlot *)(sectAddr + JMPTABLEOFFSET),
       endOfText = (void *)(sectAddr + sect->size);
       !foundEnd && (jmpSlot < (JmpSlot *)endOfText);
       jmpSlot++) {
    if ((*jmpSlot                      != TRAPOPCODE_SPARC) &&
       ((*jmpSlot & OPCODE_MASK_SPARC) != JMPOPCODE_SPARC))
    {
       foundEnd = YES;
    }
  }
  return (CORE_ADDR *)(sect->addr + ((void *)(jmpSlot - 1)- sectAddr));
}

cpu_type_t cpuType()
{
  return CPU_TYPE_SPARC;
}
