/*
** PPCRun
**
** Run an ELF object under AmigaOS as a new PowerPC task.
**
** V1.0  22-Dec-98 phx
**       Completely redesigned and simplified. This version has nothing
**       to do with its predecessors!
**       PPC task is started synchronously and uses PPCTASKTAG_BREAKSIGNAL
**       to pass CTRL-C.
**       New options:
**        -s for setting the stack size (defaults to 68k-stack size * 2)
**        -p for defining the PPC task priority (defaults to 0)
** V0.9a 24-Dec-97 vb
**       Modified to use inline library calls
** V0.9  23-Dec-97 phx
**       To be compatible with elfrun and SAS/C-ppc, pass the command
**       line in GPR3, instead argc/argv in GPR3/4.
**       PPCRun is compiled with vbcc's minimal startup code to get
**       direct access to the command line.
**       Removed all debugging output.
**       Successfully tested with ppc.library V45.2. Removing a crashed
**       task with CTRL-D works perfectly now. But I'm not sure, if I
**       fixed a bug or
** V0.4  18-Oct-97 phx
**       Close(errfh) was missing. Included a help text.
** V0.3  11-Oct-97 phx
**       Define DEBUG for debugging output.
** V0.2  07-Oct-97 phx
**       fixed some infinite loops :)
** V0.1  04-Oct-97 phx
**       created
*/

#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/libraries.h>
#include <dos/dos.h>
#include <dos/dosextens.h>
#include <dos/dostags.h>
#include <utility/tagitem.h>
#include <powerup/ppclib/tasks.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/ppc.h>

#define VERSION "PPCRun 1.0 (22.12.98)"
#define PPCLIBVER 45
#define PPCDEFPRI 0


struct ExecBase *SysBase;
struct DosLibrary *DOSBase;
struct Library *PPCLibBase;

static const char *ppcrun = "PPCRun";
static const char *version = "$VER: " VERSION "\r\n";
static const char *ppclibname = "ppc.library";


static char *nextarg(char *);
static char *skiparg(char *);



int __saveds PPCRun(__reg("a0")char *cmdline)
{
  int rc = 10;

  SysBase = *(struct ExecBase **)4;
  if (SysBase->LibNode.lib_Version < 36)  /* We need OS2.0! */
    return(20);
  if (!(DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",36)))
    return(20);

  if (*cmdline==0 || *cmdline=='\n' || *cmdline=='?') {
    Printf(VERSION "\n(c)1997-98 by Frank Wille\n\nUsage: "
           "%s [options] <ELF object> [<aguments> ...]\n\nOptions:\n"
           " -s <size>\t\tstack size for PPC task\n"
           " -p <priority>\t\tPPC task priority\n"
           ,ppcrun);
    rc = 5;
  }

  else {
    if (PPCLibBase = OpenLibrary((STRPTR)ppclibname,PPCLIBVER)) {
      void *elfobject;
      ULONG stksiz;
      LONG tskpri = PPCDEFPRI;
      struct Process *pr = (struct Process *)FindTask(NULL);
      char *p;

      /* determine stack size of M68k process */
      if (pr->pr_Task.tc_Node.ln_Type == NT_PROCESS && pr->pr_CLI)
        stksiz = *(ULONG *)pr->pr_ReturnAddr;
      else
        stksiz = (ULONG)((char *)pr->pr_Task.tc_SPUpper
                         - (char *)pr->pr_Task.tc_SPLower);

      /* parse options and determine name of ELF object */
      while (cmdline = nextarg(cmdline)) {
        if (*cmdline != '-')
          break;
        cmdline++;
        switch (*cmdline++) {
          case 's':  /* -s <stack size> */
            if (cmdline = nextarg(cmdline))
              StrToLong((STRPTR)cmdline,(LONG *)&stksiz);
            else
              Printf("%s: Illegal stack size\n",ppcrun);
            break;
          case 'p':  /* -p <task priority> */
            if (cmdline = nextarg(cmdline))
              StrToLong((STRPTR)cmdline,&tskpri);
            else
              Printf("%s: Illegal priority\n",ppcrun);
            break;
          default:
            Printf("%s: Unrecognized option -%lc!\n",
                   ppcrun,(ULONG)*(--cmdline));
            break;
        }
        cmdline = skiparg(cmdline);
      }

      if (p = cmdline) {
        char elfname[256];
        ULONG len;

        if ((len = skiparg(p)-p) > 255) {
          len = 255;
          Printf("%s: Name of ELF object > 255 chars!\n",ppcrun);
        }
        if (*p == '\"') {  /* file name in quotes? */
          p++;
          len -= 2;
        }
        CopyMem(p,elfname,len);
        elfname[len] = '\0';

        if (elfobject = (void *)PPCLoadObject(elfname)) {
          static struct TagItem tasktags[10];

          /* create synchronous PowerPC task */
          tasktags[0].ti_Tag	=	PPCTASKTAG_WAITFINISH;
          tasktags[0].ti_Data	=	TRUE;
          tasktags[1].ti_Tag	=	PPCTASKTAG_INPUTHANDLE;
          tasktags[1].ti_Data	=	(ULONG)Input();
          tasktags[2].ti_Tag	=	PPCTASKTAG_OUTPUTHANDLE;
          tasktags[2].ti_Data	=	(ULONG)Output();
          tasktags[3].ti_Tag	=	PPCTASKTAG_ARG1;
          tasktags[3].ti_Data	=	(ULONG)cmdline;
          tasktags[4].ti_Tag	=	PPCTASKTAG_STACKSIZE;
          tasktags[4].ti_Data	=	(stksiz>0x8000)? (stksiz<<1) : 0x10000;
          tasktags[5].ti_Tag	=	NP_CloseInput;
          tasktags[5].ti_Data	=	FALSE;
          tasktags[6].ti_Tag	=	NP_CloseOutput;
          tasktags[6].ti_Data	=	FALSE;
          tasktags[7].ti_Tag	=	PPCTASKTAG_BREAKSIGNAL;
          tasktags[7].ti_Data	=	TRUE;
          tasktags[8].ti_Tag	=	PPCTASKTAG_PRIORITY;
          tasktags[8].ti_Data	=	(ULONG)tskpri;
          tasktags[9].ti_Tag	=	TAG_END;
          rc = (int)PPCCreateTask(elfobject,tasktags);
          PPCUnLoadObject(elfobject);
        }
        else
          Printf("%s: Unknown command.\n",elfname);
      }
      else
        Printf("Missing argument <ELF object>!\n");
      CloseLibrary(PPCLibBase);
    }
    else
      Printf("Can't open %s V%ld.\n",ppclibname,PPCLIBVER);
  }
  CloseLibrary((struct Library *)DOSBase);
  return(rc);
}


static char *nextarg(char *p)
/* get pointer to next argument in command line */
{
  while (*p<=' ' && *p!=0)
    p++;
  if (*p == 0)
    return (NULL);
  return (p);
}


static char *skiparg(char *p)
/* find end of argument */
{
  char c;

  if (*p != '\"') {
    while (*p > ' ')
      p++;
  }
  else {
    p++;
    do
      c = *p++;
    while (c!='\"' && c!=0);
    if (c==0)
      p--;
  }
  return (p);
}
