/* * * Crtinit: C run-time initialization code. * Written by Eric R. Smith, and placed in the public domain. * Use at your own risk. * * 01/03/89 ++jrb * The (new) meaning of _stksize: (thanks to allan pratt for the feedback) * * _stksize meaning * -1L keep all of memory (except MINFREE at top) and do * mallocs from own heap, with heap grown upwards towards * stack, and the stack growing down towards heap, * with a minimum slush between them so that they * dont meet (only checked while malloc'ing). With * this model, further spawning is not possible, but it is * well suited for programs such as gcc-cc1 etc. * Thanks to Piet van Oostrum & Atze Dijkstra for this idea * * 0L keep minimum amount of memory. this is also the * case when _stksize is undefined by the user. * 1L keep 1/4 of memory, free 3/4 ( as in Alcyon GEMSTART) * 2L keep 2/4 (1/2), free rest * 3L keep 3/4, free 1/4 * other keep that many bytes * -other keep |other| bytes, use the heap for mallocs * * 02/14/90 ++jrb (thanks edgar) * auto acc detect * undump friendly * * NOTE: dumping applications should use _initial_stack instead: if * !=0, then _stksize is initialized from _initial_stack, and * mallocs are always from internal heap. (TeX works much better now), * thanks edgar! * * Acc convention: * user sets _heapbase to bottom of stack + heap area * sets _stksize to the size of this area * at startup, sp will be set to top of this area * (_heapbase + _stksize ) and malloc()'s will happen from heap. * (note malloc() and *not* Malloc()) * * 02/16/90 ++jrb * - bug fix: dont get screwed by desktop launch when fast bit is set * convert env string to format usable * (atari get your act together!!) */ #include #include #define isspace(c) ((c) == ' '||(c) == '\t') #define BUFSIZ ((unsigned long)1024) /* this must track the value */ /* in stdio.h */ #define MINFREE (8L * 1024L) /* free at least this much mem */ /* on top */ #define MINKEEP (8L * 1024L) /* keep at least this much mem */ BASEPAGE *_base; char **environ; static long argc; static char **argv; /* * initial stack is used primarily by dumping application, * if it is, malloc is always from heap, and _stksize is init * from initial_stack (to preserve the value in the undumped run) */ long _initial_stack; /* .comm __initial_size, 4 */ extern long _stksize; /* picked up from user or from stksiz.c */ /* set to heap base addr when _stksize == -1L || _initial_stack || When DA */ void *_heapbase; /* default sizeof stdio buffers */ unsigned long __DEFAULT_BUFSIZ__; /* .comm */ /* are we an app? */ short _app; static long parseargs(); static void setup_handlers __PROTO((void)); __EXTERN void _main __PROTO((long, char **, char **)); __EXTERN void _init_signal __PROTO((void)); __EXTERN void monstartup __PROTO((void *lowpc, void *highpc)); __EXTERN void _mcleanup __PROTO((void)); __EXTERN void moncontrol __PROTO((long)); /* * accessories start here: */ static char *acc_argv[] = {"", (char *) 0}; /* no name and no arguments */ void _acc_main() { _app = 0; /* this is an accessory */ _main(1L, acc_argv, acc_argv); /*NOTREACHED*/ } /* functions in crt0.s or gcrt0.s */ extern void _setstack(), _monstartup(), _moncontrol(), __mcleanup(); void _crtinit() { register BASEPAGE *bp; register long m; register long freemem; _app = 1; /* its an application */ if(!__DEFAULT_BUFSIZ__) __DEFAULT_BUFSIZ__ = BUFSIZ; bp = _base; m = parseargs(bp); /* m = # bytes used by environment + args */ /* make m the total number of bytes required by program sans stack/heap */ m += (bp->p_tlen + bp->p_dlen + bp->p_blen); m = (m + 3L) & (~3L); /* freemem the amount of free mem accounting for MINFREE at top */ if((freemem = (long)bp->p_hitpa - (long)bp->p_tbase - MINFREE - m) <= 0L) goto notenough; if(_initial_stack) { /* the primary use of _initial_stack will be in dumping */ /* applications where only a heap for malloc makes sense */ _heapbase = (void *) ((long)bp->p_tbase + m); _stksize = _initial_stack; } switch(_stksize) { case -1L: /* keep all but MINFREE, and malloc from own heap */ _stksize = freemem; _heapbase = (void *) ((long)bp->p_tbase + m); break; case 0L: /* free all but MINKEEP */ _stksize = MINKEEP; break; case 1L: /* keep 1/4, free 3/4 */ _stksize = freemem >> 2; break; case 2L: /* keep 1/2, free 1/2 */ _stksize = freemem >> 1; break; case 3L: /* keep 3/4, free 1/4 */ _stksize = freemem - (freemem >> 2); break; default: if(_stksize < 0) { /* keep |_stksize|, use heap for mallocs */ _stksize = -_stksize; _heapbase = (void *)((long)bp->p_tbase + m); } } /* make m the total number of bytes including stack */ _stksize = _stksize & (~3L); m += _stksize; /* make sure there's enough room for the stack */ if ((((long)bp->p_tbase) + m) > ((long)bp->p_hitpa - MINFREE)) goto notenough; /* set up the new stack to bp->p_tbase + m */ _setstack(bp->p_tbase + m); /* shrink the TPA */ /* this is right only if bp == bp->p_tbase - sizeof(BASEPAGE) */ (void)Mshrink(bp, m + sizeof(BASEPAGE)); /* establish handlers, call the main routine */ setup_handlers(); /* start profiling, if we were linked with crt0.o */ _monstartup((void *)(bp->p_tbase), (void *)((long)bp->p_tbase + bp->p_tlen)); _main(argc, argv, environ); /* not reached normally */ notenough: Cconws("Fatal error: insufficient memory\r\n"); Pterm(-1); } /* * parseargs(bp): parse the environment and arguments pointed to by the * basepage. Return the number of bytes of environment and arguments * that have been appended to the bss area (the environ and argv arrays * are put here, as is a temporary buffer for the command line, if * necessary). * * The MWC extended argument passing scheme is assumed. * */ static long parseargs(bp) BASEPAGE *bp; { long count = 4; /* compensate for aligning */ long i; char *from, *cmdln, *to; char **envp, **arg; /* flag to indicate desktop-style arg. passing */ long desktoparg = 0; /* handle the environment first */ environ = envp = (char **)(( (long)bp->p_bbase + bp->p_blen + 4) & (~3)); from = bp->p_env; while (*from) { /* if we find MWC arguments, tie off environment here */ if (*from == 'A' && *(from+1) == 'R' && *(from+2) == 'G' && *(from+3) == 'V' && *(from+4) == '=') { *envp++ = (char *) 0; count += 4; *from++ = 0; #ifdef STRICTLY_COMPATIBLE_WITH_STANDARD if (bp->p_cmdlin[0] != 127) goto old_cmdlin; #endif while (*from++) ; /* skip ARGV= string */ argv = arg = envp++; *arg++ = from; count+= 4; while (*from++) ; /* skip argv[0] */ goto do_argc; } *envp++ = from; count += 4; desktoparg = 1; while (*from) { if (*from == '=') { desktoparg = 0; } from++; } from++; /* skip 0 */ /* the desktop (and some shells) use the environment in the wrong way, putting in "PATH=\0C:\0" instead of "PATH=C:". so if we find an "environment variable" without an '=' in it, we see if the last environment variable ended with '=\0', and if so we append this one to the last one */ if(desktoparg && envp > &environ[1]) { /* launched from desktop -- fix up env */ char *p, *q; q = envp[-2]; /* current one is envp[-1] */ while (*q) q++; if (q[-1] == '=') { p = *--envp; while(*p) *q++ = *p++; *q = '\0'; } } } *envp++ = (char *)0; count += 4; /* old_cmdlin: */ /* Allocate some room for the command line to be parsed */ cmdln = bp->p_cmdlin; i = *cmdln++; from = to = (char *) envp; if (i > 0) { count += (i&(~3)); envp = (char **) ( ((long) envp) + (i&(~3)) ); } envp += 2; count += 8; /* Now parse the command line and put argv after the environment */ argv = arg = envp; *arg++ = ""; /* argv[0] not available */ count += 4; while(i > 0 && isspace(*cmdln) ) cmdln++,--i; while (i > 0) { if (isspace(*cmdln)) { --i; cmdln++; while (i > 0 && isspace(*cmdln)) --i,cmdln++; *to++ = 0; } else { if (!(*to++ = *cmdln++)) break; --i; } } *to++ = '\0'; *to = '\0'; /* bug fix example:cmdln == '\3' 'a' ' ' 'b' '\0' */ /* the loop below expects \0\0 at end to terminate! */ /* the byte @ cmdln[i+2] != 0 when fast bit is set */ do_argc: argc = 1; /* at this point argv[0] is done */ while (*from) { *arg++ = from; argc++; count += 4; while(*from++) ; } *arg++ = (char *) 0; return count+4; } static void setup_handlers() { _init_signal(); } /* * _exit: does the actual exiting. Note that _moncontrol and __mcleanup are * dummies in crt0.s, but do something in gcrt0.s */ void _exit(status) int status; { _moncontrol(0L); __mcleanup(); Pterm(status); }