home *** CD-ROM | disk | FTP | other *** search
/ The Fred Fish Collection 1.5 / ffcollection-1-5-1992-11.iso / ff_disks / 300-399 / ff355.lzh / TrackSalve / Source / main.c < prev    next >
C/C++ Source or Header  |  1990-06-12  |  24KB  |  780 lines

  1. /*
  2.  *  Main.c  -   Copyright 1990 D.W.Reisig
  3.  *
  4.  *   #    date    by    Comment
  5.  *  -- --------- ----- ---------------------
  6.  *   0 29-Oct-89  DWR   Created for Tracksalve
  7.  *   1 10-Apr-90  DWR   TrackSalve 1.3, switches added, new status report
  8.  *
  9.  */
  10.  
  11. /*
  12.  
  13. TrackSalve can be separated into two parts.  The patches of Trackdisk and the
  14. part that controls it.  Because of some compiling problems all patches are
  15. gathered into one large file named patches.a. All other sources belong to the
  16. controlling part.  Most obvious controlling is covered by this file main.c.
  17.  
  18. The patch exists of an allocation of memory to merge our code and Trackdisk
  19. into.  Two functions use a buffer of 26k.  These resources are accessable from
  20. here, we allocate them, and from one to four Trackdisk unit tasks.  These
  21. accesses can interfere in very bad ways, so we have to protect them.  This is
  22. done with semaphores.  The memory in which Trackdisk is executed is protected
  23. by a semaphore that once installed, never will leave us anymore.  It is used
  24. under the name Hook and exists of a TSHook structure.  It is also used as an
  25. anchor in the system.  Any subsequent execution of the controlling task can
  26. find the hook with FindSemaphore(). Another semaphore regulates the usage of
  27. the salve/verify buffer, which is shared by all units.
  28.  
  29. Installing the patch exists of some clearly distinguishable stages:
  30.  
  31. The allocation of the memory in which the patched TD unit tasks will run.  It
  32. takes about 10k bytes fast memory.  This memory is descripted by a structure
  33. named TSControl.  All members begin with TSC_. The begin contains some arrays,
  34. whose elements are data structures used by the different unit tasks.  Then
  35. there are some global variables, like which units use what resources and the
  36. salve buffer semaphore.  Here after begins an area in which the extension code
  37. is copied into, directly followed by the copy of Trackdisk code.
  38.  
  39. Now that we have the TSControl structure we must furnish it.  We copy the
  40. extension code into it.  This code is not completely position independent, and
  41. must be relocated.  Then Trackdisk code is copied tight hereafter.  Trackdisk
  42. also is not position indepent, and must be relocated too.  The last
  43. modification is the installation of the patches.
  44.  
  45. The code is ready to be executed, but it takes more than just adding an offset
  46. to the PC of the unit we want to patch.  We must catch the unit in its highest
  47. taskloop, otherwise a rts would pull a ROM-adress from the stack, and we lose
  48. the PC again.  Fortunately TD has a small toplevel task loop with two points
  49. where the PC will be most of its time.  We will switch PC only if the unit task
  50. is here.  This has an additional benefit:  we can let the unit do some special
  51. initialisation.  We donot want to spend our resources to uncatchable units, so
  52. we spend some time catching and if not successful we print some error message
  53. and delete the request.
  54.  
  55. If we want a unit to go back to ROM, it is not enough to catch the PC again and
  56. put it back.  If f.e. readonly is selected, that must be undone before the PC
  57. can go back.  Therefore we TELL the unit to go back.  Then it has time to clean
  58. up. But what if it was the last unit that executed in RAM?  Forget the 10k?
  59. No, it has to cleanup the TSControl structure as well.  Tricky bussiness
  60. freeing currently executed memory.  A nice exercise!  WE must be able to free
  61. the structure as well.  If we did not catch any unit, we must cleanup the
  62. TSControl structure ourselves.  So all involved tasks can free about
  63. everything.  This must be arranged very careful.  If we get GURU $81000009, we
  64. know we were not careful enough.
  65.  
  66. For each unit there is a data area available.  It is organised as the UnitData
  67. structure.  These are part of the TSControl structure.  That way we donot have
  68. to allocate it individually and handle involved errors.  The structure contains
  69. data as an IORequest, the ROM-PC, the address of the TSControl structure, some
  70. data fields used in the patch and a command byte:  UD_Cmd1. Its a byte in which
  71. we tell a unit what to do and what not.
  72.  
  73. The task control block has a member TC_UserData.  This is not used by a TD
  74. unit.  We use it to store a pointer to our UnitData structure.  There is
  75. another member we can use:  UNIT_pad.  UNIT_pad is a byte that should be the
  76. same as the UD_Cmd1. While it is not, TD will take actions to achieve that.
  77. More about this in patch.a.
  78.  
  79. The commandline is scanned and all commands are collected but not executed.
  80. Commands are initially gathered in UD_Cmd0. If no error is found in the
  81. commandline, the commands are passed from UD_Cmd0 to UD_Cmd1, thus passing them
  82. to the unit tasks.  Before we can do so, we have done an OpenDevice() for each
  83. unit.  Now it is known which units are available.  If a unit is not
  84. transferred, a CloseDevice() is done with it.  We have two reasons to do an
  85. OpenDevice(). First to find its data area (io_Unit) and TCB
  86. (io_Message.mn_ReplyPort->mp_SigTask). Second we donot want a real
  87. CloseDevice(), deleting the patched unit task before we can perform our
  88. cleanups. (And what if the unit was already closed, huh?)
  89.  
  90. All messages are stored in a buffer and printed later.  This program can hold
  91. up Trackdisk tasks and must not be blocked by malfunctioning output.
  92.  
  93. */
  94.  
  95.  
  96. #include <exec/types.h>
  97. #include <exec/memory.h>
  98. #include <exec/resident.h>
  99. #include <exec/execbase.h>
  100. #include <exec/semaphores.h>
  101. #include <hardware/blit.h>
  102. #include <devices/trackdisk.h>
  103. #include <string.h>
  104. #include "ts.h"
  105. #include <proto/exec.h>
  106. #pragma syscall FindSemaphore 252 901        /*  Use a1 instead of a0  */
  107. #include <string.h>
  108.  
  109.  
  110. #define btst(a,b) ((1<<a)&b)
  111. #define bset(a,b) (b|=1<<a)
  112. #define bclr(a,b) (b&=~(1<<a))
  113.  
  114. extern struct Resident *FindResident();
  115. extern char *strcpy();
  116. extern char *TSCodeBegin();                     /*  Not a function but begin of extension code                  */
  117. extern LONG R2;                                 /*  Secondary result code  (Why uses it)                        */
  118. extern char Cr[];                               /*  Creation/Version line                                       */
  119. extern ULONG TSCodeSize;                        /*  Size of extension code (should be a constant)               */
  120. extern ULONG BufferSize;                        /*  Size of a chip buffer used for tracksalvage and verify      */
  121. extern void TSPatchTable();                     /*  Not realy a function but data in a code hunk                */
  122. extern struct ExecBase *SysBase;
  123. extern ULONG TSVersion;                         /*  Perhaps generations are not compatible, check it!           */
  124.  
  125. int  __regargs PatchMem(struct Resident *, void (*)());
  126. int  __regargs XCommands(int, char *[], struct TSHook *);
  127. int  __regargs Help(int, char *[]);
  128. int  __regargs ScanCmdLine(int, char *[], struct TSControl *);
  129. int  __regargs PCtoTS(struct UnitData *);
  130. int  __regargs ShowTS(struct TSControl *);
  131. void __regargs ConMsg(char *);
  132. void __regargs RelocateTS(struct TSControl *);
  133. void __regargs RelocateTD(struct TSControl *);
  134. void __regargs AddLine(char *, char *);
  135. void __regargs AddStr(char *);
  136.  
  137. struct TSControl *AllocTSControl();
  138.  
  139.  
  140. #define MSGBUFSIZE 750                          /*  Little careful with this, stack checking is disabled        */
  141.  
  142. char *OP;                                       /*  Pointer into the message buffer                             */
  143. char *Pr;                                       /*  Program name from the commandline                           */
  144. LONG R2a;                                       /*  Error from subs which return a pointer                      */
  145. struct Library *IntuitionBase;                  /*  We find Intuition for the unit tasks  (Requesters)          */
  146.  
  147. char             TDName[]=TD_NAME;
  148. char    TSSemaphoreName[]=TSSEMAPHORENAME;
  149. char             LowMem[]="Did not get memory";
  150. char            *Number[]={ "0","1","2","3" };
  151.  
  152.  
  153. /*********************************************************
  154.  *
  155.  *  main
  156.  *
  157.  *  Scan the commandline for the help command.  If found print the text and leave.
  158.  *  Set up a buffer to collect all output.  Find or create the semaphore that controls
  159.  *  access to the TSControl structure.  Call XCommands and print the buffer.
  160.  *
  161.  *  Input:      argc and argv
  162.  *
  163.  *  Return:     ZERO or return value from XCommands()
  164.  *
  165.  */
  166.  
  167. __stdargs main(argc,argv)
  168. int argc;
  169. char *argv[];
  170. {
  171.   register struct TSHook *Hook;
  172.   register int RVal;
  173.   char OutBuf[MSGBUFSIZE];      /* Buffer on the stack */
  174.  
  175.   OP=OutBuf;                    /* Init bufferpointer  */
  176.   *OP='\0';
  177.   Pr=argv[0];                   /* Adapt programname from the commandline (WB already in startup disabled) */
  178.  
  179.   if (Help(argc,argv))          /* Scan cmdline for help function  */
  180.     return(ZERO);
  181.  
  182.  /*
  183.   *  Why spend time to protect this program against people who try to mess things up?
  184.   *  Anyway, protect the creation of the Semaphore against doublure by disabling
  185.   *  taskswitching during search, creation and instalation.
  186.   */
  187.  
  188.   Forbid();
  189.   if (!(Hook=(struct TSHook *)FindSemaphore(TSSemaphoreName))){
  190.     if (!(Hook=(struct TSHook *)AllocMem(sizeof(struct TSHook)+sizeof(TSSemaphoreName),PUBCLR))){
  191.       AddLine(LowMem,0);
  192.       R2=E_ALLOCMEM;
  193.       RVal=FAIL;
  194.     } else{
  195.       Hook->TSH_Semaphore.ss_Link.ln_Name=strcpy((char *)Hook+sizeof(struct TSHook),TSSemaphoreName);
  196.       InitSemaphore((struct SignalSemaphore *)Hook);
  197.       Enqueue(&SysBase->SemaphoreList,(struct Node *)Hook);
  198.     }
  199.   }
  200.   Permit();
  201.  
  202.  /*
  203.   *  Get exclusive right to modify the Hook and its attachments.  Check for commands to perform.
  204.   */
  205.  
  206.   if (Hook){
  207.     ObtainSemaphore((struct SignalSemaphore *)Hook);
  208.     RVal=XCommands(argc,argv,Hook);
  209.     ReleaseSemaphore((struct SignalSemaphore *)Hook);
  210.   }
  211.  
  212.   if (RVal>ERROR)
  213.     AddLine("NB! No commands passed or executed!",0);
  214.   Write(Output(),OutBuf,strlen(OutBuf));
  215.   return(RVal);
  216. }
  217.  
  218.  
  219. /*********************************************************
  220.  *
  221.  *  XCommands
  222.  *
  223.  *  Build the TSControl structure if it does not exist.  Open all possible
  224.  *  Trackdisk devices and scan the command line for commands for them to perform.
  225.  *  Let selected TD units execute in RAM.  Allocate the salve buffer if needed.
  226.  *  Release all unused resources and print a status report.
  227.  *
  228.  *  Input:      argc, argv and Hook
  229.  *
  230.  *  Return:     ZERO, WARN, ERROR and FAIL
  231.  *
  232.  */
  233.  
  234. int __regargs XCommands(argc,argv,Hook)
  235. register int argc;
  236. char *argv[];
  237. struct TSHook *Hook;
  238. {
  239.   register struct TSControl *Control;
  240.   register struct UnitData *UD;
  241.   register short i;
  242.   register char Collect;
  243.   register int RVal=ZERO;
  244.  
  245.   Control=Hook->TSH_TSControl;
  246.  
  247.  /*
  248.   *  Check the version number of the anchor.  If TD is patched and it is not ours, return
  249.   *  with some message.
  250.   */
  251.  
  252.   if (Control){
  253.     if (Hook->TSH_Version!=TSVersion){
  254.       AddLine("Another version of TrackSalve is already active",0);
  255.       return(FAIL);
  256.     }
  257.   } else   Hook->TSH_Version=TSVersion;
  258.  
  259.  /*
  260.   *  If the command line has no arguments we just print the present situation.
  261.   */
  262.  
  263.   if (argc==1)   return(ShowTS(Control));
  264.  
  265.  /*
  266.   *  If there is not yet a TSControl structure, build one and connect it with the Hook
  267.   */
  268.  
  269.   if (!Control){
  270.     Control=Hook->TSH_TSControl=AllocTSControl();
  271.     R2=R2a;
  272.     if (!Control)  return(FAIL);
  273.     Control->TSC_TSHook=Hook;
  274.   }
  275.  
  276.  /*
  277.   *  We have two arrays in the TSControl structure. One is an array of UnitData structures
  278.   *  and the other is an array of pointers to them.  If the pointer is zero, the unit is
  279.   *  closed and we try to open it.  In that case the unit will run in ROM and does not
  280.   *  execute special functions.  So we init UD_Cmd0 and UD_Cmd1 to 0.  If the OpenDevice()
  281.   *  fails the pointer stays zero.  If the pointer was not zero, the unit is already open
  282.   *  and it executes already in RAM.  In that case we are interested in its current
  283.   *  commands and copy them from UD_Cmd1 to UD_Cmd0.
  284.   *  We will not do any IO with the units, therefore our IORquest does not need a port.
  285.   */
  286.  
  287.   Control->TSC_AvailUnits=0;
  288.   for (i=0;i<NUMUNITS;++i){
  289.     if (UD=Control->TSC_UnitData[i]){
  290.       UD->UD_Cmd0=UD->UD_Cmd1;
  291.     } else{
  292.       UD=&Control->TSC_UDAlloc[i];
  293.       if (!OpenDevice(TDName,i,(struct IORequest *)&UD->UD_TDReq,TDF_ALLOW_NON_3_5)){
  294.         Control->TSC_UnitData[i]=UD;
  295.         UD->UD_Cmd0=UD->UD_Cmd1=0;
  296.       } else  UD=0;
  297.     }
  298.     if (UD)   bset(i,Control->TSC_AvailUnits);
  299.   }
  300.  
  301.  /*
  302.   *  Scan the commandline and translate commandline arguments into real commands in UD_Cmd0.
  303.   *  If no serious cmdline error is met, pass for each existing unit UD_Cmd0 to UD_Cmd1.
  304.   *  If an unit has special functions to execute, but is not yet in RAM, see that that is
  305.   *  done.  If not successful print a message and clear its commands as a signal to close
  306.   *  the device again (later).  If a unit is in RAM, but its UD_Cmd1 is empty, set the
  307.   *  F_TERM flag in it.  This will bring the unit to clean up and go back to ROM.
  308.   */
  309.  
  310.   if (RVal=ScanCmdLine(argc,argv,Control)){
  311.     R2=E_CMDLINE;
  312.     if (RVal>ERROR) return(RVal);
  313.   }
  314.  
  315.  
  316.   for (Collect=0,i=0;i<NUMUNITS;++i){
  317.     if (UD=Control->TSC_UnitData[i]){
  318.  
  319.       if ((UD->UD_Cmd1=UD->UD_Cmd0)&FUNCTIONS){
  320.         if (!btst(i,Control->TSC_InUse)){
  321.           if (PCtoTS(UD)){
  322.             AddLine("Cannot find trackdisk task for unit ",Number[i]);
  323.             UD->UD_Cmd1=0;
  324.             RVal=ERROR;
  325.           }
  326.         }
  327.         Collect|=UD->UD_Cmd1;
  328.       } else{
  329.         if (btst(i,Control->TSC_InUse)){
  330.           UD->UD_Cmd1=F_TERM;
  331.         }
  332.       }
  333.  
  334.     }/*Unit is open*/
  335.   }/*For every possible unit*/
  336.  
  337.  
  338.  /*
  339.   *  If any unit has its salve function active and there is not yet a buffer, try
  340.   *  to allocate it.
  341.   */
  342.  
  343.   if ((Collect&(F_SALVE+F_VERIFY1+F_VERIFY0))&&!Control->TSC_Buffer)
  344.     if (!(Control->TSC_Buffer=AllocMem(BufferSize,MEMF_CHIP)))   RVal=ERROR;
  345.  
  346.  
  347.  /*
  348.   *  Close all devices which are open but not executed in RAM, and clear their pointers.
  349.   */
  350.  
  351.   for (i=0;i<NUMUNITS;++i){
  352.     if ((!btst(i,Control->TSC_InUse))&&(UD=Control->TSC_UnitData[i])){
  353.       if (UD->UD_TDReq.iotd_Req.io_Device>0)
  354.         CloseDevice((struct IORequest *)&UD->UD_TDReq);
  355.       Control->TSC_UnitData[i]=0;
  356.     }
  357.   }
  358.  
  359.  /*
  360.   *  Free TSCcontrol structure if no users of it and untie it from the hook.
  361.   */
  362.  
  363.   if (!Control->TSC_InUse){
  364.     CloseLibrary(IntuitionBase);
  365.     FreeMem(Control,Control->TSC_Size);
  366.     Hook->TSH_TSControl=0;
  367.   }
  368.  
  369.  /*
  370.   *  Print a status report and return.
  371.   */
  372.  
  373.   ShowTS(Hook->TSH_TSControl);
  374.  
  375.   return(RVal);
  376. }
  377.  
  378.  
  379.  
  380. /*********************************************************
  381.  *
  382.  *  Help
  383.  *
  384.  *  Scan the commandline for any chars that could be a request for help.
  385.  *  If found print a apropriate message and return.
  386.  *
  387.  *  Input:      argc and argv
  388.  *
  389.  *  Return:     Non zero if help request detected and zero if not.
  390.  */
  391.  
  392. int __regargs Help(argc,argv)
  393. int argc;
  394. char *argv[];
  395. {
  396.   register int argcnt;
  397.   register char *Option;
  398.   register short avail;
  399.  
  400.   for (argcnt=1;argcnt<argc;++argcnt){
  401.     Option=argv[argcnt];
  402.     avail=1;
  403.     while (avail){
  404.       switch(*(Option++)){
  405.  
  406.         case'?':
  407.         case'h':
  408.         case'H':
  409.           if (*Option=='?')   ConMsg(Cr);
  410.           else                Explain();
  411.           return(-1);
  412.  
  413.         case'\0':
  414.           avail=0;
  415.       }
  416.     }
  417.   }
  418.  
  419.   return(0);
  420. }
  421.  
  422.  
  423. /*********************************************************
  424.  *
  425.  *  ScanCmdLine
  426.  *
  427.  *  Scan the commandline for unit numbers and commands.  Check whether unit numbers
  428.  *  select existing units.  Return FAIL if a non-existing unit is selected unless
  429.  *  a special option us used.  Modify the UD_Cmd0 member of the selected units.
  430.  *  Unrecognised chars result in a FAIL.
  431.  *
  432.  *  Input:      argc and argv
  433.  *              TSControl structure
  434.  *
  435.  *  Return:     ZERO if no errors detected,
  436.  *              WARN if a special option is used together with the selection of a non existing unit
  437.  *              FAIL if a non existing unit is selected or a command is given without a selected
  438.  *                      unit, or a not recognised command is detected.
  439.  */
  440.  
  441. int __regargs ScanCmdLine(argc,argv,Control)
  442. int argc;
  443. char *argv[];
  444. struct TSControl *Control;
  445. {
  446.   short ArgCnt;
  447.   register short i, UnitNr;
  448.   char *Argument, *Arg0;
  449.   register UBYTE OnCmd, OffCmd;
  450.   UBYTE  BeStrictOnUnitNumbers, Avail, CmdUnits0, CmdUnits1;
  451.   int RVal;
  452.  
  453.   RVal=0;
  454.   CmdUnits0=0;
  455.   CmdUnits1=0;
  456.   BeStrictOnUnitNumbers=1;
  457.  
  458.   for (ArgCnt=1;ArgCnt<argc;++ArgCnt){
  459.     Argument=argv[ArgCnt];
  460.     Avail=1;
  461.     while (Avail){
  462.       OnCmd=0;
  463.       OffCmd=0;
  464.       UnitNr=-1;
  465.       switch(tolower(*(Arg0=Argument++))){
  466.  
  467.         case'\0':
  468.           Avail=0;
  469.         case'\t':
  470.         case' ':
  471.         case',':
  472.         case'-':
  473.         break;
  474.  
  475.         case'a':
  476.           CmdUnits0=CmdUnits1=Control->TSC_AvailUnits;
  477.           for (i=0;i<NUMUNITS;++i){
  478.             if (btst(i,CmdUnits1))
  479.               Control->TSC_UnitData[i]->UD_Cmd0|=F_RAM;
  480.           }
  481.         break;
  482.  
  483.         case'!':
  484.           BeStrictOnUnitNumbers=0;
  485.         break;
  486.  
  487.         case'/':
  488.           CmdUnits0=0;
  489.           BeStrictOnUnitNumbers=1;
  490.         break;
  491.  
  492.         case'0':
  493.           UnitNr=0;
  494.         break;
  495.  
  496.         case'1':
  497.           UnitNr=1;
  498.         break;
  499.  
  500.         case'2':
  501.           UnitNr=2;
  502.         break;
  503.  
  504.         case'3':
  505.           UnitNr=3;
  506.         break;
  507.         
  508.         case'u':
  509.           OnCmd=F_UPDATE;
  510.         break;
  511.         
  512.         case'e':
  513.           OffCmd=F_UPDATE;
  514.         break;
  515.  
  516.         case's':
  517.           OnCmd=F_SALVE;
  518.         break;
  519.  
  520.         case't':
  521.           OffCmd=F_SALVE;
  522.         break;
  523.  
  524.         case'n':
  525.           OnCmd=F_NOCLICK;
  526.         break;
  527.  
  528.         case'c':
  529.           OffCmd=F_NOCLICK;
  530.         break;
  531.  
  532.         case'r':
  533.           OnCmd=F_READONLY;
  534.         break;
  535.  
  536.         case'w':
  537.           OffCmd=F_READONLY;
  538.         break;
  539.  
  540.         case'v':
  541.           OffCmd=(F_VERIFY1+F_VERIFY0);
  542.           OnCmd=F_VERIFY0;
  543.           while (tolower(*Argument)=='v'){
  544.             ++Argument;
  545.             if (OnCmd!=(F_VERIFY1+F_VERIFY0))
  546.               OnCmd+=F_VERIFY0;
  547.           }
  548.         break;
  549.  
  550.         case'b':
  551.           OffCmd=(F_VERIFY1+F_VERIFY0);
  552.         break;
  553.  
  554.         case'o':
  555.           OffCmd=FUNCTIONS;
  556.         break;
  557.  
  558.         default:
  559.           AddLine("Not recognised: ",Arg0);
  560.           R2=E_CMDLINE;
  561.           return(FAIL);
  562.         break;
  563.  
  564.       }
  565.  
  566.      /*
  567.       *  Check if a unit selection is made.  If so, check its presence and act accordingly.
  568.       *  If the unit exists, set its bit in CmdUnits0 and copy CmdUnits0 to CmdUnits1.
  569.       *  Set the B_RAM bit int the UD_Cmd0 of the selected unit.
  570.       */
  571.  
  572.       if (UnitNr>=0){
  573.         if (!btst(UnitNr,Control->TSC_AvailUnits)){
  574.           AddLine("Trackdisk does not control this unit: ",Number[UnitNr]);
  575.           RVal=WARN;
  576.           R2=E_CMDLINE;
  577.           if (BeStrictOnUnitNumbers)
  578.             return(FAIL);
  579.         } else{
  580.           bset(UnitNr,CmdUnits0);
  581.           Control->TSC_UnitData[UnitNr]->UD_Cmd0|=F_RAM;
  582.         }
  583.         CmdUnits1=CmdUnits0;
  584.         BeStrictOnUnitNumbers=1;
  585.       }
  586.  
  587.      /*
  588.       *  If a command is detected, delete the old unit select base so that later a new unit pattern
  589.       *  can be build.  If the pattern is empty, the command is floating, which is an error.
  590.       *  Else apply the command to the units in the pattern CmdUnits1.
  591.       */
  592.  
  593.       if (OnCmd||OffCmd){
  594.         BeStrictOnUnitNumbers=1;
  595.         CmdUnits0=0;
  596.         if (!CmdUnits1){
  597.           AddLine("Command not applied to a unit: ",Arg0);
  598.           R2=E_CMDLINE;
  599.           return(FAIL);
  600.         }
  601.         for (UnitNr=0;UnitNr<NUMUNITS;++UnitNr){
  602.           UBYTE *Cmd0=&Control->TSC_UnitData[UnitNr]->UD_Cmd0;
  603.           if (btst(UnitNr,CmdUnits1)){
  604.             *Cmd0&=~OffCmd;
  605.             *Cmd0|=OnCmd;
  606.           }
  607.         }
  608.       }
  609.  
  610.     } /* while Avail */
  611.   } /* for ArgCnt */
  612.  
  613.   return(RVal);
  614. }
  615.  
  616.  
  617. /*********************************************************
  618.  *
  619.  *  AllocTSControl
  620.  *
  621.  *  Find the Trackdisk code in ROM, check version numbers, allocate space for
  622.  *  data and code for the patched Trackdisk tasks.  Init the structure as far
  623.  *  as possible, copy our patch extension code into it, relocate it, copy TD
  624.  *  code into it,  relocate the TD code and patch it.
  625.  *
  626.  *  Return:     TSControl structure or 0
  627.  */
  628.  
  629. struct TSControl *AllocTSControl()
  630. {
  631.   register struct TSControl *Control;
  632.   register struct Resident *TDTag;
  633.   char *AllocPtr;
  634.   register ULONG TDCodeSize, Size;
  635.   register int i;
  636.  
  637.   if (!(IntuitionBase=OpenLibrary("intuition.library",0))){
  638.     AddLine("No intuition",0);
  639.     R2a=E_INTUITION;
  640.     return(0);
  641.   }
  642.  
  643.   if (!(TDTag=FindResident(TDName))){
  644.     AddLine("Did not find resident module: ",TDName);
  645.     R2a=E_RTAG;
  646.     return(0);
  647.   }
  648.   if ((TDTag->rt_Version!=TD_VERSION1_2)&&(TDTag->rt_Version!=TD_VERSION1_3)){
  649.     AddLine("Wrong Trackdisk version (not 33 or 34)",0);
  650.     R2a=E_TD_VERSION;
  651.     return(0);
  652.   }
  653.   TDCodeSize=(char *)TDTag->rt_EndSkip-(char *)TDTag->rt_MatchTag;
  654.   Size=sizeof(struct TSControl)+TSCodeSize+TDCodeSize;
  655.   if (!(AllocPtr=AllocMem(Size,PUBCLR))){
  656.     AddLine(LowMem,0);
  657.     R2a=E_ALLOCMEM;
  658.     return(0);
  659.   }
  660.   Control=(struct TSControl *)AllocPtr;
  661.   for (i=0;i<NUMUNITS;++i)
  662.     Control->TSC_UDAlloc[i].UD_TSControl=Control;
  663.   Control->TSC_IntuBase=IntuitionBase;
  664.   Control->TSC_Size=Size;
  665.   Control->TSC_TDTag=TDTag;
  666.   Control->TSC_TDCodeSize=TDCodeSize;
  667.   CopyMem((char *)TSCodeBegin,AllocPtr+=sizeof(struct TSControl),TSCodeSize);
  668.   Control->TSC_TSCode=AllocPtr;
  669.   Control->TSC_TSCodeSize=TSCodeSize;
  670.   RelocateTS(Control);
  671.   CopyMem((char *)TDTag,AllocPtr+=TSCodeSize,TDCodeSize);
  672.   Control->TSC_TSTag=(struct Resident *)AllocPtr;
  673.   RelocateTD(Control);
  674.   PatchMem(Control->TSC_TSTag,TSPatchTable);
  675.   InitSemaphore(&Control->TSC_OwnBuffer);
  676.   return(Control);
  677. }
  678.  
  679.  
  680.  
  681.  
  682. /*********************************************************
  683.  *
  684.  *  ShowTS
  685.  *
  686.  *  Build a string which shows the modifications presently active
  687.  *  on the various TD-drives.
  688.  *
  689.  *  Input:      TSConTrol structure
  690.  */
  691.  
  692. __regargs ShowTS(Control)
  693. register struct TSControl *Control;
  694. {
  695.   register short i;
  696.   register UBYTE Func, Collect;
  697.   int RVal=0;
  698.  
  699.   static char  On[]="    On   ";
  700.   static char Off[]="    Off  ";
  701.   static char  NY[]="  Not Yet";
  702.   static char *OnN[]={
  703.     "    On 1 ",
  704.     "    On 2 ",
  705.     "    On 3 "
  706.   };
  707.  
  708.   if (!Control||!Control->TSC_InUse){
  709.     AddLine("Trackdisk not patched for any drive",0);
  710.   } else{
  711.     Collect=0;
  712.     AddLine("Present situation:",0);
  713.     AddStr(" Unit    Code    Verify    Salve   NoClick  ReadOnly Update\n");
  714.     for (i=0;i<NUMUNITS;++i){
  715.       if (btst(i,Control->TSC_AvailUnits)){
  716.         AddStr("   ");
  717.         AddStr(Number[i]);
  718.         if (Control->TSC_UnitData[i]){
  719.           Func=Control->TSC_UnitData[i]->UD_Cmd1;
  720.           if (Func&F_TERM){
  721.             AddStr("   Terminating");
  722.           } else{
  723.             Collect|=Func;
  724.             AddStr("   Patched ");
  725.             AddStr((Func&(F_VERIFY1+F_VERIFY0))?(Control->TSC_Buffer? OnN[(Func&(F_VERIFY1+F_VERIFY0))-1]:NY):Off);
  726.             AddStr((Func&F_SALVE)?(Control->TSC_Buffer?On:NY):Off);
  727.             AddStr((Func&F_NOCLICK)?On:Off);
  728.             AddStr((Func&F_READONLY)?On:Off);
  729.             AddStr((Func&F_UPDATE)?On:Off);
  730.           }
  731.         } else{
  732.           AddStr("   Original");
  733.         }
  734.  
  735.         AddStr("\n");
  736.       }
  737.     }
  738.     if ((Collect&(F_VERIFY1+F_VERIFY0+F_SALVE))&&!Control->TSC_Buffer){
  739.       AddStr("Low chip memory, Salve and/or Verify functions not yet active!!\n");
  740.       RVal=ERROR;
  741.     }
  742.   }
  743.   return(RVal);
  744. }
  745.  
  746.  
  747. /*********************************************************
  748.  *
  749.  *  AddLine - AddStr
  750.  *
  751.  *  Copy string arguments into the stringbuffer
  752.  */
  753.  
  754. void __regargs AddLine(a,b)
  755. char *a,*b;
  756. {
  757.   AddStr(Pr);
  758.   AddStr(": ");
  759.   AddStr(a);
  760.   AddStr(b);
  761.   AddStr("\n");
  762. }
  763.  
  764.  
  765. void __regargs AddStr(a)
  766. register char *a;
  767. {
  768.   static short Size=MSGBUFSIZE-2;
  769.   register short Len;
  770.  
  771.   if (a){
  772.     Len=strlen(a);
  773.     if (Len<Size){
  774.       OP=stpcpy(OP,a);
  775.       Size-=Len;
  776.     }
  777.     else  Size=0;
  778.   }
  779. }
  780.