[comp.sys.atari.st] Correct use of _shell_p

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.