[net.micro.amiga] RUNNING LOADED CODE.... BASIS FOR SHELL PROGRAMS

robp@amiga.UUCP (Robert A. Peck) (03/25/86)

RUNNING LOADED CODE ...... < SHELL HELP > < SHELL HELP > < SHELL HELP > 

There has been some discussion on the networks about making shells to
replace or supplement the AmigaDOS CLI.  The primary problem has been
an inability to obtain a return-value when one "Execute()"s a function, 
since Execute() does not return the value that the function itself
returned.

I've consulted with the developer of AmigaDOS, and the following information
is the result of that discussion.  To get a return value from a disk-resident
function, you can:

	1.  bptr = LoadSeg("testprogram");

	2.  LoadSeg returns a bcpl pointer... convert it to a real address
	    by (bptr << 2).   This provides the address of the link field
	    of the segment that points to the next segment.  

	3.  Add 4 to this value to obtain the starting address of the 
	    actual code:
			startaddr = (int)(bptr<<2) + 4);

	4.  JSR startaddr, with register A0 pointing to the first character
	     of the parameter string to be passed, and D0 containing the
	     count of the number of valid characters in the string
	     (including the terminating null at the end of the string).

	5.  UnloadSeg when the slave program is done.

The programs that follow actually implement this operation, and can be
used as a guide for creating shell programs if you wish.

***************************************************************************
LIMITATIONS:

	There are several limitations to this approach....   

	1.  Existing AmigaDOS BCPL-based functions are highly
	    UNLIKELY to run properly.... they must be run with
	    a process with a CLI attached.  Thus functions such
	    as COPY or EXECUTE or RUN, to name a few (C: commands)
	    will probably not function correctly, if at all.
	    You will probably have to (want to?) create your
	    own custom versions of these programs for use under
	    your shell.

	2.  The programs run on YOUR stack, so your stack must
	    be large enough to accommodate whatever program you
	    load this way.

	3.  A program that simply ends by falling off the end
	    of the world or issues a return(value) will result
	    in the return of a value of 0.  A program must use
	    the exit(value) function when linked either with
	    Lstartup.obj or Astartup.obj in order to actually
	    sense that value you want to pass back to the caller.

***************************************************************************

There are three files that are a part of this message.  

    test.c - a Hello World program that sets an exit value.

	compile test.c with "makesimple", (link with Lstartup.obj).

    runit.c - implements the LoadSeg, call, UnloadSeg sequence
	specified above.  Calls a little bit of assembly code
	help that actually loads the registers as described.

	compile runit.c with "make", then 
			link with Lstartup.obj+runloaded.obj

    runloaded.asm - the assembly code interface.  You'll need the
	amiga assembler or something like it to do this part.



You can test the function by typing:

	RUN test

AmigaDOS runs it then says that it failed, returncode = 88, confirming
that AmigaDOS knows how to retrieve the returncode.

Then type:

	runit test foo


SUGGESTIONS:

	For smaller code, I've compiled with makesimple, modified
	so that -v option (disable stack checking) is used for lc2,
	and linked with Astartup.obj and amiga.lib instead of
	Lstartup.obj and lc.lib+amiga.lib.   I've noticed
	intermittent problems in the stack checking, and havent
	been able to chase it.  This alternate means of linking
	solves both the size and the stack difficulties.
 

IMPROVEMENTS:

	This version passes only one parameter as the parameterlist.
	For the sake of shell implementations, the entire command
	line would have to be passed to the program, and it, in turn,
	would parse the string into individual parameters.  Rather
	than concentrate on this aspect, I felt it more important
	to distribute the access method, and let someone else handle
	the shell mechanics.


 
I know the question about return values has surfaced several times, and
I hope this provides enough information for those of you who have a need
to perform this function.  GOOD LUCK!

robp. 

DISCLAIMER:

        This program is provided as a service to the programmer
        community to demonstrate one or more features of the Amiga
        personal computer.  These code samples may be freely used
        for commercial or noncommercial purposes.

        Commodore Electronics, Ltd ("Commodore") makes no
        warranties, either expressed or implied, with respect
        to the program described herein, its quality, performance,
        merchantability, or fitness for any particular purpose.
        This program is provided "as is" and the entire risk
        as to its quality and performance is with the user.
        Should the program prove defective following its
        purchase, the user (and not the creator of the program,
        Commodore, their distributors or their retailers)
        assumes the entire cost of all consequent damages.  In
        no event will Commodore be liable for direct, indirect,
        incidental or consequential damages resulting from any
        defect in the program even if it has been advised of the
        possibility of such damages.  Some laws do not allow
        the exclusion or limitation of implied warranties or
        liabilities for incidental or consequential damages,
        so the above limitation or exclusion may not apply.


/* test.c ************************************************************ */
/*                            can be "RUN" on its own if desired       */
main(argc, argv)
   int argc;
   char *argv[];
{
   printf("\nHello, World\n");
   printf("\nMeans I did run the right program!!!!\n");
   exit(88);
}


/* runit.c ************************************************************ */
/* 									*/
/* 									*/
/* A program that demonstrates how to load a program, run it, and 
 * retrieve its return value (exit-value that is).  	
 */
/* author:  Rob Peck   3/14/86                 */
/* system software version: V1.1               */

#include "exec/types.h"
#include "exec/nodes.h"
#include "exec/lists.h"
#include "exec/libraries.h"
#include "exec/ports.h"
#include "exec/interrupts.h"
#include "exec/io.h"
#include "exec/memory.h"
#include "libraries/dos.h"
#include "libraries/dosextens.h"

#include "workbench/startup.h"

#define PRIORITY 0
#define STACKSIZE 5000

extern struct Message *GetMsg();
extern int LoadSeg();
extern struct MsgPort *CreateProc();

/* <RunIt command parameterlist> */

main(argc, argv)
	int argc;
	char *argv[];
{
	char *command;
	char *parameterlist;
	int littleSeg, linkseg; 
	int entrypoint, returnvalue;	
	char buffer[256];
	char *buf;

	/* assume run from CLI only for now */
	if(argc < 2)
	{
		printf("\nCommand format:\n");
		printf("runit command \"parameter_string\" \n");
		exit(0);
	}
	command = argv[1];
	parameterlist = argv[2];
	
/* LOAD THE PROGRAM TO BE STARTED FROM MAIN ****************************** */

	littleSeg = LoadSeg(command);
	if(littleSeg == 0) 
	{
		printf("\n%ls not found",command);
		exit(999);
	}
	/* Actually littleSeg is a BPTR, but the int declaration
	 * keeps the compiler happy and we don't use the
	 * value ourselves anyhow... just pass it on.
	 *
	 * Change a BPTR value into a real address value;
	 * provides the address at which the link to the next
	 * segment in the loaded segment list is contained.
	 */
	linkseg = (littleSeg << 2);

	/* The actual entry point to the loaded code is contained
	 * at the first longword following the segment link... 
	 * calculate this value 
	 */
	entrypoint = linkseg+4; 

	/* Now call the ASSEMBLY code magic that will call the
	 * loaded routine as a subroutine and return a value.
	 * We don't have any idea at all about which registers
	 * this routine uses, so we'll have to save them all.
	 * See runloaded.asm for details.
	 */

	printf("\nNow going to start routine named:  %ls",command);
	printf("\nAnd passing it parameter string: %ls",parameterlist);

	returnvalue = 
	    RunLoaded( entrypoint, parameterlist, strlen(parameterlist)+1 ); 

	        /*  JSR entrypoint, with A0 parmlist, having D0 valid bytes */
		   
	printf("\nValue returned from function %ls was %ld,\n",
					     argv[1],returnvalue); 
	UnLoadSeg(littleSeg);

	printf("\nIt unloaded the segment!");

}	/* end of main */

strlen( s )
register char *s;
{
    register i = 0;

    while( *s++ ) i++;

    return(i);
}


* ********* runloaded.asm ************************************************
*	RunLoaded(routineaddress, parameterstring, countofvalidchars);
*
	xdef 	_RunLoaded

_RunLoaded:
		movem.l D1-D7/A0-A6,-(SP)
		move.l  56+4(sp),A1  ;address of routine
		move.l  56+8(sp),A0  ;address of parameter string 
		move.l	56+12(sp),D0 ;count of valid characters in parameter
	
		jsr	(A1) 	  ;Perform the function
		movem.l	(SP)+,D1-D7/A0-A6
		rts
	end