/* * 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 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 = ""; /* 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