/*****************************************************************************

			       XCopilot

This code is part of XCopilot, a port of copilot

		  Copyright (C) 1997 Ivan A. Curtis
		       icurtis@radlogic.com.au

The original MS-Windows95 copilot emulator was written by Greg Hewgill.
The following copyright notice appeared on the original copilot sources:

		  Copyright (c) 1996 Greg Hewgill

 MC68000 Emulation code is from Bernd Schmidt's Unix Amiga Emulator.
       The following copyright notice appeared in those files:

	  Original UAE code Copyright (c) 1995 Bernd Schmidt

This code must not be distributed without these copyright notices intact.

*******************************************************************************
*******************************************************************************

Filename:	pdebug.c

Description:	debugging routines for xcopilot emulator

Update History:   (most recent first)
   Ian Goldberg 26-Jun-98 12:42 -- added support for PalmOS 3.x
   Ian Goldberg 25-Sep-97 16:18 -- fixed loading of filenames with spaces
   Ian Goldberg 10-Apr-97 16:53 -- added logging of flow traces
   I. Curtis     9-Apr-97 11:54 -- added debug via socket
   I. Curtis     5-Mar-97 21:03 -- added load command
   I. Curtis    27-Feb-97 22:16 -- created - start/stop/quit commands

******************************************************************************/
#include <config.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>

#include "sysdeps.h"
#include "shared.h"
#include "memory.h"
#include "custom.h"
#include "newcpu.h"
#include "dragonball.h"

#include "pilotcpu.h"
#include "fakecall.h"
#include "pdebug.h"

#define DEBUG_OK  0
#define DEBUG_ERR 1
#define MaxLineLen 200		      /* input buffer */
static char *Delim = " \t\r\n";	      /* command token delimeters */

/*****************************************************************************
 *                                                                           *
 * 			   Command Handlers                                  *
 *                                                                           *
 *****************************************************************************/

/*
 * Each command handler is a function with the 
 * prototype :
 *  int fn(FILE *out, FILE *in, char *cmd, char *line, shared_img *shptr)
 * out and in are the files currently being used for output and input
 * cmd is the comand word, line is the rest of the command line
 * and shptr is a pointer to the shared memroy image
 */

#ifndef HAVE_STRSEP
/* Copyright (C) 1992, 1993, 1996 Free Software Foundation, Inc.
This file is part of the GNU C Library.

The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

The GNU C Library 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
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with the GNU C Library; see the file COPYING.LIB.  If
not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.  */
char *strsep (char **stringp, const char *delim)
{
  char *begin, *end;

  begin = *stringp;
  if (! begin || *begin == '\0')
    return NULL;

  /* Find the end of the token.  */
  end = strpbrk (begin, delim);
  if (end)
    {
      /* Terminate the token and set *STRINGP past NUL character.  */
      *end++ = '\0';
      *stringp = end;
    }
  else
    /* No more delimiters; this is the last token.  */
    *stringp = NULL;

  return begin;
}
#endif

/* This variable stores the MEMORY_VERSION corresponding to the ROM we're
   using.  We need to know this so that we can construct a valid chunk
   header.  A value of 1 is for ROMs <= 2.x; A value of 2 is for 3.0.
   It's unclear what is the best way to set this.  Autodetection would be
   great, if we could do it correctly.  - Ian */
int pdebug_memversion = 1;

/***********************
 * Load an application *
 ***********************/
int dbg_loadapp(FILE *out, FILE *in, char *cmd, char *line, shared_img *shptr)
{
  char *fname;
  FILE *prcfile;
  int err;

  struct stat prcstats;
  if (out == NULL) {
    out = stderr;
  }

  /*
   * get the file name
   */
  if (cmd) { /* If invoked from the right-button menu, skip these checks */
      if (!(fname = strsep(&line, Delim))) {
	if (out) {
	  fprintf(out, "error - you must specify a file to load\n");
	}
	return DEBUG_ERR;
      }
  } else {
    fname = line;
  }

  err = stat(fname, &prcstats);
  if (err != 0) {
    if (out) {
      fprintf(out, "error - cannot stat \"%s\"\n", fname);
    }
    return DEBUG_ERR;
  }

  prcfile = fopen(fname, "r");
  if (prcfile == NULL) {
    if (out) {
      fprintf(out, "error - cannot open \"%s\" for read\n", fname);
    }
    return DEBUG_ERR;
  }

  if (pdebug_memversion == 1) {
    int size;
    unsigned int csize;
    size = prcstats.st_size;
    csize = (size + 7) & ~1;
    /*
     * Check that size is not larger than scratchmemory
     */
    if (size + 8 > SCRATCH_SIZE || csize > 0xFFFF) {
	if (out) {
	    fprintf(out, "error - \"%s\" too large\n", fname);
	}
	fclose(prcfile);
	return DEBUG_ERR;
    }
    /*
     * set up fake chunk header 
     */
    scratchmemory[0] = 0xFF;
    scratchmemory[1] = 0xFF;
    scratchmemory[2] = (UBYTE)((csize >> 8) & 0xff);
    scratchmemory[3] = (UBYTE)(csize & 0xff);
    scratchmemory[4] = 0xF0;
    scratchmemory[5] = (UBYTE)((csize - 6 - size) & 0x0f);
    scratchmemory[6] = 0;
    scratchmemory[7] = 0;
  } else if (pdebug_memversion == 2) {
    int size;
    unsigned int csize;
    size = prcstats.st_size;
    csize = (size + 9) & ~1;
    /*
     * Check that size is not larger than scratchmemory
     */
    if (size + 8 > SCRATCH_SIZE || csize > 0xFFFFFF) {
	if (out) {
	    fprintf(out, "error - \"%s\" too large\n", fname);
	}
	fclose(prcfile);
	return DEBUG_ERR;
    }
    /*
     * set up fake chunk header (version 2)
     */
    scratchmemory[0] = (UBYTE)((csize - 8 - size) & 0x0f);
    scratchmemory[1] = (UBYTE)((csize >> 16) & 0xff);
    scratchmemory[2] = (UBYTE)((csize >> 8) & 0xff);
    scratchmemory[3] = (UBYTE)(csize & 0xff);
    scratchmemory[4] = 0xF0;
    scratchmemory[5] = 0;
    scratchmemory[6] = 0;
    scratchmemory[7] = 0;
  } else {
    if (out) {
	fprintf(out, "error - unknown memversion\n");
    }
    fclose(prcfile);
    return DEBUG_ERR;
  }
  fread(&scratchmemory[8], 1, prcstats.st_size, prcfile);
  fclose(prcfile);

  /*
   *   fprintf(stderr, "D - setting exception flag\n");
   *   fflush(stderr);
   */
  CPU_setexceptionflag(shptr, 32 + 0xF, 1);
  CPU_wait(shptr);
  /*
   *   fprintf(stderr, "D - clearing exception flag\n");
   *   fflush(stderr);
   */
  CPU_setexceptionflag(shptr, 32 + 0xF, 0);


  /*
   *   fprintf(stderr, "D - Requesting cpuLoadApp..\n");
   *   fflush(stderr);
   */
  shptr->CpuReq = cpuLoadApp;
  while (shptr->CpuState != cpuLoaded) {
    usleep(1000);
  }
  /*
   *   fprintf(stderr, "D - cpuLoadApp done\n");
   *   fflush(stderr);
   */
  {
    int ret_code;
    if (shptr->ErrNo == 0) {
      ret_code = DEBUG_OK;
    } else {
      ret_code = DEBUG_ERR;
    }

    shptr->CpuReq = cpuStart;
    return ret_code;
  }
}

/*****************
 * Start the cpu *
 *****************/
int dbg_start(FILE *out, FILE *in, char *cmd, char *line, shared_img *shptr)
{
  shptr->CpuReq = cpuStart;
  return DEBUG_OK;
}

/****************
 * Stop the cpu *
 ****************/
int dbg_stop(FILE *out, FILE *in, char *cmd, char *line, shared_img *shptr)
{
  shptr->CpuReq = cpuStop;
  return DEBUG_OK;
}

/***************
 * Show the PC *
 ***************/
int dbg_pc(FILE *out, FILE *in, char *cmd, char *line, shared_img *shptr)
{
  fprintf(out, "%08lx\n", (shptr->regs).pc +
      ((char *)(shptr->regs).pc_p - (char *)(shptr->regs).pc_oldp));
  return DEBUG_OK;
}

/**************************
 * Turn logging on or off *
 **************************/
int dbg_log(FILE *out, FILE *in, char *cmd, char *line, shared_img *shptr)
{
  char *arg = strsep(&line, Delim);
  if (!strcmp(arg, "on")) {
    /* Turn logging on */
    shptr->dolog = 1;
    return DEBUG_OK;
  } else if (!strcmp(arg, "off")) {
    /* Turn logging off */
    shptr->dolog = 0;
    return DEBUG_OK;
  } else {
    fprintf(out, "Usage: %s on|off\n", cmd);
    return DEBUG_OK;
  }
}

/**********************
 * Quit the emulation *
 **********************/
int dbg_quit(FILE *out, FILE *in, char *cmd, char *line, shared_img *shptr)
{
  shptr->CpuReq = cpuExit;
  return DEBUG_OK;
}

int dbg_help(FILE *out, FILE *in, char *cmd, char *line, shared_img *shptr);

/**************************
 * Command dispatch table *
 **************************/
struct {
  char *name;
  int (*fun)(FILE *, FILE *, char *, char *, shared_img *shptr);
  char *help;
} cmd_table[] = {
  {"help", dbg_help, ": Give brief help on commands"},
  {"start", dbg_start, ": Start Cpu running"},
  {"stop", dbg_stop, ": Stop Cpu running"},
  {"pc", dbg_pc, ": Show current PC"},
  {"log", dbg_log, "on|off : turn logging on or off"},
  {"load", dbg_loadapp, " <prcfile>: load application"},
  {"quit", dbg_quit, ": Quit XCopilot"},
  {NULL, NULL, NULL}
};

int dbg_help(FILE *out, FILE *in, char *cmd, char *line, shared_img *shptr)
{
  int i;
  fprintf(out, "Command Summary\n");
  for (i = 0; cmd_table[i].name; i++) {
    fprintf(out, "%s\t %s\n", cmd_table[i].name, cmd_table[i].help);
  }
  return DEBUG_OK;
}
#include <netinet/in.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>

#define MaxHostNameLen 20

/*
 * Establish a socket and listen for connections
 * Accept a connection on the socket
 * s and return the file descriptor
 * or -1 if failed
 */
int dbg_connect(unsigned short portnum)
{
  char host_name[MaxHostNameLen];
  struct sockaddr_in saddr;
  struct hostent *host_ptr;
  int sid;
  int one = 1;

  /*
   * Clear out socket structure
   */
  memset(&saddr, 0, sizeof(struct sockaddr_in));

  /*
   * Get host name
   */
  gethostname(host_name, MaxHostNameLen - 1);
  host_ptr = gethostbyname(host_name);
  if (host_ptr == NULL) {
    return -1;
  }

  /*
   * Set socket address: host and port
   * and create socket
   */
  saddr.sin_family = host_ptr->h_addrtype;
  saddr.sin_port = htons(portnum);
  sid = socket(AF_INET, SOCK_STREAM, 0);
  if (sid < 0) {
    return -1;
  }

  /*
   * Bind socket to address
   */
  setsockopt(sid, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one));
  if (bind(sid, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in)) < 0) {
    close(sid);
    return -1;
  }

  /*
   * Listen for at most 1 connection
   */
  listen(sid, 1);
  return accept(sid, NULL, NULL);
}

/*****************************************************************************
 *                                                                           *
 * 			  Main debugger loop                                 *
 *                                                                           *
 *****************************************************************************/
void pdebug_loop(char *args, int portnum, shared_img *shptr)
{
  char buff[MaxLineLen], *line, *word;
  int i, status;
  int fid;
  FILE *in, *out;

  /*
   * Establish a socket
   */
  fid = dbg_connect(portnum);
  if (fid < 0) {
    perror("dbg_connect");
    return;
  }
  in = fdopen(fid, "r");
  if (in == NULL) {
    perror("fdopen in");
    return;
  }
  out = fdopen(fid, "w");
  if (out == NULL) {
    perror("fdopen out");
    return;
  }

  do {
    fprintf(out, "command > ");
    fflush(out);
    if (!(line = fgets(buff, MaxLineLen, in))) {
      break;
    }
    if (!(word = strsep(&line, Delim))) {
      continue;
    }
    i = 0;
    while (cmd_table[i].name && strcmp(word, cmd_table[i].name) != 0) {
      i++;
    }
    if (cmd_table[i].name) {
      status = (cmd_table[i].fun)(out, in, word, line, shptr);
    } else if (*word != '\0' && *word != ';') {
      fprintf(out, "E - debug - unrecognized command \"%s\"\n", word);
    }
  }
  while (shptr->CpuReq != cpuExit);
}
