/**************************************************************** * * * 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 */ }