/*  parent.c -- ARMulator RDP comms code:  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 Parent process continues here...                          */
/* It waits on the socket and passes on RDP messages down a pipe */
/* to the ARMulator RDP to RDI interpreter.                      */
/*****************************************************************/

#include <stdio.h>
#include <sys/types.h>
#include <signal.h>
#include "time.h"
#include "armdefs.h"
#include "dbg_rdi.h"
#include "communicate.h"

/* The socket to the debugger */
extern int debugsock;

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

/* A pipe for handling SWI return values that goes straight from the */
/* parent to the ARMulator host interface, bypassing the child's RDP */
/* to RDI interpreter */
extern int DebuggerARMul[2];

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

/* The child process id. */
extern pid_t child;

void
parent ()
{
  int i, j, k;
  unsigned char message, CPnum, exreturn;
  ARMword mask, nbytes, messagetype;
  unsigned char c, d;
  ARMword x, y;
  int virgin = 1;
  struct fd_set readfds;

#ifdef DEBUG
  fprintf (stderr, "parent ()...\n");
#endif

panic_error:

  if (!virgin)
    {
#ifdef DEBUG
      fprintf (stderr, "Arghh! What is going on?\n");
#endif
      kill (child, SIGHUP);
      MYwrite_char (debugsock, RDP_Reset);
    }

  virgin = 0;

  while (1)
    {

      /* Wait either for the ARMulator or the debugger */

      FD_ZERO (&readfds);
      FD_SET (kidmum[0], &readfds);	/* Wait for messages from ARMulator */
      FD_SET (debugsock, &readfds);	/* Wait for messages from debugger */

#ifdef DEBUG
      fprintf (stderr, "Waiting for ARMulator or debugger... ");
#endif

      while ((i = select (nfds, &readfds, (fd_set *) 0, (fd_set *) 0, 0)) < 0)
	{
	  perror ("select");
	}

#ifdef DEBUG
      fprintf (stderr, "(%d/2)", i);
#endif

      if (FD_ISSET (debugsock, &readfds))
	{
#ifdef DEBUG
	  fprintf (stderr, "->debugger\n");
#endif

	  /* Inside this rather large if statement with simply pass on a complete 
	     message to the ARMulator.  The reason we need to pass messages on one
	     at a time is that we have to know whether the message is an OSOpReply
	     or an info(stop), so that we can take different action in those
	     cases. */

	  if (MYread_char (debugsock, &message))
	    goto panic_error;

	  switch (message)
	    {
	    case RDP_Start:
	      /* Open and/or Initialise */
#ifdef DEBUG
	      fprintf (stderr, "RDP Open\n");
#endif
	      if (MYread_char (debugsock, &c))	/* type */
		goto panic_error;

	      if (MYread_word (debugsock, &x))	/* memory size */
		goto panic_error;

	      MYwrite_char (mumkid[1], message);
	      MYwrite_char (mumkid[1], c);
	      MYwrite_word (mumkid[1], x);
	      if (c & 0x2)
		{
		  passon (debugsock, mumkid[1], 1);	/* speed */
		}
	      break;

	    case RDP_End:
	      /* Close and Finalise */
#ifdef DEBUG
	      fprintf (stderr, "RDP Close\n");
#endif
	      MYwrite_char (mumkid[1], message);
	      break;

	    case RDP_Read:
	      /* Read Memory Address */
#ifdef DEBUG
	      fprintf (stderr, "RDP Read Memory\n");
#endif
	      MYwrite_char (mumkid[1], message);
	      if (passon (debugsock, mumkid[1], 4))
		goto panic_error;	/* address */
	      if (MYread_word (debugsock, &nbytes))
		goto panic_error;	/* nbytes */
	      MYwrite_word (mumkid[1], nbytes);
	      break;

	    case RDP_Write:
	      /* Write Memory Address */
#ifdef DEBUG
	      fprintf (stderr, "RDP Write Memory\n");
#endif
	      if (MYread_word (debugsock, &x))
		goto panic_error;	/* address */

	      if (MYread_word (debugsock, &y))
		goto panic_error;	/* nbytes */

	      MYwrite_char (mumkid[1], message);
	      MYwrite_word (mumkid[1], x);
	      MYwrite_word (mumkid[1], y);
	      passon (debugsock, mumkid[1], y);	/* actual data */
	      break;

	    case RDP_CPUread:
	      /* Read CPU State */
#ifdef DEBUG
	      fprintf (stderr, "RDP Read CPU\n");
#endif
	      if (MYread_char (debugsock, &c))
		goto panic_error;	/* mode */

	      if (MYread_word (debugsock, &mask))
		goto panic_error;	/* mask */

	      MYwrite_char (mumkid[1], message);
	      MYwrite_char (mumkid[1], c);
	      MYwrite_word (mumkid[1], mask);
	      break;

	    case RDP_CPUwrite:
	      /* Write CPU State */
#ifdef DEBUG
	      fprintf (stderr, "RDP Write CPU\n");
#endif
	      if (MYread_char (debugsock, &c))
		goto panic_error;	/* mode */

	      if (MYread_word (debugsock, &x))
		goto panic_error;	/* mask */

	      MYwrite_char (mumkid[1], message);
	      MYwrite_char (mumkid[1], c);
	      MYwrite_word (mumkid[1], x);
	      for (k = 1, j = 0; k != 0x80000000; k *= 2, j++)
		if ((k & x) && passon (debugsock, mumkid[1], 4))
		  goto panic_error;
	      break;

	    case RDP_CPread:
	      /* Read Co-Processor State */
#ifdef DEBUG
	      fprintf (stderr, "RDP Read CP state\n");
#endif
	      if (MYread_char (debugsock, &CPnum))
		goto panic_error;

	      if (MYread_word (debugsock, &mask))
		goto panic_error;

	      MYwrite_char (mumkid[1], message);
	      MYwrite_char (mumkid[1], CPnum);
	      MYwrite_word (mumkid[1], mask);
	      break;

	    case RDP_CPwrite:
	      /* Write Co-Processor State */
#ifdef DEBUG
	      fprintf (stderr, "RDP Write CP state\n");
#endif
	      if (MYread_char (debugsock, &CPnum))
		goto panic_error;

	      if (MYread_word (debugsock, &mask))
		goto panic_error;

	      MYwrite_char (mumkid[1], message);
	      MYwrite_char (mumkid[1], c);
	      MYwrite_char (mumkid[1], x);
	      for (k = 1, j = 0; k != 0x80000000; k *= 2, j++)
		if (k & x)
		  {
		    if ((c == 1 || c == 2) && k <= 128)
		      {
			/* FP register = 12 bytes + 4 bytes format */
			if (passon (debugsock, mumkid[1], 16))
			  goto panic_error;
		      }
		    else
		      {
			/* Normal register = 4 bytes */
			if (passon (debugsock, mumkid[1], 4))
			  goto panic_error;
		      }
		  }
	      break;

	    case RDP_SetBreak:
	      /* Set Breakpoint */
#ifdef DEBUG
	      fprintf (stderr, "RDP Set Breakpoint\n");
#endif
	      if (MYread_word (debugsock, &x))
		goto panic_error;	/* address */

	      if (MYread_char (debugsock, &c))
		goto panic_error;	/* type */

	      MYwrite_char (mumkid[1], message);
	      MYwrite_word (mumkid[1], x);
	      MYwrite_char (mumkid[1], c);
	      if (((c & 0xf) >= 5) && passon (debugsock, mumkid[1], 4))
		goto panic_error;	/* bound */
	      break;

	    case RDP_ClearBreak:
	      /* Clear Breakpoint */
#ifdef DEBUG
	      fprintf (stderr, "RDP Clear Breakpoint\n");
#endif
	      MYwrite_char (mumkid[1], message);
	      if (passon (debugsock, mumkid[1], 4))
		goto panic_error;	/* point */
	      break;

	    case RDP_SetWatch:
	      /* Set Watchpoint */
#ifdef DEBUG
	      fprintf (stderr, "RDP Set Watchpoint\n");
#endif
	      if (MYread_word (debugsock, &x))
		goto panic_error;	/* address */

	      if (MYread_char (debugsock, &c))
		goto panic_error;	/* type */

	      if (MYread_char (debugsock, &d))
		goto panic_error;	/* datatype */

	      MYwrite_char (mumkid[1], message);
	      MYwrite_word (mumkid[1], x);
	      MYwrite_char (mumkid[1], c);
	      MYwrite_char (mumkid[1], d);
	      if (((c & 0xf) >= 5) && passon (debugsock, mumkid[1], 4))
		goto panic_error;	/* bound */
	      break;

	    case RDP_ClearWatch:
	      /* Clear Watchpoint */
#ifdef DEBUG
	      fprintf (stderr, "RDP Clear Watchpoint\n");
#endif
	      MYwrite_char (mumkid[1], message);
	      if (passon (debugsock, mumkid[1], 4))
		goto panic_error;	/* point */
	      break;

	    case RDP_Execute:
	      /* Excecute */
#ifdef DEBUG
	      fprintf (stderr, "RDP Execute\n");
#endif

	      /* LEAVE THIS ONE 'TIL LATER... */
	      /* NEED TO WORK THINGS OUT */

	      /* NO ASCYNCHROUS RUNNING */

	      if (MYread_char (debugsock, &c))
		goto panic_error;	/* return */

	      /* Remember incase bit 7 is set and we have to send back a word */
	      exreturn = c;

	      MYwrite_char (mumkid[1], message);
	      MYwrite_char (mumkid[1], c);
	      break;

	    case RDP_Step:
	      /* Step */
#ifdef DEBUG
	      fprintf (stderr, "RDP Step\n");
#endif

	      if (MYread_char (debugsock, &c))
		goto panic_error;	/* return */

	      if (MYread_word (debugsock, &x))
		goto panic_error;	/* ninstr */

	      MYwrite_char (mumkid[1], message);
	      MYwrite_char (mumkid[1], c);
	      MYwrite_word (mumkid[1], x);
	      break;

	    case RDP_Info:
	      /* Info */
#ifdef DEBUG
	      fprintf (stderr, "RDP Info\n");
#endif
	      /* INFO TARGET, SET RDI LEVEL */
	      if (MYread_word (debugsock, &messagetype))
		goto panic_error;	/* info */

	      switch (messagetype)
		{
		case RDIInfo_Target:
		  MYwrite_char (mumkid[1], message);
		  MYwrite_word (mumkid[1], messagetype);
		  break;

		case RDISet_RDILevel:
		  MYwrite_char (mumkid[1], message);
		  MYwrite_word (mumkid[1], messagetype);
		  if (passon (debugsock, mumkid[1], 1))
		    goto panic_error;	/* argument */
		  break;

		case RDISet_Cmdline:
		  /* Got to pass on a string argument */
		  MYwrite_char (mumkid[1], message);
		  MYwrite_word (mumkid[1], messagetype);
		  do
		    {
		      if (MYread_char (debugsock, &c))
			goto panic_error;

		      MYwrite_char (mumkid[1], c);
		    }
		  while (c);
		  break;

		case RDISignal_Stop:
		  kill (child, SIGUSR1);
		  MYwrite_char (debugsock, RDP_Return);
		  MYwrite_char (debugsock, RDIError_UserInterrupt);
		  break;

		case RDIVector_Catch:
		  MYread_word (debugsock, &x);
		  MYwrite_char (mumkid[1], message);
		  MYwrite_word (mumkid[1], messagetype);
		  MYwrite_word (mumkid[1], x);
		  break;

		case RDIInfo_Step:
		  MYwrite_char (mumkid[1], message);
		  MYwrite_word (mumkid[1], messagetype);
		  break;

		case RDIInfo_Points:
		  MYwrite_char (mumkid[1], message);
		  MYwrite_word (mumkid[1], messagetype);
		  break;

		default:
		  fprintf (stderr, "Unrecognized RDIInfo request %d\n",
			   messagetype);
		  goto panic_error;
		}
	      break;

	    case RDP_OSOpReply:
	      /* OS Operation Reply */
#ifdef DEBUG
	      fprintf (stderr, "RDP OS Reply\n");
#endif
	      MYwrite_char (mumkid[1], message);
	      if (MYread_char (debugsock, &message))
		goto panic_error;
	      MYwrite_char (mumkid[1], message);
	      switch (message)
		{
		case 0:	/* return value i.e. nothing else. */
		  break;

		case 1:	/* returns a byte... */
		  if (MYread_char (debugsock, &c))
		    goto panic_error;

		  MYwrite_char (mumkid[1], c);
		  break;

		case 2:	/* returns a word... */
		  if (MYread_word (debugsock, &x))
		    goto panic_error;

		  MYwrite_word (mumkid[1], x);
		  break;
		}
	      break;

	    case RDP_Reset:
	      /* Reset */
#ifdef DEBUG
	      fprintf (stderr, "RDP Reset\n");
#endif
	      MYwrite_char (mumkid[1], message);
	      break;

	    default:
	      /* Hmm.. bad RDP operation */
	      fprintf (stderr, "RDP Bad RDP request (%d)\n", message);
	      MYwrite_char (debugsock, RDP_Return);
	      MYwrite_char (debugsock, RDIError_UnimplementedMessage);
	      break;
	    }
	}

      if (FD_ISSET (kidmum[0], &readfds))
	{
#ifdef DEBUG
	  fprintf (stderr, "->ARMulator\n");
#endif
	  /* Anything we get from the ARMulator has to go to the debugger... */
	  /* It is that simple! */

	  passon (kidmum[0], debugsock, 1);
	}
    }
}
