Files
Digital-Research-Source-Code/CPM OPERATING SYSTEMS/CPM 68K/1.0X SOURCES/v101/bdos/fileio.c
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

660 lines
14 KiB
C

/****************************************************************
* *
* CP/M-68K BDOS File I/O Module *
* *
* This module contains all file handling BDOS functions *
* except for read and write for CP/M-68K. Included are: *
* *
* seldsk() - select disk *
* openfile() - open file *
* close_fi() - close file *
* search() - search for first/next file match *
* create() - create file *
* delete() - delete file *
* rename() - rename file *
* set_attr() - set file attributes *
* getsize() - get file size *
* setran() - set random record field *
* free_sp() - get disk free space *
* move() - general purpose byte mover *
* *
* *
* Compiled with Alcyon C on the VAX *
* *
****************************************************************/
#include "bdosinc.h" /* Standard I/O declarations */
#include "bdosdef.h" /* Type and structure declarations for BDOS */
#include "pktio.h" /* Packet I/O definitions */
/* declare external fucntions */
EXTERN UWORD dirscan(); /* directory scanning routine */
EXTERN UWORD error(); /* disk error routine */
EXTERN UWORD ro_err(); /* read-only file error routine */
EXTERN UWORD do_phio(); /* packet disk i/o handler */
EXTERN clraloc(); /* clear bit in allocation vector */
EXTERN setaloc(); /* set bit in allocation vector */
EXTERN UWORD swap(); /* assembly language byte swapper */
EXTERN UWORD dir_wr(); /* directory write routine */
EXTERN tmp_sel(); /* temporary select disk routine */
EXTERN UWORD calcext(); /* calc max extent allocated for fcb */
EXTERN UWORD udiv(); /* unsigned divide routine */
/* declare external variables */
EXTERN UWORD log_dsk; /* logged-on disk vector */
EXTERN UWORD ro_dsk; /* read-only disk vector */
EXTERN UWORD crit_dsk; /* vector of disks in critical state */
/************************************
* This function passed to dirscan *
* from seldsk (below) *
************************************/
BOOLEAN alloc(fcbp, dirp, dirindx)
/* Set up allocation vector for directory entry pointed to by dirp */
struct fcb *fcbp; /* not used in this function */
REG struct dirent *dirp; /* pointer to directory entry */
WORD dirindx; /* index into directory for *dirp */
{
REG WORD i; /* loop counter */
BSETUP
if ( UBWORD(dirp->entry) < 0x10 ) /* skip MP/M 2.x and CP/M 3.x XFCBs */
{
(GBL.dphp)->hiwater = dirindx; /* set up high water mark for disk */
i = 0;
if ((GBL.parmp)->dsm < 256)
{
do setaloc( UBWORD(dirp->dskmap.small[i++]) );
while (i <= 15);
}
else
{
do setaloc(swap(dirp->dskmap.big[i++]));
while (i <= 7);
}
}
}
/************************
* seldsk entry point *
************************/
seldsk(dsknum)
REG UBYTE dsknum; /* disk number to select */
{
struct iopb selpkt;
REG WORD i;
UWORD j;
REG UBYTE logflag;
BSETUP
logflag = ~(log_dsk >> dsknum) & 1;
if ((GBL.curdsk != dsknum) || logflag)
{ /* if not last used disk or not logged on */
selpkt.iofcn = sel_info;
GBL.curdsk = (selpkt.devnum = dsknum);
if (UBWORD(dsknum) > 15) error(2);
selpkt.ioflags = logflag ^ 1;
do
{
do_phio(&selpkt); /* actually do the disk select */
if ( (GBL.dphp = selpkt.infop) != NULL ) break;
} while ( ! error(3) );
GBL.dirbufp = (GBL.dphp)->dbufp;
/* set up GBL copies of dir_buf and dpb ptrs */
GBL.parmp = (GBL.dphp)->dpbp;
}
if (logflag)
{ /* if disk not previously logged on, do it now */
LOCK /* must lock the file system while messing with alloc vec */
i = (GBL.parmp)->dsm;
do clraloc(i); while (i--); /* clear the allocation vector */
i = udiv( (LONG)(((GBL.parmp)->drm) + 1),
4 * (((GBL.parmp)->blm) + 1), &j);
/* calculate nmbr of directory blks */
if (j) i++; /* round up */
do setaloc(--i); while (i); /* alloc directory blocks */
dirscan(alloc, NULL, 0x0e); /* do directory scan & alloc blocks */
log_dsk |= 1 << dsknum; /* mark disk as logged in */
}
}
/*******************************
* General purpose byte mover *
*******************************/
move(p1, p2, i)
REG BYTE *p1;
REG BYTE *p2;
REG WORD i;
{
while (i--)
*p2++ = *p1++;
}
/*************************************
* General purpose filename matcher *
*************************************/
BOOLEAN match(p1, p2, chk_ext)
REG UBYTE *p1;
REG UBYTE *p2;
BOOLEAN chk_ext;
{
REG WORD i;
REG UBYTE temp;
BSETUP
i = 12;
do
{
temp = (*p1 ^ '?');
if ( ((*p1++ ^ *p2++) & 0x7f) && temp )
return(FALSE);
i -= 1;
} while (i);
if (chk_ext)
{
if ( (*p1 != '?') && ((*p1 ^ *p2) & ~((GBL.parmp)->exm)) )
return(FALSE);
p1 += 2;
p2 += 2;
if ((*p1 ^ *p2) & 0x3f) return(FALSE);
}
return(TRUE);
}
/************************
* openfile entry point *
************************/
BOOLEAN openfile(fcbp, dirp, dirindx)
REG struct fcb *fcbp; /* pointer to fcb for file to open */
struct dirent *dirp; /* pointer to directory entry */
WORD dirindx;
{
REG UBYTE fcb_ext; /* extent field from fcb */
REG BOOLEAN rtn;
BSETUP
if ( rtn = match(fcbp, dirp, TRUE) )
{
fcb_ext = fcbp->extent; /* save extent number from user's fcb */
move(dirp, fcbp, sizeof *dirp);
/* copy dir entry into user's fcb */
fcbp->extent = fcb_ext;
fcbp->s2 |= 0x80; /* set hi bit of S2 (write flag) */
crit_dsk |= 1 << (GBL.curdsk);
}
return(rtn);
}
/*************************/
/* flush buffers routine */
/*************************/
UWORD flushit()
{
REG UWORD rtn; /* return code from flush buffers call */
struct iopb flushpkt; /* I/O packet for flush buffers call */
flushpkt.iofcn = flush;
while ( rtn = do_phio(&flushpkt) )
if ( error(1) ) break;
return(rtn);
}
/*********************************
* file close routine for dirscan *
*********************************/
BOOLEAN close(fcbp, dirp, dirindx)
REG struct fcb *fcbp; /* pointer to fcb */
REG struct dirent *dirp; /* pointer to directory entry */
WORD dirindx; /* index into directory */
{
REG WORD i;
REG UBYTE *fp;
REG UBYTE *dp;
REG UWORD fcb_ext;
REG UWORD dir_ext;
BSETUP
if ( match(fcbp, dirp, TRUE) )
{ /* Note that FCB merging is done here as a final
confirmation that disks haven't been swapped */
LOCK
fp = &(fcbp->dskmap.small[0]);
dp = &(dirp->dskmap.small[0]);
if ((GBL.parmp)->dsm < 256)
{ /* Small disk map merge routine */
i = 16;
do
{
if (*dp)
{
if (*fp)
{
if (*dp != *fp) goto badmerge;
}
else *fp = *dp;
}
else *dp = *fp;
fp += 1;
dp += 1;
i -= 1;
} while (i);
}
else
{ /* Large disk map merge routine */
i = 8;
do
{
if (*(UWORD *)dp)
{
if (*(UWORD *)fp)
{
if (*(UWORD *)dp != *(UWORD *)fp) goto badmerge;
}
else *(UWORD *)fp = *(UWORD *)dp;
}
else *(UWORD *)dp = *(UWORD *)fp;
(UWORD *)fp += 1;
(UWORD *)dp += 1;
i -= 1;
} while (i);
}
/* Disk map merging complete */
fcb_ext = calcext(fcbp); /* calc max extent for fcb */
dir_ext = (UWORD)(dirp->extent) & 0x1f;
if ( (fcb_ext > dir_ext) ||
((fcb_ext == dir_ext) &&
(UBWORD(fcbp->rcdcnt) > UBWORD(dirp->rcdcnt))) )
/* if fcb points to larger file than dirp */
{
dirp->rcdcnt = fcbp->rcdcnt; /* set up rc, ext from fcb */
dirp->extent = (BYTE)fcb_ext;
}
dirp->s1 = fcbp->s1;
if ( (dirp->ftype[robit]) & 0x80) ro_err(fcbp,dirindx);
/* read-only file error */
dirp->ftype[arbit] &= 0x7f; /* clear archive bit */
dir_wr(dirindx >> 2);
UNLOCK
return(TRUE);
badmerge:
UNLOCK
ro_dsk |= (1 << GBL.curdsk);
return(FALSE);
}
else return(FALSE);
}
/************************
* close_fi entry point *
************************/
UWORD close_fi(fcbp)
struct fcb *fcbp; /* pointer to fcb for file to close */
{
flushit(); /* first, flush the buffers */
if ((fcbp->s2) & 0x80) return(0); /* if file write flag not on,
don't need to do physical close */
return( dirscan(close, fcbp, 0)); /* call dirscan with close function */
}
/************************
* search entry point *
************************/
/* First two functions for dirscan */
BOOLEAN alltrue(p1, p2, i)
UBYTE *p1;
UBYTE *p2;
WORD i;
{
return(TRUE);
}
BOOLEAN matchit(p1, p2, i)
UBYTE *p1;
UBYTE *p2;
WORD i;
{
return(match(p1, p2, TRUE));
}
/* search entry point */
UWORD search(fcbp, dsparm, p)
REG struct fcb *fcbp; /* pointer to fcb for file to search */
REG UWORD dsparm; /* parameter to pass through to dirscan */
UBYTE *p; /* pointer to pass through to tmp_sel */
{
REG UWORD rtn; /* return value */
BSETUP
if (fcbp->drvcode == '?')
{
seldsk(GBL.dfltdsk);
rtn = dirscan(alltrue, fcbp, dsparm);
}
else
{
tmp_sel(p); /* temporarily select disk */
if (fcbp->extent != '?') fcbp->extent = 0;
fcbp->s2 = 0;
rtn = dirscan(matchit, fcbp, dsparm);
}
move( GBL.dirbufp, GBL.dmaadr, SECLEN);
return(rtn);
}
/************************
* create entry point *
************************/
BOOLEAN create(fcbp, dirp, dirindx)
REG struct fcb *fcbp; /* pointer to fcb for file to create */
REG struct dirent *dirp; /* pointer to directory entry */
REG WORD dirindx; /* index into directory */
{
REG BYTE *p;
REG WORD i;
REG BOOLEAN rtn;
BSETUP
if ( rtn = ((dirp->entry) == 0xe5) )
{
p = &(fcbp->rcdcnt);
i = 17;
do
{ /* clear fcb rcdcnt and disk map */
*p++ = 0;
i -= 1;
} while (i);
move(fcbp, dirp, sizeof *dirp); /* move the fcb to the directory */
dir_wr(dirindx >> 2); /* write the directory sector */
if ( dirindx > (GBL.dphp)->hiwater )
(GBL.dphp)->hiwater = dirindx;
crit_dsk |= 1 << (GBL.curdsk);
}
return(rtn);
}
/************************
* delete entry point *
************************/
BOOLEAN delete(fcbp, dirp, dirindx)
REG struct fcb *fcbp; /* pointer to fcb for file to delete */
REG struct dirent *dirp; /* pointer to directory entry */
REG WORD dirindx; /* index into directory */
{
REG WORD i;
REG BOOLEAN rtn;
BSETUP
if ( rtn = match(fcbp, dirp, FALSE) )
{
if ( (dirp->ftype[robit]) & 0x80 ) ro_err(fcbp,dirindx);
/* check for read-only file */
dirp->entry = 0xe5;
LOCK
dir_wr(dirindx >> 2);
/* Now free up the space in the allocation vector */
if ((GBL.parmp)->dsm < 256)
{
i = 16;
do clraloc(UBWORD(dirp->dskmap.small[--i]));
while (i);
}
else
{
i = 8;
do clraloc(swap(dirp->dskmap.big[--i]));
while (i);
}
UNLOCK
}
return(rtn);
}
/************************
* rename entry point *
************************/
BOOLEAN rename(fcbp, dirp, dirindx)
REG struct fcb *fcbp; /* pointer to fcb for file to delete */
REG struct dirent *dirp; /* pointer to directory entry */
REG WORD dirindx; /* index into directory */
{
REG UWORD i;
REG BYTE *p; /* general purpose pointers */
REG BYTE *q;
REG BOOLEAN rtn;
BSETUP
if ( rtn = match(fcbp, dirp, FALSE) )
{
if ( (dirp->ftype[robit]) & 0x80 ) ro_err(fcbp,dirindx);
/* check for read-only file */
p = &(fcbp->dskmap.small[1]);
q = &(dirp->fname[0]);
i = 11;
do
{
*q++ = *p++ & 0x7f;
i -= 1;
} while (i);
dir_wr(dirindx >> 2);
}
return(rtn);
}
/************************
* set_attr entry point *
************************/
BOOLEAN set_attr(fcbp, dirp, dirindx)
REG struct fcb *fcbp; /* pointer to fcb for file to delete */
REG struct dirent *dirp; /* pointer to directory entry */
REG WORD dirindx; /* index into directory */
{
REG BOOLEAN rtn;
BSETUP
if ( rtn = match(fcbp, dirp, FALSE) )
{
move(&fcbp->fname[0], &dirp->fname[0], 11);
dir_wr(dirindx >> 2);
}
return(rtn);
}
/****************************
* utility routine used by *
* setran and getsize *
****************************/
LONG extsize(fcbp)
/* Return size of extent pointed to by fcbp */
REG struct fcb *fcbp;
{
return( ((LONG)(fcbp->extent & 0x1f) << 7)
| ((LONG)(fcbp->s2 & 0x3f) << 12) );
}
/************************
* setran entry point *
************************/
setran(fcbp)
REG struct fcb *fcbp; /* pointer to fcb for file to set ran rec */
{
struct
{
BYTE b3;
BYTE b2;
BYTE b1;
BYTE b0;
};
LONG random;
random = (LONG)UBWORD(fcbp->cur_rec) + extsize(fcbp);
/* compute random record field */
fcbp->ran0 = random.b2;
fcbp->ran1 = random.b1;
fcbp->ran2 = random.b0;
}
/**********************************/
/* fsize is a funtion for dirscan */
/* passed from getsize */
/**********************************/
BOOLEAN fsize(fcbp, dirp, dirindx)
REG struct fcb *fcbp; /* pointer to fcb for file to delete */
REG struct dirent *dirp; /* pointer to directory entry */
WORD dirindx; /* index into directory */
{
REG BOOLEAN rtn;
struct
{
BYTE b3;
BYTE b2;
BYTE b1;
BYTE b0;
};
LONG temp;
if ( rtn = match(fcbp, dirp, FALSE) )
{
temp = (LONG)UBWORD(dirp->rcdcnt) + extsize(dirp);
/* compute file size */
fcbp->ran0 = temp.b2;
fcbp->ran1 = temp.b1;
fcbp->ran2 = temp.b0;
}
return(rtn);
}
/************************
* getsize entry point *
************************/
getsize(fcbp)
/* get file size */
REG struct fcb *fcbp; /* pointer to fcb to get file size for */
{
LONG maxrcd;
LONG temp;
REG WORD dsparm;
struct
{
BYTE b3;
BYTE b2;
BYTE b1;
BYTE b0;
};
maxrcd = 0;
dsparm = 0;
temp = 0;
while ( dirscan(fsize, fcbp, dsparm) < 255 )
{ /* loop until no more matches */
temp.b2 = fcbp->ran0;
temp.b1 = fcbp->ran1;
temp.b0 = fcbp->ran2;
if (temp > maxrcd) maxrcd = temp;
dsparm = 1;
}
fcbp->ran0 = maxrcd.b2;
fcbp->ran1 = maxrcd.b1;
fcbp->ran2 = maxrcd.b0;
}
/************************
* free_sp entry point *
************************/
free_sp(dsknum)
UBYTE dsknum; /* disk number to get free space of */
{
REG LONG records;
REG UWORD *alvec;
REG UWORD bitmask;
REG UWORD alvword;
REG WORD i;
BSETUP
seldsk(dsknum); /* select the disk */
records = (LONG)0; /* initialize the variables */
alvec = (GBL.dphp)->alv;
bitmask = 0;
for (i = 0; i <= (GBL.parmp)->dsm; i++) /* for loop to compute */
{
if ( ! bitmask)
{
bitmask = 0x8000;
alvword = ~(*alvec++);
}
if ( alvword & bitmask)
records += (LONG)( ((GBL.parmp)->blm) + 1 );
bitmask >>= 1;
}
*(LONG *)GBL.dmaadr = records; /* move # records to DMA address */
}