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