home *** CD-ROM | disk | FTP | other *** search
- /* :ts=8 bk=0
- *
- * proc.c: Illustration of how to create a full-fledged DOS process
- * without needing to LoadSeg() it first. Based on an idea
- * presented at BADGE (don't remember the name of the guy
- * who thought it up).
- *
- * Leo L. Schwab 8903.01
- */
- #include <exec/types.h>
- #include <libraries/dosextens.h>
-
- extern void *AllocMem(), *CreatePort(), *GetMsg(), *FindTask();
-
- extern LONG Open(), Output(), CreateProc();
-
-
- /*
- * Cursor manipulation strings.
- */
- char fwdcursor[] = "\033[C";
- char backcursor = '\b';
-
- /*
- * Buried deep in the AmigaDOS Technical Reference Manual, the structure of
- * of memory lists and SegLists are described. A SegList consists of a BPTR
- * to the next element in the list (NULL-terminated), followed by code.
- * The pointers point to the NextSeg field. The size of the node in bytes
- * is stored in the longword just before NextSeg field. However, the
- * size is only of real interest to UnLoadSeg(), which you won't be calling
- * in this case. So we don't need it.
- *
- * So the PhonySegList structure consists merely of a NextSeg field, which
- * is initialized to NULL (CreateProc() ignores this field, anyway), followed
- * by code to execute, which CreateProc() will jump to. The code we give it
- * to execute is an absolute jump to the real entry point, which you
- * initialize appropriately.
- */
- struct PhonySegList {
- BPTR psl_NextSeg; /* BPTR to next element in list */
- UWORD psl_JMP; /* A 68000 JMP abs.l instruction */
- LONG (*psl_EntryPoint)(); /* The address of the function */
- };
-
- /*
- * This is NOT the structure that will actually get passed to CreateProc().
- * AmigaDOS demands that everything, including SegLists, be on longword
- * boundaries. Short of compiler-dependent switches, the only way to
- * guarantee correct alignment is to AllocMem() a PhonySegList structure,
- * and copy this template into it. You should also remember to initialize
- * the psl_EntryPoint field (the argument to the JMP instruction) in the
- * allocated structure, for obvious reasons.
- */
- struct PhonySegList template = {
- NULL, /* No next element. */
- 0x4EF9, /* JMP abs.l */
- NULL /* Argument for JMP instruction */
- };
-
-
- /*
- * This is the routine that will be CreateProc()ed. Due to the global nature
- * of library base variables, any library routines this routine calls will
- * be using library base variables that, strictly speaking, belong to the
- * "parent" process. Despite the fact that it will work, this is
- * nevertheless a dangerous practice. In an ideal world, you would want to
- * convince the linker to resolve all library base pointer references to
- * your co-process's own copies. Lattice, on the other hand, due to its
- * #pragmas, may not have this problem (can someone confirm this?).
- */
- LONG
- coprocess ()
- {
- register int i, n;
- struct Process *me;
- struct Message *startupmsg;
- LONG doswin;
- char buf[256];
-
- #ifdef AZTEC_C
- #ifndef _LARGE_DATA
- /*
- * This gets to be a bloody nuisance.
- */
- geta4 ();
- #endif
- #endif
- /*
- * Startup messages are important. Not only can they provide
- * configuration information for the newly-created process (what
- * directory you're in, what size to make your window, etc.), but
- * they also serve to inform the program that started you that
- * you have finished executing. This is important, because the
- * parent program will want to know when it's okay to free the
- * resources it allocated to start you. In this example, we
- * grab the message so that we can reply it. The act of replying
- * the startup message will inform the parent that we're done.
- */
- me = FindTask (NULL);
- WaitPort (&me -> pr_MsgPort);
- startupmsg = GetMsg (&me -> pr_MsgPort);
-
- /*
- * Recall that, because a global DOSBase pointer has been initialized
- * by the parent, and because the stubs will reference it at the link
- * stage, we don't need to open dos.library here.
- */
- if (!(doswin = Open ("CON:0/0/320/100/Sub-Process", MODE_NEWFILE)))
- goto xit;
-
- /*
- * Say hello.
- */
- Write (doswin, "This is the child speaking.\n", 28L);
-
- /*
- * Print the value of FindTask(NULL) to prove we're a separate
- * task, and print some values in the Process structure to prove
- * we're a real process.
- *
- * (Another caveat: The stdio functions in this example (like
- * sprintf()) are being shared by both processes. Some stdio
- * routines utilize a set of global variables, access to which is
- * not arbitrated. Therefore, it is possible for the processes to
- * collide when calling stdio functions, causing Bad Things to
- * happen. Be aware of this when doing your own multiprogramming.
- * (I'm pretty sure sprintf() is safe.))
- */
- sprintf (buf, "I'm at 0x%lx\n", me);
- Write (doswin, buf, (LONG) strlen (buf));
-
- sprintf (buf, "My stack size appears to be\n%ld bytes.\n",
- me -> pr_StackSize);
- Write (doswin, buf, (LONG) strlen (buf));
-
- sprintf (buf, "pr_StackBase is 0x%lx\n", me -> pr_StackBase);
- Write (doswin, buf, (LONG) strlen (buf));
-
- /*
- * Make the cursor in the window zot back and forth, which will
- * happen concurrently with the same action in the CLI window,
- * proving beyond a shadow of a doubt that there really are two
- * separate programs running.
- */
- for (n = 20; n--; ) {
- for (i = 35; i--; )
- Write (doswin, fwdcursor, sizeof (fwdcursor) - 1L);
- for (i = 35; i--; )
- Write (doswin, &backcursor, 1L);
- }
-
- /*
- * We've proved our existence. We now reply our startup message,
- * and exit. Note the use of Forbid() without a corresponding
- * Permit(). This prevents this process from being switched out
- * before exiting completely. When this process is totally gone,
- * Exec will notice, and do the equivalent of a Permit() internally.
- */
- Close (doswin); /* Get ridda da window. */
- xit:
- Forbid ();
- ReplyMsg (startupmsg);
- return (0);
- }
-
-
- /*
- * Here is the main program. Its job will be to create the support
- * structures to fire off the sub-process, print out some relevant
- * information, then while away the time until the child exits.
- */
- main ()
- {
- register int i;
- struct Process *me;
- struct MsgPort *replyport = NULL;
- struct Message startupmsg;
- struct PhonySegList *fakelist = NULL;
- LONG child,
- priority,
- term;
-
- /*
- * Get a handle on the current output stream so that we can Write()
- * to it. Note that this implies this program really ought to be
- * run from a CLI.
- */
- if (!(term = Output ()))
- goto xit;
-
- /*
- * Create a message port for the startup message to be replied to.
- */
- if (!(replyport = CreatePort (NULL, NULL)))
- goto xit;
-
- /*
- * Allocate a PhonySegList structure.
- */
- if (!(fakelist = AllocMem ((LONG) sizeof (*fakelist), NULL)))
- goto xit;
-
- /*
- * Copy the template into the allocated memory, and set the entry
- * point to the sub-process.
- */
- CopyMem (&template, fakelist, (LONG) sizeof (template));
- fakelist -> psl_EntryPoint = coprocess;
-
- /*
- * Initialize the startup message. There's nothing really amazing
- * happening here. Its sole purpose in life is to be replied.
- */
- startupmsg.mn_Node.ln_Type = NT_MESSAGE;
- startupmsg.mn_Node.ln_Pri = 0;
- startupmsg.mn_ReplyPort = replyport;
-
- /*
- * Find ourselves. Discover what priority we're running at, so that
- * we can start the sub-process at the same priority.
- */
- me = FindTask (NULL);
- priority = me -> pr_Task.tc_Node.ln_Pri;
-
- /*
- * Print some information about ourselves.
- */
- puts ("This is the parent speaking.");
- printf ("I'm at 0x%lx\n", me);
- printf ("My stack size appears to be\n%ld bytes.\n",
- me -> pr_StackSize);
- printf ("pr_StackBase is 0x%lx\n", me -> pr_StackBase);
-
- /*
- * Now, create and start the sub-process. The current release of
- * AmigaDOS CreateProc() does nothing special with SegLists.
- * All it uses it for is to find the first bit of code to execute.
- * By passing it 'fakelist', we're essentially feeding it a JMP
- * instruction which jumps to the real start of our sub-process.
- * (Note that we have to convert 'fakelist' into a BPTR.)
- * Thus, we get a full-fledged DOS process, which we can do things
- * with, and we didn't have to LoadSeg() it.
- */
- if (!(child = CreateProc ("Sub-Process",
- priority,
- (LONG) fakelist >> 2,
- 2048L)))
- goto xit;
-
- /*
- * Send the startup message. This will get the sub-process doing
- * its thing.
- * (Note that CreateProc() returns a pointer, not to the process
- * structure, but to the pr_MsgPort structure *within* the process
- * structure.)
- */
- PutMsg (child, &startupmsg);
-
- /*
- * Make our cursor fly back and forth, which hopefully will happen
- * concurrently with the sub-process's activity. We continue to
- * do this until the sub-process replies its message, making
- * GetMsg() return a non-NULL value. Since we "know" what's arriving
- * at the replyport, we can safely throw the result from GetMsg()
- * away.
- */
- while (!GetMsg (replyport)) {
- for (i = 64; i--; )
- Write (term, fwdcursor, sizeof (fwdcursor) - 1L);
- for (i = 64; i--; )
- Write (term, &backcursor, 1L);
- }
-
- /*
- * At this point, the sub-process has completely exited. We may
- * now safely deallocate all the support structures, and exit.
- */
- puts ("Child terminated.");
- xit:
- if (fakelist) FreeMem (fakelist, (LONG) sizeof (*fakelist));
- if (replyport) DeletePort (replyport);
- }
-