/*!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * NB: This file is used for both 16 and 32 bit code. * USE NO "int"s IN HERE!!!! *!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! * * * Crt0: C run-time initialization code. * Written by Eric R. Smith, and placed in the public domain. * Use at your own risk. * * _start(base): sets things up for the process whose basepage is * base. In particular, it sets up a valid environment, shrinks the * TPA to a reasonable value, and parses the command line. * * 01/03/89 ++jrb * The (new) meaning of _stksize: (thanks to allan pratt for the feedback) * * 11/27/91 ++jrb * More meanings for _stksize (thanks to eric and allan for the feedback) * * _stksize meaning * -4L keep 3/4, free 1/4, malloc from own heap * -3L keep 2/4 (1/2), free 1/2 malloc from own heap * -2L keep 1/4 of memory, free 3/4, malloc from own heap * * NOTE: all of the following will do malloc from Malloc() first, * when that fails, malloc() will do further mallocs from * our own heap. This lets us use the maximum amount of * memory in traditional ST's as well as newer split address * STs. * * -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 and malloc from own heap * * 02/14/90 ++jrb (thanks edgar) * auto acc detect * undump friendly * Note: some of the stuff here may seem extraneous: these are in * prep for upcoming plug'n play device interface * (the moment the ol'boss lets me do some "real" work). * * * 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()) * OR * user sets only _stksize. then _heapbase is set to Malloc(_stksize) * sp to _heapbase + _stksize and mallocs all happen from this heap. * * 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 #ifndef _COMPILER_H #include #endif #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 atleast this much mem */ /* on top */ #define MINKEEP (8L * 1024L) /* keep atleast this much mem */ /* * this little goodie is for emacs * edgar: note: it has grown two leading '_', * adjust in emacs/src/sysdep.c:start_of_data() * * __data_start removed, as that can be determined from the basepage. */ 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 stksiz.c */ /* set to heap base addr when _stksize == -1L || _initial_stack || When DA */ void *_heapbase; /* default sizeof stdio buffers */ size_t __DEFAULT_BUFSIZ__; /* .comm */ /* are we an app? */ short _app; /* are we on a split addr mem ST */ short _split_mem = 0; /* externs to pull in ident strings of all used libraries into the executable. if a library is not used, then the extern is satisfied by a dummy in the library */ asm(" .globl ___Ident_libg .globl ___Ident_curses .globl ___Ident_widget .globl ___Ident_gem .globl ___Ident_pml .globl ___Ident_gnulib "); static void _acc_main __PROTO((void)); static void _start1 __PROTO((BASEPAGE *bp)); static long parseargs __PROTO((BASEPAGE *bp)); static void setup_handlers __PROTO((void)); static void _start0 __PROTO((BASEPAGE *)); static void setup_handlers __PROTO((void)); __EXTERN void _main __PROTO((long, char **, char **)); __EXTERN void _init_signal __PROTO((void)); __EXTERN void _start __PROTO((BASEPAGE *)); #ifdef __GCRT0__ __EXTERN void monstartup __PROTO((void *lowpc, void *highpc)); __EXTERN void monitor __PROTO((void *lowpc, void *highpc, void *buffer, unsigned long bufsize, unsigned int nfunc)); __EXTERN void moncontrol __PROTO((long flag)); __EXTERN void _mcleanup __PROTO((void)); __EXTERN int profil __PROTO((void *buff, unsigned long bufsiz, unsigned long offset, int shift)); #endif /* * From: kbad@atari.UUCP (Ken Badertscher) * Newsgroups: comp.sys.atari.st * Subject: Am I a DA? (long) * ..... * I mentioned before that a DA's registers are garbage on startup, well, * that's not entirely true. When the DA gets control, register A0 always * points to its basepage. When a program is started by a GEMDOS Pexec(), * register A0 is always cleared. Using this fact, it is possible to * implement startup code which gets the basepage address from register A0 * if the code is launched as a DA, or at 4(sp) if the code is launched by * Pexec(). Since it knows how it was launched, it can also do the stack * setup required of a DA, otherwise it can use the stack pointer it gets. * */ /* * revert back to testing A0 (instead of SP) after controversy on the net. * packers will just have to fix themselves. * in addition to testing A0 check long A0@(36) (parents basepage). for an * acc this should be NULL. */ __asm__(" .text .even .globl __start __start: " #ifdef __MBASE__ " movl a0,a1 /* Find basepage, data seg */ cmpw #0,a1 jne 1f movl sp@(4),a1 1: movl a1@(16)," __MBASESTR__ " /* Set base to data seg + 32K */ subw #32768," __MBASESTR__ " " #define Base __MBASESTR__ "@(__base)" #define Heapbase __MBASESTR__ "@(__heapbase)" #define Stksize __MBASESTR__ "@(__stksize)" #else #define Base "__base" #define Heapbase "__heapbase" #define Stksize "__stksize" #endif " cmpw #0,a0 /* test acc or prog */ jeq __start0 /* br if prog */ tstl a0@(36) /* tst parent basepage pointer */ jne __start0 /* its a prog if != 0 */ /* its an acc, set up a stck+heap */ movl a0," Base " /* sto basepage */ tstl " Heapbase " /* setup _heapbase and sp */ jne 1f movl " Stksize ",d3 /* _heapbase not specified */ addql #3, d3 andl #0xfffffffc,d3 movl d3,sp@- /* _heapbase = Malloc(_stksize) */ movw #0x48,sp@- trap #1 addqw #6,sp movl d0," Heapbase " addl d3,d0 movl d0, sp /* sp = _heapbase + _stksize */ jra __acc_main 1: /* heap base specified */ movl " Heapbase ",sp /* setup sp */ addl " Stksize ",sp jra __acc_main"); /* acc main */ /* dont even think of */ /* dropping through */ static char *acc_argv[] = {"", (char *) 0}; /* no name and no arguments */ static void _acc_main(void) { _app = 0; /* this is an accessory */ _main(1L, acc_argv, acc_argv); /*NOTREACHED*/ } void _start0(bp) /* bp is passed at sp@(4) */ register BASEPAGE *bp; { /* temporarily set sp at hitpa (rounded),then call the real startup function. not doing this was blowing up the stack provided by gemdos on a 1040, because _start1() was pushing too many reggies on the stack _start1() will finally set sp to its final value and then do a mshrink(). */ asm volatile("movl %0,sp" ::"r"((long)bp->p_hitpa & ~3L)); _start1(bp); } static void _start1(bp) register BASEPAGE *bp __asm("a3"); { register long m __asm("d3"); register long freemem __asm("d4"); extern void etext(); /* fake-out if pcrel used */ _app = 1; /* its an application */ _base = bp; if(!__DEFAULT_BUFSIZ__) __DEFAULT_BUFSIZ__ = BUFSIZ; 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 + sizeof(BASEPAGE)); m = (m + 3L) & (~3L); /* freemem the amount of free mem accounting for MINFREE at top */ if((freemem = (long)bp->p_hitpa - (long)bp - 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 + m); _stksize = _initial_stack; } if((_stksize < -1L)) { _heapbase = (void *) ((long)bp + m); _stksize = -_stksize - 1; } if((!_initial_stack) && (_stksize >= -1L)) { /* malloc from Malloc first, then from own heap */ _split_mem = 1; } switch(_stksize) { case -1L: /* keep all but MINFREE */ _stksize = freemem; _heapbase = (void *) ((long)bp + 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 that much */ break; } /* 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 + m) > ((long)bp->p_hitpa - MINFREE)) goto notenough; /* set up the new stack to bp + m */ asm volatile("\ movl %0, sp | move base to sp addal %1, sp | add total bytes" : /* outputs */ : "g"(bp), "g"(m) /* inputs */ ); /* we dont tell gcc about clobbered reggies */ /* shrink the TPA - this is always correct, shared text or not -- hyc */ (void)Mshrink(bp, m); asm volatile("subl a6,a6"); /* clear link reg for gdb */ /* establish handlers, call the main routine */ setup_handlers(); #ifdef __GCRT0__ monstartup((void *)(bp->p_tbase), (void *)etext-1); #endif _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; /* 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; while (*from++); /* if launched from desktop -- fix up env * be careful while doing this, as the environment * may have a variable whose value is the empty * string. (make puts a MAKFLAGS=\0). The desktop * typically has "PATH=\0C:\\0\0", so to distinguish * the two cases, check for uppercase drive letter * followed by ":\\" */ #define ISDRV(x) (('A' <= (x)) && ((x) <= 'Z')) if ( (from[ -2 ] == '=') && (ISDRV(*from)) && (from[1] == ':') && (from[2] == '\\') ) { char *p = &from[-1]; /* typically "PATH=\0C:\\0\0" */ while(*from) *p++ = *from++; *p = '\0'; from++; } } *envp++ = (char *)0; count += 4; /* 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(void) { /* more stuff to come */ _init_signal(); } void __exit(status) long status; { #ifdef __GCRT0__ moncontrol(0L); _mcleanup(); #endif Pterm(status); }