gert@nikhefh.UUCP (Gert Poletiek) (05/18/87)
In article <8705161656.AA19592@ucbvax.Berkeley.EDU> K538915@CZHRZU1A.BITNET writes: >(maybe one day Atari will decide on for what it actually is :-)). > > Simon Poole > K538915@CZHRZU1A.BITNET OK, here is the description of how _shell_p should be used, that is how at least one person at Atari wants it to be used. When writing a shell for the ST I was wondering if it would be possible to have a 'hook' into the shell such that one could invoke the shell from any child job *WITHOUT* starting a new shell. Unix users can now probably guess what I was aiming for: something like the 'system' call in Unix. In order to do this I needed an unused low memory global that would hold the address of the entry point in the shell. Since I knew that _shell_p was not used (and none of the books and documentation on the ST said it was 'reserved') I contacted Allan Pratt about this _shell_p variable. Here follows a part from the letter he sent me in reply <start quote> I talked with Landon about _shell_p: it was intended for one purpose, then never used for it [..] It should be a pointer to a procedure which acted like the Unix system() call: extern int (*shell_p)(); system ( command ) char *command; { errcode = (*shell_p)( command ); } Such a thing would be very handy from applications such as make. Just think: Emacs could execute the command "make >errlist", then visit the file errlist and even visit the files / go to the lines with errors! Without having to know where "make.prg" is, or even how "make" has been aliased within the shell! <end quote> So this is the way I implemented it, and I must say this works perfectly. Currently we have a development system with each component integrated into my shell using _shell_p. This developmentent system includes the shell, a C-shell like shell, with some 60 commands (Pipes and I/O redirection and lots more), and an editor (beating the hell out of every other editor on the ST, like searching at 0.5 Mega-bytes per second), and several utilities including a make. Both the editor and the make use the _shell_p interface of the shell to execute commands. I think that if everybody sticks to the rules as outlined by Atari, shells and user programs could easily be integrated. If needed, I will post source code explaining exactly how to use _shell_p in user programs. Gert Poletiek. (gert@nikhefh.uucp)
john@viper.Lynx.MN.ORG (John Stanley) (05/19/87)
In article <276@nikhefh.UUCP> gert@nikhefh.UUCP (Gert Poletiek) writes: > >I think that if everybody sticks to the rules as outlined by Atari, shells >and user programs could easily be integrated. > >If needed, I will post source code explaining exactly how to use _shell_p >in user programs. > Thanks for this information Gert. One "small" problem. We need a way of checking to see if this pointer is functional. The sample code you gave as part of your message should only be called if a command shell has been installed. We can't expect lots of people to write software that uses _shell_p unless they have some way of checking to see if it is functional or not. Is _shell_p initialized to anything at boot time? At re(warm)boot??? If not, this could be pose a serious problem/limitation... Aside to Neil Harris, if the current roms don't initialize _shell_p to something that is either (a) something safe to jump to, or (b) something that can be tested reliably, then would it be possible to get this added to the new roms? It would only require sticking a pointer to a known RTS instruction in there durring a warmboot and would make using this as a good functional enhancement to the ST that much easier... --- John Stanley (john@viper.UUCP) Software Consultant - DynaSoft Systems UUCP: ...{amdahl,ihnp4,rutgers}!{meccts,dayton}!viper!john
gert@nikhefh.UUCP (05/20/87)
In article <1019@viper.Lynx.MN.ORG> john@viper.UUCP (John Stanley) writes: >In article <276@nikhefh.UUCP> gert@nikhefh.UUCP (Gert Poletiek) writes: [..] > Thanks for this information Gert. One "small" problem. We need a way >of checking to see if this pointer is functional. The sample code you >gave as part of your message should only be called if a command shell >has been installed. We can't expect lots of people to write software >that uses _shell_p unless they have some way of checking to see if it >is functional or not. Is _shell_p initialized to anything at boot >time? At re(warm)boot??? If not, this could be pose a serious >problem/limitation... > [..] >--- >John Stanley (john@viper.UUCP) >Software Consultant - DynaSoft Systems >UUCP: ...{amdahl,ihnp4,rutgers}!{meccts,dayton}!viper!john If you have a standard system, i.e Tos in rom, any ST, with or without a harddisk, and GemBoot NOT installed then _shell_p will contain a long 0. This will not change during uptime. If you have system with (the current version of) GemBoot _shell_p will be made to point to a 'PATH' string. This use of _shell_p is not correct. The only program that uses _shell_p as intended by atari is the shell I wrote. (This remark is pretty useles for most of you, since I didn't market my shell yet, however the following description is valid for any program that uses _shell_p correctly). When my shell is started it installs a pointer to a function in the shell that will handle user program requests. When the shell exits, it will put the old value of _shell_p back, in order not to crash the program(s) that may eventually try to use _shell_p with my shell being installed. As a matter of fact the current version of GemBoot and my shell can be used together, as long as the shell is run after GemBoot. Finding out if the value _shell_p is correct should be done like this: (Code taken from a message from Allan Pratt @ atari.uucp, additions by me) *************** CODE EXAMPLE FOR USE OF _shell_p VARIABLE ***************** /* * Example of use of _shell_p variable. * * The value at _shell_p is the address of a procedure which takes a * string pointer as an argument, executes the string as if it were * typed at the shell prompt, and returns the exit status of the command. * * This code checks to see of the value at _shell_p is null (no shell * around) or points to the word "PATH" (adulterated by GEMBOOT). */ #include <osbind.h> main() { long oldstack; long *shell_p = (long *)0x4f6; long (*shell)(); long status; char buf[256]; oldstack = Super(0L); /* get into Super mode */ shell = *shell_p; /* get value of _shell_p */ Super(oldstack); /* get back to user mode */ /* validate _shell_p */ if (shell == 0L) { printf("No shell available to execute a command\n"); Pterm(-1); } /* * Code added by Gert Poletiek * * Test whether _shell_p has a value that is smaller than the * base page of your own program. * This has to be done differently for the different C compiler * system: * */ #ifdef ALCYON /* digital research C or alcyon C */ if ( (long)shell > (long)_base ) { printf ( "_shell_p has invalid value\n" ); Pterm ( -1 ); } #endif ALCYON #ifdef MWC /* mark williams C */ if ( (long)shell > (long)_start ) { printf ( "_shell_p has invalid value\n" ); Pterm ( -1 ); } #endif MWC #ifdef MEGAMAX /* megamax C */ if ( (long)shell > (long)_base ) { printf ( "_shell_p has invalid value\n" ); Pterm ( -1 ); } #endif MEGAMAX if (strncmp(shell,"PATH",4) == 0) { printf("_shell_p has been adulterated: it points to \"PATH\"\n"); Pterm(-1); } /* get a line from the keyboard using Cconrs() */ printf("Enter a command line for the shell to execute: "); buf[0] = 253; Cconrs(buf); buf[2+buf[1]] = '\0'; /* null-terminate the line */ /* execute the command */ status = (*shell)(&buf[2]); printf("Return status is %ld\n",status); Pterm0(); } Alternatively the following assembler code can be used from C (this version is for alcyon, but can be easily adapted) xdef _system * * * Get a command executed by the GP shell. * * (C) 1987 Gert Poletiek * * * * Calling syntax from a C program: * * system ( command ) * char *command * * and from assembly language * * pea command * jsr _system (or bsr) * addq.l #4,sp * * * Not that only the a3 register is used here and saved here. * All other registers are saved and restored by the shell. * The free stack must be at least 100 bytes (that's where the * the shell saves your registers). The shell will allocate it's * own stack on the fly. * * _system: move.l a3,-(sp) * save a3 move sr,d0 * test if we're in supervisor mode btst #13,d0 * in supervisore mode? bne sys_error * don't call this from supervisor mode clr.l -(sp) * get into supervisor mode move.w #$20,-(sp) trap #1 addq.l #6,sp * now we're in supervisor mode move.l d0,-(sp) * save old value of ssp movea.l $4f6,a3 * get function pointer move.l a3,d0 * move to d0 just to test it beq sys_nosh * ain't no use dereferencing a null pointer move.w #$20,-(sp) * get back into user mode trap #1 addq.l #6,sp move.l 8(sp),-(sp) * push our string argument jsr (a3) * call the shell addq.l #4,sp * restore stack move.l (sp)+,a3 * restore a3 rts * done: shell returnvalue is in d0 sys_nosh: move.w #$20,-(sp) * get back into user mode trap #1 addq.l #6,sp sys_error: move.w #-1,d0 * return general mishap move.l (sp)+,a3 * restore a3 rts end NOTE: This code is only useful if you have the GP-shell (sorry for the ad) Gert Poletiek.
t68@nikhefh.UUCP (05/20/87)
There is a little to be added to the proper use of _shell_p. I ran into this problem when building an editor that can also be run as a desk accessory, and doesn't need to run under a shell. In that case it has to test whether _shell_p is useable, and if not it should try to interprete the command itself, just being less powerful. The problem is that apon a warm reset the value of _shell_p is not restored to zero, so any program that runs afterwards, but not from the shell will think that the shell is there and kaboum..... There is no real solution to this problem, only a semi solution. The job looks for _shell_p at startup and it tests its validity, assuming that any shell program will lie in a lower piece of memory, so the value of _shell_p must be less than the basepage address of the utility that uses it. This is not foolproof but you got to work at it to cheat it ( from the mt shell its not difficult ). After this it doesn't check the validity anymore. To have _shell_p point at a 'rts' instruction after booting is not a good idea, because then there is no safe way of telling whether your command has been executed, except for putting 'magics' in d0 and looking whether they got changed. That doesn't work too well from a higher language. There are more variables that are not reset at a warm boot. Some are most annoying, like the drivemap. For RAM disks like K-RAM that look for the first available drive this means that they move up one drive after each reset. Jos Vermaseren t68@nikhefh.uucp
braner@batcomputer.tn.cornell.edu (braner) (05/21/87)
[] Gert's shell sounds very good. But meanwhile, can some hackers out there (or perhaps the shell authors!) tell us how to hook into the command line interpreters of the commonly used shells? (mCS, MWC, GULAM) - Moshe Braner
gert@nikhefh.UUCP (Gert Poletiek) (05/25/87)
In article <1083@batcomputer.tn.cornell.edu> braner@tcgould.tn.cornell.edu.UUCP (braner) writes: >[] > >Gert's shell sounds very good. But meanwhile, can some hackers out there >(or perhaps the shell authors!) tell us how to hook into the command line >interpreters of the commonly used shells? (mCS, MWC, GULAM) > >- Moshe Braner Sorry out of luck. None of the shells I know (except ofcourse my own) cannot be called from a child process. The MWC development system provides a 'system' call, but this merely starts a new shell, therefor using 'system' with MWC seems to take forever. My own shell is completely recursive to allow shell scrips to be run by the same instance of the shell that accepts keyboard commands. Only a slight deviation from complete recursiveness will make it impossible to implement the system call interface as my shell does. As for mCS: it can't do what you have in mind..... Gert Poletiek.