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 >
Wrap
C/C++ Source or Header
|
1990-06-12
|
24KB
|
780 lines
/*
* Main.c - Copyright 1990 D.W.Reisig
*
* # date by Comment
* -- --------- ----- ---------------------
* 0 29-Oct-89 DWR Created for Tracksalve
* 1 10-Apr-90 DWR TrackSalve 1.3, switches added, new status report
*
*/
/*
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 two points
where the PC will be most of its time. We will switch PC only if the unit task
is here. 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 <exec/types.h>
#include <exec/memory.h>
#include <exec/resident.h>
#include <exec/execbase.h>
#include <exec/semaphores.h>
#include <hardware/blit.h>
#include <devices/trackdisk.h>
#include <string.h>
#include "ts.h"
#include <proto/exec.h>
#pragma syscall FindSemaphore 252 901 /* Use a1 instead of a0 */
#include <string.h>
#define btst(a,b) ((1<<a)&b)
#define bset(a,b) (b|=1<<a)
#define bclr(a,b) (b&=~(1<<a))
extern struct Resident *FindResident();
extern char *strcpy();
extern char *TSCodeBegin(); /* Not a function but begin of extension code */
extern LONG R2; /* Secondary result code (Why uses it) */
extern char Cr[]; /* Creation/Version line */
extern ULONG TSCodeSize; /* Size of extension code (should be a constant) */
extern ULONG BufferSize; /* Size of a chip buffer used for tracksalvage and verify */
extern void TSPatchTable(); /* Not realy a function but data in a code hunk */
extern struct ExecBase *SysBase;
extern ULONG TSVersion; /* Perhaps generations are not compatible, check it! */
int __regargs PatchMem(struct Resident *, void (*)());
int __regargs XCommands(int, char *[], struct TSHook *);
int __regargs Help(int, char *[]);
int __regargs ScanCmdLine(int, char *[], struct TSControl *);
int __regargs PCtoTS(struct UnitData *);
int __regargs ShowTS(struct TSControl *);
void __regargs ConMsg(char *);
void __regargs RelocateTS(struct TSControl *);
void __regargs RelocateTD(struct TSControl *);
void __regargs AddLine(char *, char *);
void __regargs AddStr(char *);
struct TSControl *AllocTSControl();
#define MSGBUFSIZE 750 /* Little careful with this, stack checking is disabled */
char *OP; /* Pointer into the message buffer */
char *Pr; /* Program name from the commandline */
LONG R2a; /* Error from subs which return a pointer */
struct Library *IntuitionBase; /* We find Intuition for the unit tasks (Requesters) */
char TDName[]=TD_NAME;
char TSSemaphoreName[]=TSSEMAPHORENAME;
char LowMem[]="Did not get memory";
char *Number[]={ "0","1","2","3" };
/*********************************************************
*
* main
*
* Scan the commandline for the help command. If found print the text and leave.
* Set up a buffer to collect all output. Find or create the semaphore that controls
* access to the TSControl structure. Call XCommands and print the buffer.
*
* Input: argc and argv
*
* Return: ZERO or return value from XCommands()
*
*/
__stdargs main(argc,argv)
int argc;
char *argv[];
{
register struct TSHook *Hook;
register int RVal;
char OutBuf[MSGBUFSIZE]; /* Buffer on the stack */
OP=OutBuf; /* Init bufferpointer */
*OP='\0';
Pr=argv[0]; /* Adapt programname from the commandline (WB already in startup disabled) */
if (Help(argc,argv)) /* Scan cmdline for help function */
return(ZERO);
/*
* Why spend time to protect this program against people who try to mess things up?
* Anyway, protect the creation of the Semaphore against doublure by disabling
* taskswitching during search, creation and instalation.
*/
Forbid();
if (!(Hook=(struct TSHook *)FindSemaphore(TSSemaphoreName))){
if (!(Hook=(struct TSHook *)AllocMem(sizeof(struct TSHook)+sizeof(TSSemaphoreName),PUBCLR))){
AddLine(LowMem,0);
R2=E_ALLOCMEM;
RVal=FAIL;
} else{
Hook->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>ERROR)
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, WARN, ERROR and FAIL
*
*/
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;
register 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(FAIL);
}
} 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(FAIL);
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;i<NUMUNITS;++i){
if (UD=Control->TSC_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>ERROR) return(RVal);
}
for (Collect=0,i=0;i<NUMUNITS;++i){
if (UD=Control->TSC_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=ERROR;
}
}
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_VERIFY1+F_VERIFY0))&&!Control->TSC_Buffer)
if (!(Control->TSC_Buffer=AllocMem(BufferSize,MEMF_CHIP))) RVal=ERROR;
/*
* Close all devices which are open but not executed in RAM, and clear their pointers.
*/
for (i=0;i<NUMUNITS;++i){
if ((!btst(i,Control->TSC_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;argcnt<argc;++argcnt){
Option=argv[argcnt];
avail=1;
while (avail){
switch(*(Option++)){
case'?':
case'h':
case'H':
if (*Option=='?') ConMsg(Cr);
else Explain();
return(-1);
case'\0':
avail=0;
}
}
}
return(0);
}
/*********************************************************
*
* ScanCmdLine
*
* Scan the commandline for unit numbers and commands. Check whether unit numbers
* select existing units. Return FAIL if a non-existing unit is selected unless
* a special option us used. Modify the UD_Cmd0 member of the selected units.
* Unrecognised chars result in a FAIL.
*
* Input: argc and argv
* TSControl structure
*
* Return: ZERO if no errors detected,
* WARN if a special option is used together with the selection of a non existing unit
* FAIL if a non existing unit is selected or a command is given without a selected
* unit, or a not recognised command is detected.
*/
int __regargs ScanCmdLine(argc,argv,Control)
int argc;
char *argv[];
struct TSControl *Control;
{
short ArgCnt;
register short i, UnitNr;
char *Argument, *Arg0;
register UBYTE OnCmd, OffCmd;
UBYTE BeStrictOnUnitNumbers, Avail, CmdUnits0, CmdUnits1;
int RVal;
RVal=0;
CmdUnits0=0;
CmdUnits1=0;
BeStrictOnUnitNumbers=1;
for (ArgCnt=1;ArgCnt<argc;++ArgCnt){
Argument=argv[ArgCnt];
Avail=1;
while (Avail){
OnCmd=0;
OffCmd=0;
UnitNr=-1;
switch(tolower(*(Arg0=Argument++))){
case'\0':
Avail=0;
case'\t':
case' ':
case',':
case'-':
break;
case'a':
CmdUnits0=CmdUnits1=Control->TSC_AvailUnits;
for (i=0;i<NUMUNITS;++i){
if (btst(i,CmdUnits1))
Control->TSC_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'u':
OnCmd=F_UPDATE;
break;
case'e':
OffCmd=F_UPDATE;
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':
OffCmd=(F_VERIFY1+F_VERIFY0);
OnCmd=F_VERIFY0;
while (tolower(*Argument)=='v'){
++Argument;
if (OnCmd!=(F_VERIFY1+F_VERIFY0))
OnCmd+=F_VERIFY0;
}
break;
case'b':
OffCmd=(F_VERIFY1+F_VERIFY0);
break;
case'o':
OffCmd=FUNCTIONS;
break;
default:
AddLine("Not recognised: ",Arg0);
R2=E_CMDLINE;
return(FAIL);
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=WARN;
R2=E_CMDLINE;
if (BeStrictOnUnitNumbers)
return(FAIL);
} else{
bset(UnitNr,CmdUnits0);
Control->TSC_UnitData[UnitNr]->UD_Cmd0|=F_RAM;
}
CmdUnits1=CmdUnits0;
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(FAIL);
}
for (UnitNr=0;UnitNr<NUMUNITS;++UnitNr){
UBYTE *Cmd0=&Control->TSC_UnitData[UnitNr]->UD_Cmd0;
if (btst(UnitNr,CmdUnits1)){
*Cmd0&=~OffCmd;
*Cmd0|=OnCmd;
}
}
}
} /* 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()
{
register struct TSControl *Control;
register struct Resident *TDTag;
char *AllocPtr;
register ULONG TDCodeSize, Size;
register 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;i<NUMUNITS;++i)
Control->TSC_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)
register struct TSControl *Control;
{
register short i;
register UBYTE Func, Collect;
int RVal=0;
static char On[]=" On ";
static char Off[]=" Off ";
static char NY[]=" Not Yet";
static char *OnN[]={
" On 1 ",
" On 2 ",
" On 3 "
};
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 Update\n");
for (i=0;i<NUMUNITS;++i){
if (btst(i,Control->TSC_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_VERIFY1+F_VERIFY0))?(Control->TSC_Buffer? OnN[(Func&(F_VERIFY1+F_VERIFY0))-1]:NY):Off);
AddStr((Func&F_SALVE)?(Control->TSC_Buffer?On:NY):Off);
AddStr((Func&F_NOCLICK)?On:Off);
AddStr((Func&F_READONLY)?On:Off);
AddStr((Func&F_UPDATE)?On:Off);
}
} else{
AddStr(" Original");
}
AddStr("\n");
}
}
if ((Collect&(F_VERIFY1+F_VERIFY0+F_SALVE))&&!Control->TSC_Buffer){
AddStr("Low chip memory, Salve and/or Verify functions not yet active!!\n");
RVal=ERROR;
}
}
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)
register char *a;
{
static short Size=MSGBUFSIZE-2;
register short Len;
if (a){
Len=strlen(a);
if (Len<Size){
OP=stpcpy(OP,a);
Size-=Len;
}
else Size=0;
}
}