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 >
C/C++ Source or Header  |  1991-06-09  |  15KB  |  515 lines

  1. /****************************************************************************
  2.  *
  3.  *        CPUBLIT.C
  4.  *
  5.  *        (C) Copyright Eddy Carroll, 1991. Freely distributable.
  6.  * 
  7.  *        CpuBlit replaces the BltBitMap function in graphics.library with
  8.  *        a version that uses the CPU where practical. This is up to 2.8
  9.  *        times faster on a 68030 system.
  10.  *
  11.  *        This module installs the new blit routine, handles parsing the
  12.  *        command line options etc. Scroll.s does the actual blitting.
  13.  *
  14.  ***************************************************************************/
  15.  
  16. #define DEBUG    0            /* If 1, then include debugging code            */
  17.  
  18. #ifndef LATTICE_50
  19. #include "system.h"
  20. typedef void (*__fptr)();    /* The sort of thing returned by SetFunction    */
  21. #endif
  22.  
  23. #include "scroll.h"
  24.  
  25. #define YES        1
  26. #define NO        0
  27.  
  28. #define NAME            "CpuBlit V1.00"
  29. #define PORTNAME        NAME
  30. #define SIGNON            NAME " \251 1991 Eddy Carroll.\n"
  31. #define DATE            "Apr 1991"
  32.  
  33. #define print(s)        Write(Output(), s, strlen(s))
  34. #define BltBitMap_LVO    (-30)    /* Offset of BltBitMap in graphics.library    */
  35.  
  36. char HelpMsg[] =
  37. NAME " \251 Eddy Carroll, " DATE ". Replaces blitter with 68020/68030.\n"
  38. "Usage: CpuBlit {options} | {keywords}\n"
  39. "\n"
  40. "Options:\n"
  41. "    -a    Always use CPU to do blits (default setting)\n"
  42. "    -1    Use CPU for blits unless another task is ready to run\n"
  43. "    -2    Use CPU for blits unless more than one task is ready to run\n"
  44. "    -b    Use CPU even if bitmap isn't initialised correctly\n"
  45. "    -o    Use CPU only when a single bitmap is involved\n"
  46. "    -s    Use CPU for blits unless a blit is already in progress\n"
  47. "    -pN   Ignore tasks with priority < N (default setting is 0)\n"
  48. "    -q    Remove CpuBlit from the system\n"
  49. "\n"
  50. "Keywords: BLITMODE=[ALWAYS|ONE|TWO|SHARE] BROKEN SINGLE MINTASKPRI=n QUIT\n"
  51. "\n"
  52. "See the documentation for details about starting CpuBlit from Workbench.\n";
  53.  
  54. /****************************************************************************
  55.  *
  56.  *        Globals
  57.  * 
  58.  ***************************************************************************/
  59.  
  60. struct Gfxbase       *GfxBase;
  61. struct IntuitionBase *IntuitionBase;
  62. struct IconBase         *IconBase;
  63.  
  64. /*
  65.  *        Valid modes of operation for BlitMode
  66.  */
  67. #define BLIT_ALWAYS        0    /* Always use CPU for blits                        */
  68. #define BLIT_ONE        1    /* Use CPU unless another task is ready to run    */
  69. #define BLIT_TWO        2    /* Use CPU unless more than one task is ready    */
  70. #define BLIT_SHARE        3    /* Use CPU unless CPU is already doing a blit    */
  71.  
  72. /*
  73.  *        This array corresponds to the above modes
  74.  */
  75. void (*BlitFuncs[])() = { StartBlit, Friend1, Friend2, ShareBlit };
  76.  
  77. /*
  78.  *        All the settings that can be set by the program
  79.  */
  80. struct Settings {
  81.     long    BlitMode;        /* Current mode of operation for blits  */
  82.     long    OnlySingle;        /* True if restricting blits to 1 bmap    */
  83.     long    Broken;            /* True if handling broken software        */
  84.     BYTE    MinTaskPri;        /* All tasks less than this are ignored    */
  85. } Settings = {
  86.     BLIT_ALWAYS, NO, NO, 0
  87. };
  88.  
  89. /*
  90.  *        Commands that we can send in a message
  91.  */
  92. #define MSG_GETVARS        0    /* Get copy of current CpuBlit settings    */
  93. #define MSG_SETVARS        1    /* Update CpuBlit settings                */
  94. #define MSG_QUIT        2    /* Remove background copy of CpuBlit    */
  95.  
  96. struct MyMsg {
  97.     struct    Message msg;    /* Standard message structure            */
  98.     struct    Settings *vars;    /* Settings used by CpuBlit                */
  99.     int        command;        /* Requested operation                    */
  100.     int        result;            /* True if command completed okay        */
  101. } MyMsg, *msg;
  102.  
  103. /*
  104.  *        Return codes passed back in result
  105.  */
  106. #define MSG_OKAY    0        /* Message was handled correctly        */
  107. #define MSG_REMOVED    1        /* CpuBlit was removed safely            */
  108. #define MSG_FAILED    2        /* CpuBlit couldn't be removed            */
  109.  
  110. /*
  111.  *        Scalar variables
  112.  */
  113. struct MsgPort *LocalPort;    /* Local port for returned messages        */
  114. long QuitFlag;                /* True if user asks CpuBlit to quit    */
  115. long FromWorkbench;            /* True if started from Workbench        */
  116. long AlreadyRunning;        /* True if CpuBlit already installed    */
  117.  
  118. /****************************************************************************
  119.  *
  120.  *        myexit(err)
  121.  *
  122.  *        Performs a small amount of cleanup and then exits to AmigaDos.
  123.  *        Principally, handles cleaning up if we were run from Workbench.
  124.  *
  125.  ***************************************************************************/
  126.  
  127. void myexit(err)
  128. {
  129.     if (LocalPort)
  130.         DeletePort(LocalPort);
  131.  
  132.     exit(err);
  133. }
  134.  
  135. /****************************************************************************
  136.  *
  137.  *        SetVars(settings)
  138.  *
  139.  *        Sets the various CpuBlit flags according to the values in the
  140.  *        supplied Settings structure.
  141.  *
  142.  ***************************************************************************/
  143.  
  144. void SetVars(struct Settings *settings)
  145. {
  146.     BlitFunc    = BlitFuncs[settings->BlitMode];
  147.     OnlySingle    = settings->OnlySingle;
  148.     Broken        = settings->Broken;
  149.     MinTaskPri    = settings->MinTaskPri;
  150. }
  151.  
  152. /****************************************************************************
  153.  *
  154.  *        ParseOption()
  155.  *
  156.  *        Parses an option string, setting the appropriate field in the
  157.  *        master Settings structure. This routine handles both Unix-style
  158.  *        -opts and also ReadArgs/ToolTypes keywords. Returns true if
  159.  *        the option string made sense, false otherwise.
  160.  *
  161.  ***************************************************************************/
  162.  
  163. int ParseOption(char *opt)
  164. {
  165. #define MATCHSTR(s1,s2)        (!strnicmp(s1, s2, sizeof(s2)-1))
  166.  
  167.     if MATCHSTR(opt, "-a") {
  168.         Settings.BlitMode    = BLIT_ALWAYS;
  169.         Settings.OnlySingle    = NO;
  170.         Settings.Broken        = NO;
  171.     }
  172.     else if MATCHSTR(opt, "-1")        Settings.BlitMode    = BLIT_ONE;
  173.     else if MATCHSTR(opt, "-2")        Settings.BlitMode    = BLIT_TWO;
  174.     else if MATCHSTR(opt, "-s")        Settings.BlitMode    = BLIT_SHARE;
  175.     else if MATCHSTR(opt, "-b")        Settings.Broken        = YES;
  176.     else if MATCHSTR(opt, "-o")        Settings.OnlySingle    = YES;
  177.     else if MATCHSTR(opt, "-q")        QuitFlag            = YES;
  178.     else if (MATCHSTR(opt, "-p") && opt[2])
  179.         Settings.MinTaskPri = atoi(opt+2);
  180.     else if MATCHSTR(opt, "BLITMODE") {
  181.         char *p = opt + 8;
  182.         if (*p++ && *p) {
  183.             if        MATCHSTR(p, "ALWAYS")    Settings.BlitMode    = BLIT_ALWAYS;
  184.             else if MATCHSTR(p, "ONE")        Settings.BlitMode    = BLIT_ONE;
  185.             else if MATCHSTR(p, "TWO")        Settings.BlitMode    = BLIT_TWO;
  186.             else if MATCHSTR(p, "SHARE")    Settings.BlitMode    = BLIT_SHARE;
  187.         } else return (0);
  188.     }
  189.     else if MATCHSTR(opt, "BROKEN") {
  190.         char *p = opt + 6;
  191.         if (*p++ && MATCHSTR(p, "NO"))        Settings.Broken        = NO;
  192.         else                                 Settings.Broken        = YES;
  193.     }
  194.     else if MATCHSTR(opt, "SINGLE") {
  195.         char *p = opt + 6;
  196.         if (*p++ && MATCHSTR(p, "NO"))        Settings.OnlySingle    = NO;
  197.         else                                 Settings.OnlySingle = YES;
  198.     }
  199.     else if MATCHSTR(opt, "MINTASKPRI") {
  200.         char *p = opt + 10;
  201.         if (*p++ && *p)                        Settings.MinTaskPri = atoi(p);
  202.         else return (0);
  203.     }
  204.     else if MATCHSTR(opt, "QUIT")            QuitFlag = YES;
  205.     else  return (0);
  206.  
  207.     return (1);
  208. }
  209.  
  210. /****************************************************************************
  211.  *
  212.  *        MyFindPort(name)
  213.  *
  214.  *        Replacement for the FindPort() in exec.library. Under 1.3, FindPort()
  215.  *        will cause Enforcer hits since FFS partitions create public ports
  216.  *        that have no name (this is a no-no), and FindPort() doesn't check
  217.  *        for null names.
  218.  *        
  219.  *        Even though this isn't really CpuBlit's problem, I had quite a few
  220.  *        reports of CpuBlit causing Enforcer hits which turned out to be
  221.  *        because of this, so I've added this workaround to keep people
  222.  *        happy.
  223.  *
  224.  *        Commodore made FindPort() (actually FindName()) a bit more robust
  225.  *        under Kickstart 2.0, so in that case we can safely use the standard
  226.  *        routine.
  227.  *
  228.  ***************************************************************************/
  229.  
  230. struct MsgPort *MyFindPort(char *name)
  231. {
  232.     struct Node *nd;
  233.     extern struct ExecBase *SysBase;
  234.  
  235.     if (SysBase->LibNode.lib_Version >= 36)
  236.         return (FindPort(name));
  237.  
  238.     Forbid();
  239.     for (nd = SysBase->PortList.lh_Head; nd; nd = nd->ln_Succ)
  240.         if (nd->ln_Name && !strcmp(nd->ln_Name, name))
  241.             break;
  242.     Permit();
  243.     return (struct MsgPort *)nd;
  244. }
  245.  
  246.  
  247. /****************************************************************************
  248.  *
  249.  *        mainloop()
  250.  *
  251.  *        This is the main event loop. It sits waiting for a message from
  252.  *        other invocations of CpuBlit, which tell it to either change the
  253.  *        current settings or to remove itself.
  254.  *
  255.  ***************************************************************************/
  256.  
  257. void mainloop(void)
  258. {
  259.     struct MsgPort *MyPort;
  260.     int installed = 1;
  261.     __fptr *BltBitMapPtr = (__fptr *)BltBitMapAddress;
  262.  
  263.     /*
  264.      *        We have to create our rendezvous port here rather than in the
  265.      *        mainline, since the message port depends on task information etc.
  266.      *        This is not altogether satisfactory since if it fails, there is
  267.      *        no way to tell the user (as a background task, we have no stdin
  268.      *        or stdout). But since port creation is unlikely to fail anyway,
  269.      *        it's not a big problem.
  270.      */
  271.     MyPort = CreatePort(PORTNAME, 0);
  272.     if (!MyPort)
  273.         return;
  274.  
  275.     /*
  276.      *        Now have to open graphics.library, so that we can add in our
  277.      *        new patch. As above, if this fails there is no easy way to
  278.      *        tell the user. However, at least it won't crash the system.
  279.      */
  280.     GfxBase = OpenLibrary("graphics.library", 0);
  281.     if (!GfxBase)
  282.         return;
  283.  
  284.     *BltBitMapPtr = SetFunction(GfxBase, BltBitMap_LVO, NewBltBitMap);
  285.  
  286.     /*
  287.      *        Now wait a message from another copy of CpuBlit. This will
  288.      *        either contain an updated command line argument or else a
  289.      *        request to quit.
  290.      */
  291.     do {
  292.         __fptr oldptr;
  293.  
  294.         WaitPort(MyPort);
  295.         while ((msg = (struct MyMsg *)GetMsg(MyPort)) != NULL) {
  296.             switch (msg->command) {
  297.  
  298.             case MSG_GETVARS:
  299.                 memcpy(msg->vars, &Settings, sizeof(Settings));
  300.                 break;
  301.  
  302.             case MSG_SETVARS:
  303.                 memcpy(&Settings, msg->vars, sizeof(Settings));
  304.                 SetVars(&Settings);
  305.                 break;
  306.  
  307.             case MSG_QUIT:
  308.                 /*
  309.                  *        Try and remove ourselves. We have to surround this
  310.                  *        with Forbid() to make sure that no other tasks manage
  311.                  *        to call BltBitMap() in the case where we restore the
  312.                  *        original vector and then realise that its current
  313.                  *        replacement actually pointed to something other than
  314.                  *        CpuBlit.
  315.                  */
  316.                 Forbid();
  317.                 oldptr = SetFunction(GfxBase, BltBitMap_LVO, *BltBitMapPtr);
  318.                 if (oldptr == NewBltBitMap) {
  319.                     installed = 0;
  320.                     msg->result = MSG_REMOVED;
  321.                 } else {
  322.                     SetFunction(GfxBase, BltBitMap_LVO, oldptr);
  323.                     msg->result = MSG_FAILED;
  324.                 }
  325.                 Permit();
  326.             }
  327.             ReplyMsg(msg);
  328.         }
  329.     } while (installed);
  330.  
  331.     /*
  332.      *        Now our patch has been removed, it only remains to free up the
  333.      *        code. It is possible that someone is still in our blitter code.
  334.      *        We can determine this fairly safely by looking at UsageCount;
  335.      *        if this is -1, nobody is in our code. Otherwise, we wait until
  336.      *        it is -1 (delaying for a little while inbetween to give programs
  337.      *        a chance to run).
  338.      *
  339.      *        We also set the blitter test function to ExitBlit, so that if
  340.      *        someone does slip through our test and end up inside our code,
  341.      *        they will get rerouted back to the normal blitter code almost
  342.      *        immediately.
  343.      */
  344.     DeletePort(MyPort);
  345.     BlitFunc = ExitBlit;
  346.  
  347.     while (UsageCount != -1)
  348.         Delay(10);                /* Wait 0.2 seconds */
  349.  
  350.     /*
  351.      *        Now we're completely finished so we can close the libraries
  352.      *        and exit.
  353.      */
  354.     CloseLibrary(GfxBase);
  355. }
  356.  
  357.  
  358. /****************************************************************************
  359.  *
  360.  *        Mainline 
  361.  *
  362.  ***************************************************************************/
  363.  
  364. void main(int argc, char **argv)
  365. {
  366.     struct MsgPort *BlitPort;
  367.     int i;
  368.  
  369.     FromWorkbench = (argc == 0);
  370.  
  371.     /*
  372.      *        Now see if CpuBlit is already running in
  373.      *        the background. If it is, then get a copy of the settings it
  374.      *        is currently using.
  375.      */
  376.     BlitPort = MyFindPort(PORTNAME);
  377.     if (BlitPort) {
  378.         /*
  379.          *        The new blit routine has already been installed. So, send
  380.          *        it a message giving the command line options (if any)
  381.          *        to the remote routine, telling it to update its own options.
  382.          */
  383.         AlreadyRunning = YES;
  384.         LocalPort = CreatePort(NULL, 0);
  385.         if (!LocalPort) {
  386.             if (!FromWorkbench)
  387.                 print("CpuBlit: couldn't create local message port.\n");
  388.             myexit(10);
  389.         }
  390.         MyMsg.msg.mn_ReplyPort    = LocalPort;
  391.         MyMsg.command            = MSG_GETVARS;
  392.         MyMsg.vars               = &Settings;
  393.         PutMsg(BlitPort, &MyMsg);
  394.         WaitPort(LocalPort);
  395.         GetMsg(LocalPort);
  396.     }
  397.     /*
  398.      *        Now parse the command line options, modifying our local copy
  399.      *        of Settings accordingly.
  400.      */
  401.     if (FromWorkbench) {
  402.         extern struct WBStartup *WBenchMsg;
  403.         struct WBArg *wbarg = WBenchMsg->sm_ArgList;
  404.  
  405.         IconBase = (struct IconBase *)OpenLibrary("icon.library", 0);
  406.         if (!IconBase)
  407.             myexit(5);
  408.  
  409.         /*
  410.          *        Now walk down all the icons we've been given (probably
  411.          *        just our own tool icon) and parse the arguments present
  412.          *        in each one.
  413.          */
  414.         for (i = 0; i < WBenchMsg->sm_NumArgs; i++, wbarg++) {
  415.             struct DiskObject *dobj;
  416.             char **tooltypes;
  417.             BPTR olddir;
  418.  
  419.             if (wbarg->wa_Lock && *wbarg->wa_Name) {
  420.                 olddir = CurrentDir(wbarg->wa_Lock);
  421.                 if (dobj = GetDiskObject(wbarg->wa_Name)) {
  422.                     for (tooltypes = dobj->do_ToolTypes;
  423.                                             *tooltypes; tooltypes++)
  424.                         ParseOption(*tooltypes);
  425.                 }
  426.                 FreeDiskObject(dobj);
  427.                 CurrentDir(olddir);
  428.             }
  429.         }
  430.         CloseLibrary(IconBase);
  431.     } else {
  432.         /*
  433.          *        Plain jane CLI startup
  434.          */
  435. #if DEBUG
  436.         if (argv[1][0] == '!') {
  437.             static buf[1000];
  438.             sprintf(buf,
  439.                 "Blitmode   = %d\n"
  440.                 "OnlySingle = %d\n"
  441.                 "Broken     = %d\n"
  442.                 "MinTaskPri = %d\n",
  443.                 Settings.BlitMode, Settings.OnlySingle,
  444.                 Settings.Broken, Settings.MinTaskPri);
  445.             print(buf);
  446.             exit(5);
  447.         }
  448. #endif
  449.         for (i = 1; i < argc; i++) {
  450.             if (!ParseOption(argv[i])) {
  451.                 print(HelpMsg);
  452.                 myexit(5);
  453.             }
  454.         }
  455.     }
  456.  
  457.     /*
  458.      *        Now we either send the options to the remote copy of CpuBlit
  459.      *        or install ourselves in the background.
  460.      */
  461.     if (AlreadyRunning) {
  462.         if (QuitFlag)
  463.             MyMsg.command = MSG_QUIT;
  464.         else
  465.             MyMsg.command = MSG_SETVARS;
  466.         PutMsg(BlitPort, &MyMsg);
  467.         WaitPort(LocalPort);
  468.         GetMsg(LocalPort);
  469.         if (FromWorkbench && MyMsg.result == MSG_FAILED) {
  470.             IntuitionBase = (struct IntuitionBase *)
  471.                             OpenLibrary("intuition.library", 0);
  472.             if (IntuitionBase) {
  473.                 DisplayBeep(0);    /* Flash all screens -- pretty rude */
  474.                 CloseLibrary(IntuitionBase);
  475.             }
  476.         }
  477.         if (!FromWorkbench) {
  478.             if (MyMsg.result == MSG_REMOVED)
  479.                 print("CpuBlit removed successfully.\n");
  480.             else if (MyMsg.result == MSG_FAILED) {
  481.                 print(
  482. "Couldn't remove CpuBlit; someone else has patched BltBitMap. Please remove\n"
  483. "any other utilities you have installed and then try again.\n");
  484.                 myexit(5);
  485.             }
  486.         }
  487.         myexit(0);
  488.     }
  489.  
  490.     /*
  491.      *        This is the first time we are being run. If we were run from
  492.      *        the CLI, detach ourselves (and allow the current process to
  493.      *        return to the CLI immediately). If we were run from Workbench,
  494.      *        then just call the message handling code directly and wait
  495.      *        until another copy of CpuBlit asks us to return.
  496.      */
  497.     if (QuitFlag) {
  498.         if (!FromWorkbench)
  499.             print("CpuBlit hasn't been installed yet.\n");
  500.         myexit(5);
  501.     }
  502.  
  503.     SetVars(&Settings);
  504.     if (FromWorkbench)
  505.         mainloop();
  506.     else {
  507.         if (!res(NAME, 5, mainloop, 4000)) {
  508.             print("Couldn't spawn background CpuBlit task.\n");
  509.             myexit(10);
  510.         }
  511.         print(SIGNON);
  512.     }
  513.     myexit(0);
  514. }
  515.