home *** CD-ROM | disk | FTP | other *** search
- /****************************************************************************
- *
- * CPUBLIT.C
- *
- * (C) Copyright Eddy Carroll, 1991. Freely distributable.
- *
- * CpuBlit replaces the BltBitMap function in graphics.library with
- * a version that uses the CPU where practical. This is up to 2.8
- * times faster on a 68030 system.
- *
- * This module installs the new blit routine, handles parsing the
- * command line options etc. Scroll.s does the actual blitting.
- *
- ***************************************************************************/
-
- #define DEBUG 0 /* If 1, then include debugging code */
-
- #ifndef LATTICE_50
- #include "system.h"
- typedef void (*__fptr)(); /* The sort of thing returned by SetFunction */
- #endif
-
- #include "scroll.h"
-
- #define YES 1
- #define NO 0
-
- #define NAME "CpuBlit V1.00"
- #define PORTNAME NAME
- #define SIGNON NAME " \251 1991 Eddy Carroll.\n"
- #define DATE "Apr 1991"
-
- #define print(s) Write(Output(), s, strlen(s))
- #define BltBitMap_LVO (-30) /* Offset of BltBitMap in graphics.library */
-
- char HelpMsg[] =
- NAME " \251 Eddy Carroll, " DATE ". Replaces blitter with 68020/68030.\n"
- "Usage: CpuBlit {options} | {keywords}\n"
- "\n"
- "Options:\n"
- " -a Always use CPU to do blits (default setting)\n"
- " -1 Use CPU for blits unless another task is ready to run\n"
- " -2 Use CPU for blits unless more than one task is ready to run\n"
- " -b Use CPU even if bitmap isn't initialised correctly\n"
- " -o Use CPU only when a single bitmap is involved\n"
- " -s Use CPU for blits unless a blit is already in progress\n"
- " -pN Ignore tasks with priority < N (default setting is 0)\n"
- " -q Remove CpuBlit from the system\n"
- "\n"
- "Keywords: BLITMODE=[ALWAYS|ONE|TWO|SHARE] BROKEN SINGLE MINTASKPRI=n QUIT\n"
- "\n"
- "See the documentation for details about starting CpuBlit from Workbench.\n";
-
- /****************************************************************************
- *
- * Globals
- *
- ***************************************************************************/
-
- struct Gfxbase *GfxBase;
- struct IntuitionBase *IntuitionBase;
- struct IconBase *IconBase;
-
- /*
- * Valid modes of operation for BlitMode
- */
- #define BLIT_ALWAYS 0 /* Always use CPU for blits */
- #define BLIT_ONE 1 /* Use CPU unless another task is ready to run */
- #define BLIT_TWO 2 /* Use CPU unless more than one task is ready */
- #define BLIT_SHARE 3 /* Use CPU unless CPU is already doing a blit */
-
- /*
- * This array corresponds to the above modes
- */
- void (*BlitFuncs[])() = { StartBlit, Friend1, Friend2, ShareBlit };
-
- /*
- * All the settings that can be set by the program
- */
- struct Settings {
- long BlitMode; /* Current mode of operation for blits */
- long OnlySingle; /* True if restricting blits to 1 bmap */
- long Broken; /* True if handling broken software */
- BYTE MinTaskPri; /* All tasks less than this are ignored */
- } Settings = {
- BLIT_ALWAYS, NO, NO, 0
- };
-
- /*
- * Commands that we can send in a message
- */
- #define MSG_GETVARS 0 /* Get copy of current CpuBlit settings */
- #define MSG_SETVARS 1 /* Update CpuBlit settings */
- #define MSG_QUIT 2 /* Remove background copy of CpuBlit */
-
- struct MyMsg {
- struct Message msg; /* Standard message structure */
- struct Settings *vars; /* Settings used by CpuBlit */
- int command; /* Requested operation */
- int result; /* True if command completed okay */
- } MyMsg, *msg;
-
- /*
- * Return codes passed back in result
- */
- #define MSG_OKAY 0 /* Message was handled correctly */
- #define MSG_REMOVED 1 /* CpuBlit was removed safely */
- #define MSG_FAILED 2 /* CpuBlit couldn't be removed */
-
- /*
- * Scalar variables
- */
- struct MsgPort *LocalPort; /* Local port for returned messages */
- long QuitFlag; /* True if user asks CpuBlit to quit */
- long FromWorkbench; /* True if started from Workbench */
- long AlreadyRunning; /* True if CpuBlit already installed */
-
- /****************************************************************************
- *
- * myexit(err)
- *
- * Performs a small amount of cleanup and then exits to AmigaDos.
- * Principally, handles cleaning up if we were run from Workbench.
- *
- ***************************************************************************/
-
- void myexit(err)
- {
- if (LocalPort)
- DeletePort(LocalPort);
-
- exit(err);
- }
-
- /****************************************************************************
- *
- * SetVars(settings)
- *
- * Sets the various CpuBlit flags according to the values in the
- * supplied Settings structure.
- *
- ***************************************************************************/
-
- void SetVars(struct Settings *settings)
- {
- BlitFunc = BlitFuncs[settings->BlitMode];
- OnlySingle = settings->OnlySingle;
- Broken = settings->Broken;
- MinTaskPri = settings->MinTaskPri;
- }
-
- /****************************************************************************
- *
- * ParseOption()
- *
- * Parses an option string, setting the appropriate field in the
- * master Settings structure. This routine handles both Unix-style
- * -opts and also ReadArgs/ToolTypes keywords. Returns true if
- * the option string made sense, false otherwise.
- *
- ***************************************************************************/
-
- int ParseOption(char *opt)
- {
- #define MATCHSTR(s1,s2) (!strnicmp(s1, s2, sizeof(s2)-1))
-
- if MATCHSTR(opt, "-a") {
- Settings.BlitMode = BLIT_ALWAYS;
- Settings.OnlySingle = NO;
- Settings.Broken = NO;
- }
- else if MATCHSTR(opt, "-1") Settings.BlitMode = BLIT_ONE;
- else if MATCHSTR(opt, "-2") Settings.BlitMode = BLIT_TWO;
- else if MATCHSTR(opt, "-s") Settings.BlitMode = BLIT_SHARE;
- else if MATCHSTR(opt, "-b") Settings.Broken = YES;
- else if MATCHSTR(opt, "-o") Settings.OnlySingle = YES;
- else if MATCHSTR(opt, "-q") QuitFlag = YES;
- else if (MATCHSTR(opt, "-p") && opt[2])
- Settings.MinTaskPri = atoi(opt+2);
- else if MATCHSTR(opt, "BLITMODE") {
- char *p = opt + 8;
- if (*p++ && *p) {
- if MATCHSTR(p, "ALWAYS") Settings.BlitMode = BLIT_ALWAYS;
- else if MATCHSTR(p, "ONE") Settings.BlitMode = BLIT_ONE;
- else if MATCHSTR(p, "TWO") Settings.BlitMode = BLIT_TWO;
- else if MATCHSTR(p, "SHARE") Settings.BlitMode = BLIT_SHARE;
- } else return (0);
- }
- else if MATCHSTR(opt, "BROKEN") {
- char *p = opt + 6;
- if (*p++ && MATCHSTR(p, "NO")) Settings.Broken = NO;
- else Settings.Broken = YES;
- }
- else if MATCHSTR(opt, "SINGLE") {
- char *p = opt + 6;
- if (*p++ && MATCHSTR(p, "NO")) Settings.OnlySingle = NO;
- else Settings.OnlySingle = YES;
- }
- else if MATCHSTR(opt, "MINTASKPRI") {
- char *p = opt + 10;
- if (*p++ && *p) Settings.MinTaskPri = atoi(p);
- else return (0);
- }
- else if MATCHSTR(opt, "QUIT") QuitFlag = YES;
- else return (0);
-
- return (1);
- }
-
- /****************************************************************************
- *
- * MyFindPort(name)
- *
- * Replacement for the FindPort() in exec.library. Under 1.3, FindPort()
- * will cause Enforcer hits since FFS partitions create public ports
- * that have no name (this is a no-no), and FindPort() doesn't check
- * for null names.
- *
- * Even though this isn't really CpuBlit's problem, I had quite a few
- * reports of CpuBlit causing Enforcer hits which turned out to be
- * because of this, so I've added this workaround to keep people
- * happy.
- *
- * Commodore made FindPort() (actually FindName()) a bit more robust
- * under Kickstart 2.0, so in that case we can safely use the standard
- * routine.
- *
- ***************************************************************************/
-
- struct MsgPort *MyFindPort(char *name)
- {
- struct Node *nd;
- extern struct ExecBase *SysBase;
-
- if (SysBase->LibNode.lib_Version >= 36)
- return (FindPort(name));
-
- Forbid();
- for (nd = SysBase->PortList.lh_Head; nd; nd = nd->ln_Succ)
- if (nd->ln_Name && !strcmp(nd->ln_Name, name))
- break;
- Permit();
- return (struct MsgPort *)nd;
- }
-
-
- /****************************************************************************
- *
- * mainloop()
- *
- * This is the main event loop. It sits waiting for a message from
- * other invocations of CpuBlit, which tell it to either change the
- * current settings or to remove itself.
- *
- ***************************************************************************/
-
- void mainloop(void)
- {
- struct MsgPort *MyPort;
- int installed = 1;
- __fptr *BltBitMapPtr = (__fptr *)BltBitMapAddress;
-
- /*
- * We have to create our rendezvous port here rather than in the
- * mainline, since the message port depends on task information etc.
- * This is not altogether satisfactory since if it fails, there is
- * no way to tell the user (as a background task, we have no stdin
- * or stdout). But since port creation is unlikely to fail anyway,
- * it's not a big problem.
- */
- MyPort = CreatePort(PORTNAME, 0);
- if (!MyPort)
- return;
-
- /*
- * Now have to open graphics.library, so that we can add in our
- * new patch. As above, if this fails there is no easy way to
- * tell the user. However, at least it won't crash the system.
- */
- GfxBase = OpenLibrary("graphics.library", 0);
- if (!GfxBase)
- return;
-
- *BltBitMapPtr = SetFunction(GfxBase, BltBitMap_LVO, NewBltBitMap);
-
- /*
- * Now wait a message from another copy of CpuBlit. This will
- * either contain an updated command line argument or else a
- * request to quit.
- */
- do {
- __fptr oldptr;
-
- WaitPort(MyPort);
- while ((msg = (struct MyMsg *)GetMsg(MyPort)) != NULL) {
- switch (msg->command) {
-
- case MSG_GETVARS:
- memcpy(msg->vars, &Settings, sizeof(Settings));
- break;
-
- case MSG_SETVARS:
- memcpy(&Settings, msg->vars, sizeof(Settings));
- SetVars(&Settings);
- break;
-
- case MSG_QUIT:
- /*
- * Try and remove ourselves. We have to surround this
- * with Forbid() to make sure that no other tasks manage
- * to call BltBitMap() in the case where we restore the
- * original vector and then realise that its current
- * replacement actually pointed to something other than
- * CpuBlit.
- */
- Forbid();
- oldptr = SetFunction(GfxBase, BltBitMap_LVO, *BltBitMapPtr);
- if (oldptr == NewBltBitMap) {
- installed = 0;
- msg->result = MSG_REMOVED;
- } else {
- SetFunction(GfxBase, BltBitMap_LVO, oldptr);
- msg->result = MSG_FAILED;
- }
- Permit();
- }
- ReplyMsg(msg);
- }
- } while (installed);
-
- /*
- * Now our patch has been removed, it only remains to free up the
- * code. It is possible that someone is still in our blitter code.
- * We can determine this fairly safely by looking at UsageCount;
- * if this is -1, nobody is in our code. Otherwise, we wait until
- * it is -1 (delaying for a little while inbetween to give programs
- * a chance to run).
- *
- * We also set the blitter test function to ExitBlit, so that if
- * someone does slip through our test and end up inside our code,
- * they will get rerouted back to the normal blitter code almost
- * immediately.
- */
- DeletePort(MyPort);
- BlitFunc = ExitBlit;
-
- while (UsageCount != -1)
- Delay(10); /* Wait 0.2 seconds */
-
- /*
- * Now we're completely finished so we can close the libraries
- * and exit.
- */
- CloseLibrary(GfxBase);
- }
-
-
- /****************************************************************************
- *
- * Mainline
- *
- ***************************************************************************/
-
- void main(int argc, char **argv)
- {
- struct MsgPort *BlitPort;
- int i;
-
- FromWorkbench = (argc == 0);
-
- /*
- * Now see if CpuBlit is already running in
- * the background. If it is, then get a copy of the settings it
- * is currently using.
- */
- BlitPort = MyFindPort(PORTNAME);
- if (BlitPort) {
- /*
- * The new blit routine has already been installed. So, send
- * it a message giving the command line options (if any)
- * to the remote routine, telling it to update its own options.
- */
- AlreadyRunning = YES;
- LocalPort = CreatePort(NULL, 0);
- if (!LocalPort) {
- if (!FromWorkbench)
- print("CpuBlit: couldn't create local message port.\n");
- myexit(10);
- }
- MyMsg.msg.mn_ReplyPort = LocalPort;
- MyMsg.command = MSG_GETVARS;
- MyMsg.vars = &Settings;
- PutMsg(BlitPort, &MyMsg);
- WaitPort(LocalPort);
- GetMsg(LocalPort);
- }
- /*
- * Now parse the command line options, modifying our local copy
- * of Settings accordingly.
- */
- if (FromWorkbench) {
- extern struct WBStartup *WBenchMsg;
- struct WBArg *wbarg = WBenchMsg->sm_ArgList;
-
- IconBase = (struct IconBase *)OpenLibrary("icon.library", 0);
- if (!IconBase)
- myexit(5);
-
- /*
- * Now walk down all the icons we've been given (probably
- * just our own tool icon) and parse the arguments present
- * in each one.
- */
- for (i = 0; i < WBenchMsg->sm_NumArgs; i++, wbarg++) {
- struct DiskObject *dobj;
- char **tooltypes;
- BPTR olddir;
-
- if (wbarg->wa_Lock && *wbarg->wa_Name) {
- olddir = CurrentDir(wbarg->wa_Lock);
- if (dobj = GetDiskObject(wbarg->wa_Name)) {
- for (tooltypes = dobj->do_ToolTypes;
- *tooltypes; tooltypes++)
- ParseOption(*tooltypes);
- }
- FreeDiskObject(dobj);
- CurrentDir(olddir);
- }
- }
- CloseLibrary(IconBase);
- } else {
- /*
- * Plain jane CLI startup
- */
- #if DEBUG
- if (argv[1][0] == '!') {
- static buf[1000];
- sprintf(buf,
- "Blitmode = %d\n"
- "OnlySingle = %d\n"
- "Broken = %d\n"
- "MinTaskPri = %d\n",
- Settings.BlitMode, Settings.OnlySingle,
- Settings.Broken, Settings.MinTaskPri);
- print(buf);
- exit(5);
- }
- #endif
- for (i = 1; i < argc; i++) {
- if (!ParseOption(argv[i])) {
- print(HelpMsg);
- myexit(5);
- }
- }
- }
-
- /*
- * Now we either send the options to the remote copy of CpuBlit
- * or install ourselves in the background.
- */
- if (AlreadyRunning) {
- if (QuitFlag)
- MyMsg.command = MSG_QUIT;
- else
- MyMsg.command = MSG_SETVARS;
- PutMsg(BlitPort, &MyMsg);
- WaitPort(LocalPort);
- GetMsg(LocalPort);
- if (FromWorkbench && MyMsg.result == MSG_FAILED) {
- IntuitionBase = (struct IntuitionBase *)
- OpenLibrary("intuition.library", 0);
- if (IntuitionBase) {
- DisplayBeep(0); /* Flash all screens -- pretty rude */
- CloseLibrary(IntuitionBase);
- }
- }
- if (!FromWorkbench) {
- if (MyMsg.result == MSG_REMOVED)
- print("CpuBlit removed successfully.\n");
- else if (MyMsg.result == MSG_FAILED) {
- print(
- "Couldn't remove CpuBlit; someone else has patched BltBitMap. Please remove\n"
- "any other utilities you have installed and then try again.\n");
- myexit(5);
- }
- }
- myexit(0);
- }
-
- /*
- * This is the first time we are being run. If we were run from
- * the CLI, detach ourselves (and allow the current process to
- * return to the CLI immediately). If we were run from Workbench,
- * then just call the message handling code directly and wait
- * until another copy of CpuBlit asks us to return.
- */
- if (QuitFlag) {
- if (!FromWorkbench)
- print("CpuBlit hasn't been installed yet.\n");
- myexit(5);
- }
-
- SetVars(&Settings);
- if (FromWorkbench)
- mainloop();
- else {
- if (!res(NAME, 5, mainloop, 4000)) {
- print("Couldn't spawn background CpuBlit task.\n");
- myexit(10);
- }
- print(SIGNON);
- }
- myexit(0);
- }
-