/*  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 3 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, see <http://www.gnu.org/licenses/>. */

/*****************************************************************/
/* 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);
	}
    }
}
