home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Format CD 18
/
amigaformatcd18.iso
/
-in_the_mag-
/
emulation
/
emus
/
fs1541
/
volume.c
< prev
next >
Wrap
C/C++ Source or Header
|
1997-07-31
|
13KB
|
616 lines
/*
FS1541
volume.c
Volume, BAM and lock handling.
*/
#include <stdio.h> /* contains decl of sprintf, but will be linked from amiga.lib! */
#include <string.h>
#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>
#include <dos/dosextens.h>
#include <dos/filehandler.h>
#include <devices/trackdisk.h>
#include <devices/timer.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/alib.h>
#include <proto/utility.h>
#include "main.h"
#include "volume.h"
#include "disk.h"
#include "packet.h"
#include "support.h"
BYTE diskchgintbit = -1;
struct VolumeNode *curvolumenode = NULL;
struct DosList *curdoslist = NULL;
int disk_inserted = FALSE;
struct BAM *bam;
struct DirEntry directory[144];
int dirsize;
UBYTE interleave = 4;
static struct MinList volumelist;
static struct IOExtTD *diskchgint_req;
static void diskchginthandler(void)
{
Signal(ourtask, 1<<diskchgintbit);
}
static struct Interrupt diskchgint =
{
{ NULL, NULL, NT_INTERRUPT, 0, "FS1541" },
NULL,
(APTR)&diskchginthandler
};
static struct MsgPort *UDStimerport;
struct timerequest *UDStimer;
static int timeropen = 0;
static void CreateDollar(struct VolumeNode *node);
/*-------------------------------------------------------------------------*/
LONG InitVolumeSS(void)
{
LONG error = 0;
if((UDStimerport = CreateMsgPort()))
{
if((UDStimer = CreateIORequest(UDStimerport, sizeof(struct timerequest))))
{
if((timeropen = (!OpenDevice(TIMERNAME,UNIT_VBLANK,(struct IORequest *)UDStimer,0))))
{
if((diskchgintbit = AllocSignal(-1))>=0)
{
if((diskchgint_req = AllocVec(sizeof(struct IOExtTD), MEMF_PUBLIC)))
{
NewList((struct List*)&volumelist);
CopyMem(diskreq, diskchgint_req, sizeof(struct IOExtTD));
diskchgint_req->iotd_Req.io_Command = TD_ADDCHANGEINT;
diskchgint_req->iotd_Req.io_Data = &diskchgint;
diskchgint_req->iotd_Req.io_Length = sizeof(struct Interrupt);
diskchgint_req->iotd_Req.io_Flags = 0;
SendIO((struct IORequest*)diskchgint_req);
return(0);
} else error = ERROR_NO_FREE_STORE;
} else error = ERROR_NO_FREE_STORE;
} else error = ERROR_DEVICE_NOT_MOUNTED;
} else error = ERROR_NO_FREE_STORE;
} else error = ERROR_NO_FREE_STORE;
QuitVolumeSS();
return(error);
}
void QuitVolumeSS(void)
{
if(diskchgint_req)
{
diskchgint_req->iotd_Req.io_Command = TD_REMCHANGEINT;
diskchgint_req->iotd_Req.io_Data = &diskchgint;
diskchgint_req->iotd_Req.io_Length = sizeof(struct Interrupt);
diskchgint_req->iotd_Req.io_Flags = 0;
DoIO((struct IORequest*)diskchgint_req);
FreeVec(diskchgint_req);
}
if(diskchgintbit >= 0)
FreeSignal(diskchgintbit);
if(timeropen) {
StopUDSTimer();
CloseDevice((struct IORequest*)UDStimer);
}
if(UDStimer)
DeleteIORequest(UDStimer);
if(UDStimerport)
DeleteMsgPort(UDStimerport);
}
/*-------------------------------------------------------------------------*/
void DoDiskInsert(void)
{
diskreq->iotd_Req.io_Command = TD_CHANGESTATE;
diskreq->iotd_Req.io_Flags = IOF_QUICK;
DoIO((struct IORequest*)diskreq);
if(diskreq->iotd_Req.io_Actual)
{
/* Switch off the motor. */
diskreq->iotd_Req.io_Command = TD_MOTOR;
diskreq->iotd_Req.io_Flags = 0;
diskreq->iotd_Req.io_Length = 0;
DoIO((struct IORequest*)diskreq);
}
else
{
/* Disk has been inserted. */
int i,t,s;
UBYTE diskname[20];
struct VolumeNode *node;
disk_inserted = TRUE;
ResetDisk();
wprotected = hardwprot;
/* Read Block Allocation Map */
if(!(bam = (struct BAM*)getblock_ts(18,0)))
return;
if(bam->id != 'A')
wprotected = TRUE;
/* Read directory */
for(i=0, dirsize=0, t=18/*bam->dirt*/, s=1/*bam->dirs*/; /* 1541 always uses 18,1 */
i<18;
t=directory[8*i].t, s=directory[8*i].s, i++)
{
struct DataBlock *block;
if(!(block = getblock_ts(t, s)))
return;
CopyMem(block, &directory[8*i], 256);
dirsize += 8;
if(!directory[8*i].t)
break;
}
/* Strip trailing type 0x00 entries */
for(i=dirsize; --i>=0 && directory[i].type == 0x00; dirsize--);
/* Check if this is a volume we know */
copy64name(diskname, bam->name, 16);
for(node=(struct VolumeNode*)volumelist.mlh_Head;
node->node.mln_Succ;
node=(struct VolumeNode*)(node->node.mln_Succ))
{
if(!Stricmp(diskname,&node->name[1]))
{
while(!AttemptLockDosList(LDF_VOLUMES|LDF_WRITE))
DoPackets();
curvolumenode = node;
curdoslist = node->volnode;
curdoslist->dol_Task = ourport;
curdoslist->dol_misc.dol_volume.dol_LockList = NULL;
UnLockDosList(LDF_VOLUMES|LDF_WRITE);
SendEvent(TRUE);
return;
}
}
/* Create a new volume node */
if((node = AllocVec(sizeof(struct VolumeNode), MEMF_PUBLIC|MEMF_CLEAR)))
{
struct DosList *newvol;
if((newvol = AllocVec(sizeof(struct DosList), MEMF_PUBLIC|MEMF_CLEAR)))
{
/* Generate DosList entry (Volume) */
LONG rc;
newvol->dol_Type = DLT_VOLUME;
newvol->dol_Task = ourport;
newvol->dol_misc.dol_volume.dol_DiskType = ID_DOS_DISK;
newvol->dol_Name = (BSTR)MKBADDR(&node->name);
copy64name(&node->name[1], bam->name, 16);
node->name[0] = strlen(&node->name[1]);
node->volnode = newvol;
AddHead((struct List*)&volumelist, (struct Node*)node);
while(!AttemptLockDosList(LDF_VOLUMES|LDF_WRITE))
DoPackets();
rc = AddDosEntry(newvol);
UnLockDosList(LDF_VOLUMES|LDF_WRITE);
if(rc)
{
curvolumenode = node;
curdoslist = newvol;
CreateDollar(node);
SendEvent(TRUE);
MotorOff();
return;
}
FreeVec(newvol);
}
FreeVec(node);
}
}
MotorOff();
}
void DoDiskRemove(void)
{
disk_inserted = FALSE;
if(curvolumenode)
{
if(!curvolumenode->locklist)
{
/* No locks -> remove completely */
while(!AttemptLockDosList(LDF_VOLUMES|LDF_WRITE))
DoPackets();
RemDosEntry(curdoslist);
Remove((struct Node*)curvolumenode);
FreeVec(curvolumenode);
UnLockDosList(LDF_VOLUMES|LDF_WRITE);
SendEvent(FALSE);
}
else
{
/* Do not remove completely, leave disk icon on Workbench. */
while(!AttemptLockDosList(LDF_VOLUMES|LDF_WRITE))
DoPackets();
curdoslist->dol_Task = NULL;
curdoslist->dol_misc.dol_volume.dol_LockList = (BPTR)curvolumenode->locklist;
UnLockDosList(LDF_VOLUMES|LDF_WRITE);
SendEvent(FALSE);
}
curvolumenode = NULL;
curdoslist = NULL;
}
}
/*-------------------------------------------------------------------------*/
static void CreateDollar(struct VolumeNode *node)
{
UBYTE buf[32], *p=node->dollarbuf;
int i,j;
static char *types[8] = { "DEL", "SEQ", "PRG", "USR", "REL", "???", "???", "???" };
copy64name(buf, bam->name2, 5);
if(bam->name2[2] == 0xA0)
{
buf[2] = ' ';
copy64name(&buf[3], &bam->name2[3], 2);
}
sprintf(p, "0 \033[7m\"%-16s\" %-5s\033[0m\n", &node->name[1], buf);
p += strlen(p);
for(i=0;i<dirsize;i++)
{
UBYTE type;
if((type=directory[i].type) == 0x00)
continue;
copy64name(buf, directory[i].name, 16);
sprintf(p, "%-5ld\"%s\"", (ULONG)(directory[i].lengthh<<8)|directory[i].lengthl, buf);
p += strlen(p);
for(j=0;j<=(16-strlen(buf));j++)
*p++ = ' ';
sprintf(p, "%s%lc\n", types[type&7], type&0x80 ? (type&0x40 ? '<' : ' ') : '*');
p += strlen(p);
}
sprintf(p, "%ld BLOCKS FREE.\n", (ULONG)(683-UsedBlocks()));
p += strlen(p);
node->dollarlen = (ULONG)(p - node->dollarbuf);
}
/*-------------------------------------------------------------------------*/
static ULONG bam_secmask[21] = {
1<<16, 1<<17, 1<<18, 1<<19, 1<<20, 1<<21, 1<<22, 1<<23,
1<<8, 1<<9, 1<<10, 1<<11, 1<<12, 1<<13, 1<<14, 1<<15,
1<<0, 1<<1, 1<<2, 1<<3, 1<<4
};
static BYTE SearchFreeBlockOnTrack(UBYTE track, UBYTE froms)
{
UBYTE i,s,numsecs;
ULONG mask, plan=bam->tracks[track-1];
if(plan>>24 > 0)
{
if(track<=17)
numsecs = 21;
else if(track<=24)
numsecs = 19;
else if(track<=30)
numsecs = 18;
else
numsecs = 17;
s = froms + interleave;
for(i=0;i<numsecs;i++,s++)
{
s = s % numsecs;
mask = bam_secmask[s];
if(plan & mask)
{
/* Free sector found. */
plan &= ~mask;
plan -= (1<<24);
bam->tracks[track-1] = plan;
return(s);
}
}
/* If we reach this point, the BAM is corrupted... */
}
return(-1);
}
UWORD AllocBlock(UBYTE fromt, UBYTE froms)
{
UBYTE track = fromt;
BYTE sector;
BOOL flag = 0;
BYTE dir = track<18 ? -1 : 1;
while(1)
{
if(track == 18)
track += dir;
if((sector = SearchFreeBlockOnTrack(track, track==fromt ? froms : 0)) >= 0)
return((track<<8)|sector);
track += dir;
if(track == 0)
{
if(flag)
return(0);
flag++;
dir = 1;
track = fromt+1;
}
if(track == 36)
{
if(flag)
return(0);
flag++;
dir = -1;
track = fromt-1;
}
}
}
void FreeBlock(UBYTE t, UBYTE s)
{
if(t>=1 && t<=35)
{
UBYTE numsecs;
if(t<=17)
numsecs = 21;
else if(t<=24)
numsecs = 19;
else if(t<=30)
numsecs = 18;
else
numsecs = 17;
if(s<numsecs)
{
ULONG plan = bam->tracks[t-1];
plan |= bam_secmask[s];
plan += (1<<24);
bam->tracks[t-1] = plan;
}
}
}
UWORD UsedBlocks(void)
{
int i;
UWORD r=683;
if(curvolumenode)
{
for(i=1;i<=17;i++)
r -= bam->tracks[i-1]>>24;
for(i=19;i<=35;i++)
r -= bam->tracks[i-1]>>24;
}
return(r);
}
/*-------------------------------------------------------------------------*/
void OptimizeDirectory(void)
{
int i, newdirsize=0;
/* Throw out all DEL files */
for(i=0;i<dirsize;i++)
if(((directory[i].type) & 0x7) != 0x00)
memmove(&directory[newdirsize++], &directory[i], sizeof(struct DirEntry));
dirsize = newdirsize;
memset(&directory[dirsize], 0, (144-newdirsize)*sizeof(struct DirEntry));
/* Clear all Track/Sector fields */
for(i=0;i<dirsize;i++) {
directory[i].t = 0;
directory[i].s = 0;
}
}
/*-------------------------------------------------------------------------*/
void StopUDSTimer(void)
{
if(!CheckIO((struct IORequest*)UDStimer)) {
AbortIO((struct IORequest*)UDStimer);
WaitIO((struct IORequest*)UDStimer);
}
SetSignal(0, 1<<(UDStimerport->mp_SigBit));
}
void StartUDSTimer(void)
{
StopUDSTimer();
UDStimer->tr_time.tv_secs = 1;
UDStimer->tr_time.tv_micro = 0;
UDStimer->tr_node.io_Command = TR_ADDREQUEST;
SendIO((struct IORequest*)UDStimer);
}
void UpdateDiskStructure(void)
{
int i,n;
UBYTE s,s2;
if(!CheckIO((struct IORequest*)UDStimer))
return;
StopUDSTimer();
/* Strip trailing type 0x00 entries */
for(i=dirsize; --i>=0 && directory[i].type == 0x00; dirsize--);
/* Write BAM and directory blocks */
bam->dirt = 18;
bam->dirs = 1;
bam->tracks[18-1] = 0x11fcff07; /* Block 0 is BAM and Block 1 is first directory block */
i = dirsize>>3;
s2 = 1;
for(n=0;n<=i;n++)
{
s = s2;
if(n<i)
{
s2 = SearchFreeBlockOnTrack(18,s); /* will always succeed */
directory[n<<3].t = 18;
directory[n<<3].s = s2;
}
else
{
directory[n<<3].t = 0;
directory[n<<3].s = 0xff;
}
putblock_ts(18,s,&directory[n<<3]);
}
putblock_ts(18,0,bam);
MotorOff();
CreateDollar(curvolumenode);
}
/*-------------------------------------------------------------------------*/
BPTR makelock(LONG flkey, LONG axs)
{
struct FileLock *fl;
if((fl = AllocVec(sizeof(struct FileLock), MEMF_PUBLIC|MEMF_CLEAR)))
{
fl->fl_Key = flkey;
fl->fl_Access = axs;
fl->fl_Task = ourport;
fl->fl_Volume = MKBADDR(curdoslist);
fl->fl_Link = (BPTR)curvolumenode->locklist;
curvolumenode->locklist = fl;
}
return(MKBADDR(fl));
}
void freelock(struct FileLock *fl)
{
struct VolumeNode *node;
for(node=(struct VolumeNode*)volumelist.mlh_Head;
node->node.mln_Succ;
node=(struct VolumeNode*)(node->node.mln_Succ))
{
struct FileLock *cur,*last;
for(cur=node->locklist, last=(struct FileLock*)&node->locklist;
cur;
last=cur, cur=(struct FileLock*)last->fl_Link)
{
if(cur == fl)
{
last->fl_Link = cur->fl_Link;
fl->fl_Task = NULL;
FreeVec(fl);
if(!node->locklist && !node->volnode->dol_Task)
{
/* An unmounted volume does not have any locks open
any more, so we can safely remove the volume node. */
while(!AttemptLockDosList(LDF_VOLUMES|LDF_WRITE))
DoPackets();
RemDosEntry((struct DosList*)node->volnode);
Remove((struct Node*)node);
FreeVec(node);
UnLockDosList(LDF_VOLUMES|LDF_WRITE);
SendEvent(FALSE);
}
return;
}
}
}
}
/* Is an object still lockable with the respective access mode? */
BOOL lockable(UBYTE t, UBYTE s, LONG mode)
{
struct FileLock *fl;
LONG searchkey = t<<8|s;
for(fl=curvolumenode->locklist; fl; fl=(struct FileLock*)fl->fl_Link)
{
if(fl->fl_Key == searchkey)
{
if(mode == EXCLUSIVE_LOCK)
return(FALSE);
else
{
if(fl->fl_Access == EXCLUSIVE_LOCK)
return(FALSE);
}
}
}
return(TRUE);
}