/* TrackSalve - main.c TrackSalve can be separated into two parts. The patches of Trackdisk and the part that controls it. Because of some compiling problems all patches are gathered into one large file named patches.a. All other sources belong to the controlling part. Most obvious controlling is covered by this file main.c. The patch exists of an allocation of memory to merge our code and Trackdisk into. Two functions use a buffer of 26k. These resources are accessable from here, we allocate them, and from one to four Trackdisk unit tasks. These accesses can interfere in very bad ways, so we have to protect them. This is done with semaphores. The memory in which Trackdisk is executed is protected by a semaphore that once installed, never will leave us anymore. It is used under the name Hook and exists of a TSHook structure. It is also used as an anchor in the system. Any subsequent execution of the controlling task can find the hook with FindSemaphore(). Another semaphore regulates the usage of the salve/verify buffer, which is shared by all units. Installing the patch exists of some clearly distinguishable stages: The allocation of the memory in which the patched TD unit tasks will run. It takes about 10k bytes fast memory. This memory is descripted by a structure named TSControl. All members begin with TSC_. The begin contains some arrays, whose elements are data structures used by the different unit tasks. Then there are some global variables, like which units use what resources and the salve buffer semaphore. Here after begins an area in which the extension code is copied into, directly followed by the copy of Trackdisk code. Now that we have the TSControl structure we must furnish it. We copy the extension code into it. This code is not completely position independent, and must be relocated. Then Trackdisk code is copied tight hereafter. Trackdisk also is not position indepent, and must be relocated too. The last modification is the installation of the patches. The code is ready to be executed, but it takes more than just adding an offset to the PC of the unit we want to patch. We must catch the unit in its highest taskloop, otherwise a rts would pull a ROM-adress from the stack, and we lose the PC again. Fortunately TD has a small toplevel task loop with a point where the PC will be most of its time. We will switch PC only if the unit task is here (waiting()). This has an additional benefit: we can let the unit do some special initialisation. We donot want to spend our resources to uncatchable units, so we spend some time catching and if not successful we print some error message and delete the request. If we want a unit to go back to ROM, it is not enough to catch the PC again and put it back. If f.e. readonly is selected, that must be undone before the PC can go back. Therefore we TELL the unit to go back. Then it has time to clean up. But what if it was the last unit that executed in RAM? Forget the 10k? No, it has to cleanup the TSControl structure as well. Tricky bussiness freeing currently executed memory. A nice exercise! WE must be able to free the structure as well. If we did not catch any unit, we must cleanup the TSControl structure ourselves. So all involved tasks can free about everything. This must be arranged very careful. If we get GURU $81000009, we know we were not careful enough. For each unit there is a data area available. It is organised as the UnitData structure. These are part of the TSControl structure. That way we donot have to allocate it individually and handle involved errors. The structure contains data as an IORequest, the ROM-PC, the address of the TSControl structure, some data fields used in the patch and a command byte: UD_Cmd1. Its a byte in which we tell a unit what to do and what not. The task control block has a member TC_UserData. This is not used by a TD unit. We use it to store a pointer to our UnitData structure. There is another member we can use: UNIT_pad. UNIT_pad is a byte that should be the same as the UD_Cmd1. While it is not, TD will take actions to achieve that. More about this in patch.a. The commandline is scanned and all commands are collected but not executed. Commands are initially gathered in UD_Cmd0. If no error is found in the commandline, the commands are passed from UD_Cmd0 to UD_Cmd1, thus passing them to the unit tasks. Before we can do so, we have done an OpenDevice() for each unit. Now it is known which units are available. If a unit is not transferred, a CloseDevice() is done with it. We have two reasons to do an OpenDevice(). First to find its data area (io_Unit) and TCB (io_Message.mn_ReplyPort->mp_SigTask). Second we donot want a real CloseDevice(), deleting the patched unit task before we can perform our cleanups. (And what if the unit was already closed, huh?) All messages are stored in a buffer and printed later. This program can hold up Trackdisk tasks and must not be blocked by malfunctioning output. */ #include #include #include #include #include #include #include #include "ts.h" #include #pragma syscall FindSemaphore 252 901 /* Use a1 instead of a0 */ #include #define btst(a,b) ((1<TSH_Semaphore.ss_Link.ln_Name=strcpy((char *)Hook+sizeof(struct TSHook),TSSemaphoreName); InitSemaphore((struct SignalSemaphore *)Hook); Enqueue(&SysBase->SemaphoreList,(struct Node *)Hook); } } Permit(); /* * Get exclusive right to modify the Hook and its attachments. Check for commands to perform. */ if (Hook){ ObtainSemaphore((struct SignalSemaphore *)Hook); RVal=XCommands(argc,argv,Hook); ReleaseSemaphore((struct SignalSemaphore *)Hook); } if (RVal>SERIOUS) AddLine("NB! No commands passed or executed!",0); Write(Output(),OutBuf,strlen(OutBuf)); return(RVal); } /********************************************************* * * XCommands * * Build the TSControl structure if it does not exist. Open all possible * Trackdisk devices and scan the command line for commands for them to perform. * Let selected TD units execute in RAM. Allocate the salve buffer if needed. * Release all unused resources and print a status report. * * Input: argc, argv and Hook * * Return: ZERO, WARNING, SERIOUS and FAILURE * */ int __regargs XCommands(argc,argv,Hook) register int argc; char *argv[]; struct TSHook *Hook; { register struct TSControl *Control; register struct UnitData *UD; register short i; register char Collect; int RVal=ZERO; Control=Hook->TSH_TSControl; /* * Check the version number of the anchor. If TD is patched and it is not ours, return * with some message. */ if (Control){ if (Hook->TSH_Version!=TSVersion){ AddLine("Another version of TrackSalve is already active",0); return(FAILURE); } } else Hook->TSH_Version=TSVersion; /* * If the command line has no arguments we just print the present situation. */ if (argc==1) return(ShowTS(Control)); /* * If there is not yet a TSControl structure, build one and connect it with the Hook */ if (!Control){ Control=Hook->TSH_TSControl=AllocTSControl(); R2=R2a; if (!Control) return(FAILURE); Control->TSC_TSHook=Hook; } /* * We have two arrays in the TSControl structure. One is an array of UnitData structures * and the other is an array of pointers to them. If the pointer is zero, the unit is * closed and we try to open it. In that case the unit will run in ROM and does not * execute special functions. So we init UD_Cmd0 and UD_Cmd1 to 0. If the OpenDevice() * fails the pointer stays zero. If the pointer was not zero, the unit is already open * and it executes already in RAM. In that case we are interested in its current * commands and copy them from UD_Cmd1 to UD_Cmd0. * We will not do any IO with the units, therefore our IORquest does not need a port. */ Control->TSC_AvailUnits=0; for (i=0;iTSC_UnitData[i]){ UD->UD_Cmd0=UD->UD_Cmd1; } else{ UD=&Control->TSC_UDAlloc[i]; if (!OpenDevice(TDName,i,(struct IORequest *)&UD->UD_TDReq,TDF_ALLOW_NON_3_5)){ Control->TSC_UnitData[i]=UD; UD->UD_Cmd0=UD->UD_Cmd1=0; } else UD=0; } if (UD) bset(i,Control->TSC_AvailUnits); } /* * Scan the commandline and translate commandline arguments into real commands in UD_Cmd0. * If no serious cmdline error is met, pass for each existing unit UD_Cmd0 to UD_Cmd1. * If an unit has special functions to execute, but is not yet in RAM, see that that is * done. If not successful print a message and clear its commands as a signal to close * the device again (later). If a unit is in RAM, but its UD_Cmd1 is empty, set the * F_TERM flag in it. This will bring the unit to clean up and go back to ROM. */ if (RVal=ScanCmdLine(argc,argv,Control)){ R2=E_CMDLINE; if (RVal>SERIOUS) return(RVal); } for (Collect=0,i=0;iTSC_UnitData[i]){ if ((UD->UD_Cmd1=UD->UD_Cmd0)&FUNCTIONS){ if (!btst(i,Control->TSC_InUse)){ if (PCtoTS(UD)){ AddLine("Cannot find trackdisk task for unit ",Number[i]); UD->UD_Cmd1=0; RVal=SERIOUS; } } Collect|=UD->UD_Cmd1; } else{ if (btst(i,Control->TSC_InUse)){ UD->UD_Cmd1=F_TERM; } } }/*Unit is open*/ }/*For every possible unit*/ /* * If any unit has its salve function active and there is not yet a buffer, try * to allocate it. */ if ((Collect&(F_SALVE+F_VERIFY))&&!Control->TSC_Buffer) if (!(Control->TSC_Buffer=AllocMem(BufferSize,MEMF_CHIP))) RVal=SERIOUS; /* * Close all devices which are open but not executed in RAM, and clear their pointers. */ for (i=0;iTSC_InUse))&&(UD=Control->TSC_UnitData[i])){ if (UD->UD_TDReq.iotd_Req.io_Device>0) CloseDevice((struct IORequest *)&UD->UD_TDReq); Control->TSC_UnitData[i]=0; } } /* * Free TSCcontrol structure if no users of it and untie it from the hook. */ if (!Control->TSC_InUse){ CloseLibrary(IntuitionBase); FreeMem(Control,Control->TSC_Size); Hook->TSH_TSControl=0; } /* * Print a status report and return. */ ShowTS(Hook->TSH_TSControl); return(RVal); } /********************************************************* * * Help * * Scan the commandline for any chars that could be a request for help. * If found print a apropriate message and return. * * Input: argc and argv * * Return: Non zero if help request detected and zero if not. */ int __regargs Help(argc,argv) int argc; char *argv[]; { register int argcnt; register char *Option; register short avail; for (argcnt=1;argcntTSC_AvailUnits; for (i=0;iTSC_UnitData[i]->UD_Cmd0|=F_RAM; } break; case'!': BeStrictOnUnitNumbers=0; break; case'/': CmdUnits0=0; BeStrictOnUnitNumbers=1; break; case'0': UnitNr=0; break; case'1': UnitNr=1; break; case'2': UnitNr=2; break; case'3': UnitNr=3; break; case's': OnCmd|=F_SALVE; break; case't': OffCmd|=F_SALVE; break; case'n': OnCmd|=F_NOCLICK; break; case'c': OffCmd|=F_NOCLICK; break; case'r': OnCmd|=F_READONLY; break; case'w': OffCmd|=F_READONLY; break; case'v': OnCmd|=F_VERIFY; break; case'b': OffCmd|=F_VERIFY; break; case'o': OffCmd|=FUNCTIONS; break; default: AddLine("Not recognised: ",Arg0); R2=E_CMDLINE; return(FAILURE); break; } /* * Check if a unit selection is made. If so, check its presence and act accordingly. * If the unit exists, set its bit in CmdUnits0 and copy CmdUnits0 to CmdUnits1. * Set the B_RAM bit int the UD_Cmd0 of the selected unit. */ if (UnitNr>=0){ if (!btst(UnitNr,Control->TSC_AvailUnits)){ AddLine("Trackdisk does not control this unit: ",Number[UnitNr]); RVal=WARNING; R2=E_CMDLINE; if (BeStrictOnUnitNumbers){ return(FAILURE); } } bset(UnitNr,CmdUnits0); CmdUnits1=CmdUnits0; Control->TSC_UnitData[UnitNr]->UD_Cmd0|=F_RAM; BeStrictOnUnitNumbers=1; } /* * If a command is detected, delete the old unit select base so that later a new unit pattern * can be build. If the pattern is empty, the command is floating, which is an error. * Else apply the command to the units in the pattern CmdUnits1. */ if (OnCmd||OffCmd){ BeStrictOnUnitNumbers=1; CmdUnits0=0; if (!CmdUnits1){ AddLine("Command not applied to a unit: ",Arg0); R2=E_CMDLINE; return(FAILURE); } for (UnitNr=0;UnitNrTSC_UnitData[UnitNr]->UD_Cmd0|=OnCmd; Control->TSC_UnitData[UnitNr]->UD_Cmd0&=~OffCmd; } } } } /* while Avail */ } /* for ArgCnt */ return(RVal); } /********************************************************* * * AllocTSControl * * Find the Trackdisk code in ROM, check version numbers, allocate space for * data and code for the patched Trackdisk tasks. Init the structure as far * as possible, copy our patch extension code into it, relocate it, copy TD * code into it, relocate the TD code and patch it. * * Return: TSControl structure or 0 */ struct TSControl *AllocTSControl() { struct TSControl *Control; struct Resident *TDTag; char *AllocPtr; ULONG Size, TDCodeSize; int i; if (!(IntuitionBase=OpenLibrary("intuition.library",0))){ AddLine("No intuition",0); R2a=E_INTUITION; return(0); } if (!(TDTag=FindResident(TDName))){ AddLine("Did not find resident module: ",TDName); R2a=E_RTAG; return(0); } if ((TDTag->rt_Version!=TD_VERSION1_2)&&(TDTag->rt_Version!=TD_VERSION1_3)){ AddLine("Wrong Trackdisk version (not 33 or 34)",0); R2a=E_TD_VERSION; return(0); } TDCodeSize=(char *)TDTag->rt_EndSkip-(char *)TDTag->rt_MatchTag; Size=sizeof(struct TSControl)+TSCodeSize+TDCodeSize; if (!(AllocPtr=AllocMem(Size,PUBCLR))){ AddLine(LowMem,0); R2a=E_ALLOCMEM; return(0); } Control=(struct TSControl *)AllocPtr; for (i=0;iTSC_UDAlloc[i].UD_TSControl=Control; Control->TSC_IntuBase=IntuitionBase; Control->TSC_Size=Size; Control->TSC_TDTag=TDTag; Control->TSC_TDCodeSize=TDCodeSize; CopyMem((char *)TSCodeBegin,AllocPtr+=sizeof(struct TSControl),TSCodeSize); Control->TSC_TSCode=AllocPtr; Control->TSC_TSCodeSize=TSCodeSize; RelocateTS(Control); CopyMem((char *)TDTag,AllocPtr+=TSCodeSize,TDCodeSize); Control->TSC_TSTag=(struct Resident *)AllocPtr; RelocateTD(Control); PatchMem(Control->TSC_TSTag,TSPatchTable); InitSemaphore(&Control->TSC_OwnBuffer); return(Control); } /********************************************************* * * ShowTS * * Build a string which shows the modifications presently active * on the various TD-drives. * * Input: TSConTrol structure */ __regargs ShowTS(Control) struct TSControl *Control; { register short i; UBYTE Func, Collect; int RVal=0; static char On[]= " On "; static char Off[]=" Off "; static char Onf[]=" Not Yet"; if (!Control||!Control->TSC_InUse){ AddLine("Trackdisk not patched for any drive",0); } else{ Collect=0; AddLine("Present situation:",0); AddStr(" Unit Code Verify Salve NoClick ReadOnly\n"); for (i=0;iTSC_AvailUnits)){ AddStr(" "); AddStr(Number[i]); if (Control->TSC_UnitData[i]){ Func=Control->TSC_UnitData[i]->UD_Cmd1; if (Func&F_TERM){ AddStr(" Terminating"); } else{ Collect|=Func; AddStr(" Patched "); AddStr((Func&F_VERIFY)?(Control->TSC_Buffer?On:Onf):Off); AddStr((Func&F_SALVE)?(Control->TSC_Buffer?On:Onf):Off); AddStr((Func&F_NOCLICK)?On:Off); AddStr((Func&F_READONLY)?On:Off); } } else{ AddStr(" Original"); } AddStr("\n"); } } if ((Collect&(F_VERIFY+F_SALVE))&&!Control->TSC_Buffer){ AddStr("Low chip memory, Salve and/or Verify functions not yet active!!\n"); RVal=SERIOUS; } } return(RVal); } /********************************************************* * * AddLine - AddStr * * Copy string arguments into the stringbuffer */ void __regargs AddLine(a,b) char *a,*b; { AddStr(Pr); AddStr(": "); AddStr(a); AddStr(b); AddStr("\n"); } void __regargs AddStr(a) char *a; { static short Size=MSGBUFSIZE-2; short Len; if (a){ Len=strlen(a); if (Len