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