home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Club Amiga de Montreal - CAM
/
CAM_CD_1.iso
/
files
/
522.lha
/
CpuBlit_v1.0
/
src
/
cpublit.c
next >
Wrap
C/C++ Source or Header
|
1991-06-09
|
15KB
|
515 lines
/****************************************************************************
*
* 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);
}