/*  kid.c -- ARMulator RDP/RDI interface:  ARM6 Instruction Emulator.
    Copyright (C) 1994 Advanced RISC Machines Ltd.
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

/*****************************************************************/
/* The child process continues here...                           */
/* It waits on a pipe from the parent and translates the RDP     */
/* messages into RDI calls to the ARMulator passing RDP replies  */
/* back up a pipe to the parent.                                 */
/*****************************************************************/

#include <sys/types.h>
#include <signal.h>

#include "armdefs.h"
#include "dbg_conf.h"
#include "dbg_hif.h"
#include "dbg_rdi.h"
#include "gdbhost.h"
#include "communicate.h"

/* The pipes between the two processes */
extern int mumkid[2];
extern int kidmum[2];

/* The maximum number of file descriptors */
extern int nfds;

/* The machine name */
#define MAXHOSTNAMELENGTH 64
extern char localhost[MAXHOSTNAMELENGTH + 1];

/* The socket number */
extern unsigned int socketnumber;

/* RDI interface */
extern const struct RDIProcVec armul_rdi;

static int MYrdp_level = 0;

static int rdi_state = 0;

/**************************************************************/
/* Signal handler that terminates excecution in the ARMulator */
/**************************************************************/
void kid_handlesignal(int sig) {
#ifdef DEBUG
  fprintf(stderr, "Terminate ARMulator excecution\n");
#endif
  if (sig != SIGUSR1) {
    fprintf(stderr, "Unsupported signal.\n");
    return;
  }
  armul_rdi.info(RDISignal_Stop, (unsigned long *) 0, (unsigned long *) 0);
}

/********************************************************************/
/* Waits on a pipe from the socket demon for RDP and                */
/* acts as an RDP to RDI interpreter on the front of the ARMulator. */
/********************************************************************/
void kid() {
  char *p, *q;
  int i, j, k;
  long outofthebag;
  unsigned char c, d, message;
  ARMword x, y, z;
  struct sigaction action;
  PointHandle point;
  Dbg_ConfigBlock config;
  Dbg_HostosInterface hostif;
  struct Dbg_MCState *MCState;
  char command_line[256];
  struct fd_set readfds;
  
  /* Setup a signal handler for SIGUSR1 */
  action.sa_handler = kid_handlesignal;
  action.sa_mask = 0;
  action.sa_flags = 0;
  
  sigaction(SIGUSR1, &action, (struct sigaction *) 0);
  
  while (1)
  {
    /* Wait for ever */
    FD_ZERO(&readfds);
    FD_SET(mumkid[0], &readfds);
    
    i = select(nfds, &readfds,
	       (fd_set *) 0,
	       (fd_set *) 0,
	       (struct timeval *) 0);
    
    if (i < 0) {
      perror("select");
    }
    
    if (read(mumkid[0], &message, 1) < 1) {
      perror("read");
    }

    switch (message) {
    case RDP_Start :
      /* Open and/or Initialise */
      BAG_newbag();

      MYread_char(mumkid[0], &c); /* type */
      MYread_word(mumkid[0], &x); /* memorysize */
      if (c & 0x2) MYread_char(mumkid[0], &d); /* speed */
      config.processor = 0;
      config.memorysize = x;
      config.bytesex = (c & 0x4) ? RDISex_Big : RDISex_Little;
      if (c & 0x8) config.bytesex = RDISex_DontCare;

      hostif.dbgprint = myprint;
      hostif.dbgpause = mypause;
      hostif.dbgarg = stdout;
      hostif.writec = mywritec;
      hostif.readc = myreadc;
      hostif.write = mywrite;
      hostif.gets = mygets;
      hostif.reset = mypause; /* do nothing */
      hostif.resetarg = "Do I love resetting or what!\n";

      if (rdi_state)
      {
	/* we have restarted, so kill off the existing run.  */
	/* armul_rdi.close(); */
      }
      i = armul_rdi.open(c & 0x3, &config, &hostif, MCState);
      rdi_state = 1;

      MYwrite_char(kidmum[1], RDP_Return);
      MYwrite_char(kidmum[1], (unsigned char) i);

      x = ~0x4;
      armul_rdi.info(RDIVector_Catch, &x, 0);

      break;

    case RDP_End :
      /* Close and Finalise */
      i = armul_rdi.close();
      rdi_state = 0;
      MYwrite_char(kidmum[1], RDP_Return);
      MYwrite_char(kidmum[1], (unsigned char) i);
      break;

    case RDP_Read :
      /* Read Memory Address */      
      MYread_word(mumkid[0], &x); /* address */
      MYread_word(mumkid[0], &y); /* nbytes */
      p = (char *) malloc(y);
      i = armul_rdi.read(x, p, (unsigned *) &y);
      MYwrite_char(kidmum[1], RDP_Return);
      for (k = 0; k < y; k++)
	MYwrite_char(kidmum[1], p[k]);
      free(p);
      MYwrite_char(kidmum[1], (unsigned char) i);
      if (i)
	MYwrite_word(kidmum[1], y); /* number of bytes sent without error */
      break;

    case RDP_Write :
      /* Write Memory Address */
      MYread_word(mumkid[0], &x); /* address */
      MYread_word(mumkid[0], &y); /* nbytes */
      p = (char *) malloc(y);
      for (k = 0; k < y; k++)
	MYread_char(mumkid[0], &p[k]);
      i = armul_rdi.write(p, x, (unsigned *) &y);
      free(p);
      MYwrite_char(kidmum[1], RDP_Return);
      MYwrite_char(kidmum[1], (unsigned char) i);
      if (i)
	MYwrite_word(kidmum[1], y); /* number of bytes sent without error */
      break;

    case RDP_CPUread :
      /* Read CPU State */
      MYread_char(mumkid[0], &c); /* mode */
      MYread_word(mumkid[0], &x); /* mask */
      p = (char *) malloc(4 * RDINumCPURegs);
      i = armul_rdi.CPUread(c, x, (ARMword *) p);
      MYwrite_char(kidmum[1], RDP_Return);
      for (k = 1, j = 0; k != 0x80000000; k *= 2)
	if (k & x) MYwrite_word(kidmum[1], ((ARMword *) p)[j++]);
      free(p);
      if (i) MYwrite_char(kidmum[1], (unsigned char) j);
      MYwrite_char(kidmum[1], (unsigned char) i);
      break;

    case RDP_CPUwrite :
      /* Write CPU State */
      MYread_char(mumkid[0], &c); /* mode */
      MYread_word(mumkid[0], &x); /* mask */

      p = (char *) malloc(4 * RDINumCPURegs);
      for (k = 1, j = 0; k != 0x80000000; k *= 2)
	if (k & x) MYread_word(mumkid[0], &(((ARMword *) p)[j++]));
      i = armul_rdi.CPUwrite(c, x, (ARMword *) p);
      MYwrite_char(kidmum[1], RDP_Return);
      MYwrite_char(kidmum[1], (unsigned char) i);
      free(p);
      break;

    case RDP_CPread :
      /* Read Co-Processor State */
      MYread_char(mumkid[0], &c); /* CPnum */
      MYread_word(mumkid[0], &x); /* mask */
      p = q = (char *) malloc(16 * RDINumCPRegs);
      i = armul_rdi.CPread(c, x, (ARMword *) p);
      MYwrite_char(kidmum[1], RDP_Return);
      for (k = 1, j = 0; k != 0x80000000; k *= 2, j++)
	if (k & x) {
	  if ((c == 1 || c == 2) && k <= 128) {
	    MYwrite_FPword(kidmum[1], q);
	    q += 16;
	  }
	  else {
	    MYwrite_word(kidmum[1], *q);
	    q += 4;
	  }
	}
      free(p);
      if (i) MYwrite_char(kidmum[1], (unsigned char) j);
      MYwrite_char(kidmum[1], (unsigned char) i);
      break;

    case RDP_CPwrite :
      /* Write Co-Processor State */
      MYread_char(mumkid[0], &c); /* CPnum */
      MYread_word(mumkid[0], &x); /* mask */
      p = q = (char *) malloc(16 * RDINumCPURegs);
      for (k = 1, j = 0; k != 0x80000000; k *= 2, j++)
	if (k & x) {
	  if ((c == 1 || c == 2) && k <= 128) {
	    MYread_FPword(kidmum[1], q);
	    q += 16;
	  }
	  else {
	    MYread_word(mumkid[0], (ARMword *) q);
	    q += 4;
	  }
	}
      i = armul_rdi.CPwrite(c, x, (ARMword *) p);
      MYwrite_char(kidmum[1], RDP_Return);
      MYwrite_char(kidmum[1], (unsigned char) i);
      free(p);
      break;
      
    case RDP_SetBreak :
      /* Set Breakpoint */
      MYread_word(mumkid[0], &x); /* address */
      MYread_char(mumkid[0], &c); /* type */
      if ((c & 0xf) >= 5) MYread_word(mumkid[0], &y); /* bound */
      i = armul_rdi.setbreak(x, c, y, &point);
      if (!MYrdp_level) BAG_putpair((long) x, (long) point);
      MYwrite_char(kidmum[1], RDP_Return);
      if (MYrdp_level) MYwrite_word(kidmum[1], point);
      MYwrite_char(kidmum[1], (unsigned char) i);      
      break;

    case RDP_ClearBreak :
      /* Clear Breakpoint */
      MYread_word(mumkid[0], &point); /* PointHandle */
      if (!MYrdp_level) {
	BAG_getsecond((long) point, &outofthebag); /* swap pointhandle for address */
	BAG_killpair_byfirst(outofthebag);
	point = outofthebag;
      }
      i = armul_rdi.clearbreak(point);
      MYwrite_char(kidmum[1], RDP_Return);
      MYwrite_char(kidmum[1], (unsigned char) i);
      break;

    case RDP_SetWatch :
      /* Set Watchpoint */
      MYread_word(mumkid[0], &x); /* address */
      MYread_char(mumkid[0], &c); /* type */
      MYread_char(mumkid[0], &d); /* datatype */
      if ((c & 0xf) >= 5) MYread_word(mumkid[0], &y); /* bound */
      i = armul_rdi.setwatch(x, c, d, y, &point);
      MYwrite_char(kidmum[1], RDP_Return);
      MYwrite_word(kidmum[1], point);
      MYwrite_char(kidmum[1], (unsigned char) i);
      break;

    case RDP_ClearWatch :
      /* Clear Watchpoint */
      MYread_word(mumkid[0], &point); /* PointHandle */
      i = armul_rdi.clearwatch(point);
      MYwrite_char(kidmum[1], RDP_Return);
      MYwrite_char(kidmum[1], (unsigned char) i);
      break;

    case RDP_Execute :
      /* Excecute */
      
      MYread_char(mumkid[0], &c); /* return */

#ifdef DEBUG
      fprintf(stderr, "Starting execution\n");
#endif
      i = armul_rdi.execute(&point);
#ifdef DEBUG
      fprintf(stderr, "Completed execution\n");
#endif
      MYwrite_char(kidmum[1], RDP_Return);
      if (c & 0x80) MYwrite_word(kidmum[1], point);
      MYwrite_char(kidmum[1], (unsigned char) i);
      break;

    case RDP_Step :
      /* Step */
      MYread_char(mumkid[0], &c); /* return */
      MYread_word(mumkid[0], &x); /* ninstr */
      point = 0x87654321;
      i = armul_rdi.step(x, &point);
      MYwrite_char(kidmum[1], RDP_Return);
      if (c & 0x80) MYwrite_word(kidmum[1], point);
      MYwrite_char(kidmum[1], (unsigned char) i);
      break;

    case RDP_Info:
      /* Info */
      MYread_word (mumkid[0], &x);
      switch (x)
	{
	case RDIInfo_Target:
	  i = armul_rdi.info (RDIInfo_Target, &y, &z);
	  MYwrite_char (kidmum[1], RDP_Return);
	  MYwrite_word (kidmum[1], y); /* Loads of info... */
	  MYwrite_word (kidmum[1], z); /* Model */
	  MYwrite_char (kidmum[1], (unsigned char) i);
	  break;

	case RDISet_RDILevel:
	  MYread_word (mumkid[0], &x); /* arg1, debug level */
	  i = armul_rdi.info (RDISet_RDILevel, &x, 0);
	  if (i == RDIError_NoError)
	    MYrdp_level = x;
	  MYwrite_char (kidmum[1], RDP_Return);
	  MYwrite_char (kidmum[1], (unsigned char) i);
	  break;

	case RDISet_Cmdline:
	  for (p = command_line; MYread_char (mumkid[0], p), *p; p++)
	    ; /* String */
	  i = armul_rdi.info (RDISet_Cmdline,
			      (unsigned long *) command_line, 0);
	  MYwrite_char (kidmum[1], RDP_Return);
	  MYwrite_char (kidmum[1], (unsigned char) i);
	  break;

	case RDIInfo_Step:
	  i = armul_rdi.info (RDIInfo_Step, &x, 0);
	  MYwrite_char (kidmum[1], RDP_Return);
	  MYwrite_word (kidmum[1], x);
	  MYwrite_char (kidmum[1], (unsigned char) i);
	  break;

	case RDIVector_Catch:
	  MYread_word (mumkid[0], &x);
	  i = armul_rdi.info (RDIVector_Catch, &x, 0);
	  MYwrite_char (kidmum[1], RDP_Return);
	  MYwrite_char (kidmum[1], i);
	  break;

	case RDIInfo_Points:
	  i = armul_rdi.info (RDIInfo_Points, &x, 0);
	  MYwrite_char (kidmum[1], RDP_Return);
	  MYwrite_word (kidmum[1], x);
	  MYwrite_char (kidmum[1], (unsigned char) i);
	  break;

	default:
	  fprintf (stderr, "Unsupported info code %d\n", x);
	  break;
	}
      break;

    case RDP_OSOpReply:
      /* OS Operation Reply */
      MYwrite_char (kidmum[1], RDP_Fatal);
      break;

    case RDP_Reset:
      /* Reset */
      for (i = 0; i < 50; i++)
	MYwrite_char(kidmum[1], RDP_Reset);
      p = (char *) malloc(MAXHOSTNAMELENGTH + 5 + 20);
      sprintf(p, "Running on %s:%d\n", localhost, socketnumber);
      MYwrite_string(kidmum[1], p);
      free(p);
      
      break;
    default:
      fprintf (stderr, "Oh dear: Something is seriously wrong :-(\n");
      /* Hmm.. bad RDP operation */
      break;
    }
  }
}


/* Handles memory read operations until an OS Operation Reply Message is */
/* encounterd. It then returns the byte info value (0, 1, or 2) and fills  */
/* in 'putinr0' with the data if appropriate. */
int wait_for_osreply(ARMword *reply)
{
  char *p, *q;
  int i, j, k;
  unsigned char c, d, message;
  ARMword x, y, z;
  struct sigaction action;
  PointHandle point;
  Dbg_ConfigBlock config;
  Dbg_HostosInterface hostif;
  struct Dbg_MCState *MCState;
  char command_line[256];
  struct fd_set readfds;
  
#ifdef DEBUG
  fprintf(stderr, "wait_for_osreply ().\n");
#endif

  /* Setup a signal handler for SIGUSR1 */
  action.sa_handler = kid_handlesignal;
  action.sa_mask = 0;
  action.sa_flags = 0;
  
  sigaction(SIGUSR1, &action, (struct sigaction *) 0);
  
  while (1)
  {
    /* Wait for ever */
    FD_ZERO(&readfds);
    FD_SET(mumkid[0], &readfds);
    
    i = select(nfds, &readfds,
	       (fd_set *) 0,
	       (fd_set *) 0,
	       (struct timeval *) 0);
    
    if (i < 0) {
      perror("select");
    }
    
    if (read(mumkid[0], &message, 1) < 1) {
      perror("read");
    }
    
    switch (message) {
    case RDP_Read :
      /* Read Memory Address */
      MYread_word(mumkid[0], &x); /* address */
      MYread_word(mumkid[0], &y); /* nbytes */
      p = (char *) malloc(y);
      i = armul_rdi.read(x, p, (unsigned *) &y);
      MYwrite_char(kidmum[1], RDP_Return);
      for (k = 0; k < y; k++)
	MYwrite_char(kidmum[1], p[k]);
      free(p);
      MYwrite_char(kidmum[1], (unsigned char) i);
      if (i)
	MYwrite_word(kidmum[1], y); /* number of bytes sent without error */
      break;

    case RDP_Write :
      /* Write Memory Address */
      MYread_word(mumkid[0], &x); /* address */
      MYread_word(mumkid[0], &y); /* nbytes */
      p = (char *) malloc(y);
      for (k = 0; k < y; k++)
	MYread_char(mumkid[0], &p[k]);
      i = armul_rdi.write(p, x, (unsigned *) &y);
      free(p);
      MYwrite_char(kidmum[1], RDP_Return);
      MYwrite_char(kidmum[1], (unsigned char) i);
      if (i)
	MYwrite_word(kidmum[1], y); /* number of bytes sent without error */
      break;
      
    case RDP_OSOpReply :
      /* OS Operation Reply */
      MYread_char(mumkid[0], &c);
      if (c == 1) MYread_char(mumkid[0], (char *) reply);
      if (c == 2) MYread_word(mumkid[0], reply);
      return c;
      break;
      
    default :
      fprintf(stderr, "HELP! Unaccounted-for message during OS request. \n");
      MYwrite_char(kidmum[1], RDP_Fatal);
    }
  }
}