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