
/*
 * This file is JRD's Atari ST startup file for C++.  Much of the
 * guts are cribbed from the old CRT0.S, with strategic additions
 * from the G++ CRT0.C.
 *
 * This file is intended to be compiled with -fomit-frame-pointer, but
 * should work otherwise.
 */

#include <basepage.h>

struct basepage * _base;		/* pointer to our basepage */
int _stksize;				/* stack size */
char ** environ;			/* our env vector */
int errno;				/* err number for various things */

extern char * __ataris_pexec_scheme_is_brain_dead;

static void setup_trap_handlers();
static void setup_global_state();
static void restore_trap_handlers();
static void restore_global_state();

#ifdef CPLUSPLUS
extern void __do_global_init ();
extern void __do_global_cleanup ();
#endif

_start(base)
struct basepage * base;
{
/* NB!  all local vars must be regs, here, as we'll be moving the
   stack pointer around! */

  register char * p;			/* general char ptr */
  register struct basepage * bp;	/* basepage ptr */
  register char ** envp;		/* ptr used for building env */
  register char ** lastenv;		/* ptr to last slot */
  register char ** argv;		/* what will get turned into argv */
  register int argc;
  register int nbytes;			/* nbytes in command line */

  _base = bp = base;			/* stash the basepage ptr */
  envp = (char ** )(bp->bbase + bp->blen); /* point to mem past bss */
  environ = envp;			/* stash the global var */
  p = bp->env;				/* point to passed env */

  while (*p)				/* while there's more env... */
	{
	*envp++ = p;			/* stash a var */
	while (*p++) ;			/* and skip over it */
	}
  *envp++ = (char * ) 0;		/* and end the env vector */
  lastenv = envp;

/* try to grok the 'ARGV=' form of command line */
  for (envp = environ ; *envp ; envp++)
	{
	p = *envp;			/* get a var */
	if (*p++ == 'A' &&
	    *p++ == 'R' &&
	    *p++ == 'G' && 
	    *p++ == 'V' &&
	    *p++ == '=')		/* it says "ARGV=" */
		{
/* at this point, envp points to the sentinel.  skip it, and
   go set up the stack.  envp will be used as argv. */
		envp[-1] = (char * )0;	/* zap ptr to sentinel */
		envp++;			/* skip the "ARGV=" */
		argv = envp;
		argc = (((int )lastenv - (int )argv) / sizeof(char *)) - 1;
		goto setup;
		}
	}

/* ok, didn't find argv.  parse command line */
  p = &bp->cmdline[0];
  nbytes = *p++;
  argv = lastenv;		/* start here */
  *lastenv++ = __ataris_pexec_scheme_is_brain_dead; /* sorry, best we can do */
  argc = 1;			/* so far */
  
  while (nbytes >= 0)		/* more bytes to scan? */
	{
	/* look for next nonwhite */
	while(*p && ((*p == ' ') || (*p == '\t')))
		{
		p++;
		if (--nbytes < 0) goto setup;
		}
	if (*p == '\0')		/* run off end of string? */
		break;		/* stop here */
	*lastenv++ = p;		/* stash this pointer in argv */
	argc++;			/* count it */

	/* look for next white */
	while(*p && ((*p != ' ') && (*p != '\t')) && (nbytes > 0))
		{
		p++;
		--nbytes;
		}
	/* zap end of string */
	*p++ = '\0';
	--nbytes;
	}

setup:
/* ok, we've got argv etc set up.  setup stack */

  if (_stksize == 0)
	_stksize = 8192;
  if (_stksize > 0)		/* -1 means leave it alone */
	{			/* want program size + argv + stack */
	nbytes = ((int )lastenv - (int )bp) + _stksize;
	nbytes |= 3;		/* round up */
	nbytes += 1;
	
	asm ("movel %0,sp" : : "a" (bp));	/* move base to sp */
	asm ("addal %0,sp" : : "d" (nbytes));	/* add nbytes */
	asm ("subl a6,a6");			/* zap a6 so as not to confuse gdb */
	
	asm ("movel %0,sp@-" : : "d" (nbytes)); /* push nbytes */
	asm ("movel %0,sp@-" : : "a" (bp));	/* push basepage addr */
	asm ("movew #0,sp@-");			/* push a halfword zero */
	asm ("movew #0x4A,sp@-");		/* Mshrink */
	asm ("trap #1");
	}
/*
   whew!  we're now in good shape, as we have a real stack.  Call the 
   rest of the startup logic.  
   Ordinarly, _main will call exit, which will call _exit, however, it's
   possible for one to write one's own _main, and it might return.
*/
  setup_trap_handlers();
  setup_global_state();
  _exit(_main(argc, argv, environ));
}

/*
   the real exit function.
*/
_exit(value)
int value;
{
  short v = value;

  restore_trap_handlers();
  restore_global_state();

  asm volatile ("movew %0,sp@-" : : "g" (v));		/* push ret code */
  asm volatile ("movew #0x4C,sp@-");			/* Exit */
  asm volatile ("trap #1");				/* Poof! */
}

/* this is here, not at beginning, so that _start really is the 
   first thing in the .text segment */

char * __ataris_pexec_scheme_is_brain_dead = "<arg0>";

/* just a stub for now.  later set up something more useful than the
   TOS bomb-printer.  */
static void setup_trap_handlers()
{
  /* setup trap vectors here */
}

/*
   hooks used by setup_global_state
*/
typedef void (* hook)();

hook __setup_stdio_hook;

static void setup_global_state()
{
  if (__setup_stdio_hook)
	(*__setup_stdio_hook)();

  /* do other env-specific init here */
#ifdef CPLUSPLUS
  __do_global_init();
#endif
}

/* put traps back the way we found them */
static void restore_trap_handlers()
{
}

static void restore_global_state()
{
  /* restore env-specific stuff */
#ifdef CPLUSPLUS
  __do_global_cleanup();
#endif
}


#ifdef CPLUSPLUS
/* 
   when compiling this stuff with the G++, include the following, cribbed
   from g++'s CRT0.C.
*/


void
__do_global_init ()
{
  extern void (*__CTOR_LIST__)();
  register void (**ppf)() = &__CTOR_LIST__;
  register int *pi = (int *)ppf;

#ifdef VIRTUAL_FUNCTION_MASK
  if ((int)_end_crt0 < VINDEX_MAX)
    {
      printf ("virtual function index too large--encroaches text space at address 0x%x\n", _end_crt0);
      abort ();
    }
#else
  /* @@What to do for losing segmented architectures?  */
#endif

  while (*pi++)
    {
      /* Losing PCC cannot handle this statement as (*ppf++)().  Pity.  */
      (*ppf)();
      ppf++;
    }
}

typedef struct dtor_table_entry
{
  struct dtor_table_entry *next;
  void (*fp)();
} dtor_table_entry;

extern dtor_table_entry *__DTOR_LIST__;

void
__do_global_cleanup ()
{
  register int i;

  while (__DTOR_LIST__)
    {
      /* Prevent problems if a destructor should mistakenly call
	 exit() by always consuming one entry per round.  */
      void (*fp)() = __DTOR_LIST__->fp;
      __DTOR_LIST__ = __DTOR_LIST__->next;
      (*fp)();
    }
}

static int
_end_crt0 ()
{
}
#endif
