/**************************************************************************** * * 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); }