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

extern thread_t current_thread;
extern RegionManager *regionManager;

void
fetch_inferior_registers (regno)
{
  struct m68k_thread_state_regs regsState;
  unsigned regsCount = M68K_THREAD_STATE_REGS_COUNT;
  struct m68k_thread_state_68882 floatRegsState;
  unsigned floatRegsCount = M68K_THREAD_STATE_68882_COUNT;
  int i;
  extern char registers[];
  extern char register_valid[];

  if (current_thread) {
    if ((regno == -1) || (regno < FP0_REGNUM)) {
      mach_call(thread_get_state(current_thread,
				 M68K_THREAD_STATE_REGS,
				 (thread_state_t)&regsState, 
				 &regsCount),
		"thread_get_state", "fetch_inferior_registers");
      bcopy(&regsState, registers,
	    sizeof(regsState.dreg) + sizeof(regsState.areg));
      *(int *)&registers[REGISTER_BYTE (PS_REGNUM)] = (int)regsState.sr;
      *(int *)&registers[REGISTER_BYTE (PC_REGNUM)] = regsState.pc;
      for (i = 0; i < FP0_REGNUM; i++)
	register_valid[i] = 1;
    }
    if ((regno == -1) || (regno >= FP0_REGNUM)) {
      mach_call(thread_get_state(current_thread,
				 M68K_THREAD_STATE_68882,
				 (thread_state_t)&floatRegsState, 
				 &floatRegsCount),
		"thread_get_state", "fetch_inferior_registers");
      bcopy(&floatRegsState, &registers[REGISTER_BYTE (FP0_REGNUM)],
	    sizeof(floatRegsState)); 
      for (i = FP0_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 m68k_thread_state_regs regsState;
  unsigned regsCount = M68K_THREAD_STATE_REGS_COUNT;
  struct m68k_thread_state_68882 floatRegsState;
  unsigned floatRegsCount = M68K_THREAD_STATE_68882_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 == -1) || (regno < FP0_REGNUM)) {
      bcopy(registers,  &regsState,
	    sizeof(regsState.dreg) + sizeof(regsState.areg));
      (int)regsState.sr = *(int *)&registers[REGISTER_BYTE (PS_REGNUM)];
      regsState.pc = *(int *)&registers[REGISTER_BYTE (PC_REGNUM)];
      mach_call(thread_set_state(current_thread,
				 M68K_THREAD_STATE_REGS,
				 (thread_state_t)&regsState, 
				 regsCount),
		"thread_set_state", "store_inferior_registers");
    }
    if ((regno == -1) || (regno >= FP0_REGNUM)) {
      bcopy(&registers[REGISTER_BYTE (FP0_REGNUM)], &floatRegsState,
	    sizeof(floatRegsState));
      mach_call(thread_set_state(current_thread,
				 M68K_THREAD_STATE_68882,
				 (thread_state_t)&floatRegsState, 
				 floatRegsCount),
		"thread_set_state", "store_inferior_registers");
    }
  }
}

CORE_ADDR
skip_prologue(function)
{
  CORE_ADDR prologue;
  unsigned int op;

  prologue = skip_prologue_by_symbol(function);
  if (!prologue) {
    prologue = function;
    op = read_memory_integer (prologue, 2);
    if (op == 0047126)					
      prologue += 4;   /* Skip link #word */			
    else if (op == 0044016)				
      prologue += 6;   /* Skip link #long */		
    /* Not sure why branches are here.  */              
    /* From m-isi.h, m-altos.h */                         
    else if (op == 0060000)				
      prologue += 4;   /* Skip bra #word */			
    else if (op == 00600377)				
      prologue += 6;   /* skip bra #long */			
    else if ((op & 0177400) == 0060000)			
      prologue += 2;   /* skip bra #char */
  }
  return prologue;
}

typedef struct _DummyFrame {
  unsigned ps;
  unsigned regs[14];
  unsigned char fpRegs[8][12];
  unsigned fp;
  unsigned pc;
} DummyFrame;

void
pushDummyFrame()
{
  DummyFrame dummyFrame;
  int i;

  unsigned sp = read_register(SP_REGNUM);
  dummyFrame.pc = read_register(PC_REGNUM);
  dummyFrame.fp = read_register(FP_REGNUM);
  for (i = 0; i < 8; i++)
    read_register_bytes(REGISTER_BYTE(FP0_REGNUM + i),
			dummyFrame.fpRegs[i], 12);
  for (i = 0; i < 14; i++)
    dummyFrame.regs[i] = read_register(i);
  dummyFrame.ps = read_register(PS_REGNUM);
  write_register(FP_REGNUM, sp - 8);
  sp = push_bytes(sp, (char *) &dummyFrame, sizeof(dummyFrame));
  write_register(SP_REGNUM, sp);
}

char breakpoint_insn[] = {0x4e, 0x4f};

typedef struct _JmpSlot {
  unsigned short opcode;
  unsigned int arg;
} JmpSlot;

#define JMPTABLEOFFSET 0x54
#define JMPOPCODE_68K  0x4EF9
#define TRAPOPCODE_68K 0x50FB

CORE_ADDR getBranch(pc)
     CORE_ADDR pc;
{
  JmpSlot *jmpSlot;
  jmpSlot = WARP((JmpSlot *)pc);
  return jmpSlot->arg;
}

CORE_ADDR
jmpTableEnd(sectAddr, sect)
     CORE_ADDR sectAddr;
     struct section *sect;
{
  JmpSlot *jmpSlot;
  void *endOfText;
  BOOL foundEnd = NO;
  
  for (jmpSlot   = (JmpSlot *) ((char *) sectAddr + JMPTABLEOFFSET),
       endOfText = (char *) sectAddr + sect->size;
       !foundEnd && (jmpSlot < (struct _JmpSlot *) endOfText);
       jmpSlot++) {
    if ((jmpSlot->opcode != JMPOPCODE_68K) &&
	(jmpSlot->opcode != TRAPOPCODE_68K))
      foundEnd = YES;
  }
  return (CORE_ADDR) (sect->addr + ((void *) (jmpSlot - 1) - sectAddr));
}

int getFirstTwoIntArgs(first, second)
     unsigned *first, *second;
{
  unsigned *sp = (unsigned*) read_register(SP_REGNUM);
  unsigned *argsPtr = [regionManager pointerFor: sp + 1
				       withSize: 2 * sizeof(CORE_ADDR)];

  if (argsPtr) {
    *first = *(argsPtr);
    *second = *(argsPtr + 1);
    return YES;
  } else {
    return NO;
  }
}

cpu_type_t cpuType() 
{
  return CPU_TYPE_MC680x0;
}
