
/****************************************************************
*								*
*		CP/M-68K BDOS Disk Read/Write Module		*
*								*
*	This module contains functions to perform sequential   	*
*	or random access read or write to the disk for CP/M-68K	*
*								*
*	It includes the following external functions:		*
*								*
*		bdosrw()    - sequential and random disk I/O	*
*								*
*								*
*	Compiled with Alcyon C on the VAX			*
*								*
****************************************************************/

#include "bdosinc.h"		/* Standard I/O declarations		*/

#include "bdosdef.h"		/* Type and structure declarations for BDOS */


/* External function definitions */
EXTERN	UWORD	rdwrt();	/* disk read/write routine		*/
EXTERN	WORD	getaloc();	/* allocate a block of disk space	*/
EXTERN	WORD	swap();		/* assembly language byte swapper	*/
EXTERN	UWORD	dirscan();	/* directory scanning routine		*/
EXTERN	BOOLEAN openfile();	/* open file function passed to dirscan */
EXTERN	UWORD	close_fi();	/* close file function			*/
EXTERN	BOOLEAN create();	/* create file function passed to dirscan */
EXTERN  UWORD	ro_err();	/* read-only file error handler		*/

/* External variable definitions */
EXTERN	UWORD	ro_dsk;		/* read-only disk vector		*/


/**********************************************************/
/*  First, some utility functions used by seqio and ranio */
/**********************************************************/

/******************************
*  FCB block number routines  *
******************************/

WORD	blkindx(fcbp)
/*  return index into fcb disk map	*/

REG struct fcb	*fcbp;			/* pointer to fcb		*/
{
    REG struct dpb *dparmp;		/* pointer to disk parameter block */
    REG WORD i;
    REG WORD blkshf;
    BSETUP

    dparmp = GBL.parmp;
    blkshf = dparmp->bsh;
    i = ((fcbp->extent) & dparmp->exm) << (7 - blkshf);
    return (i + (UBWORD(fcbp->cur_rec) >> blkshf) );
}


UWORD	blknum(fcbp, index, wrdfcb)
/* return block number in fcb indicated by index */

REG struct fcb	*fcbp;		/* pointer to fcb			*/
REG WORD	index;		/* index into disk map of fcb 		*/
WORD		wrdfcb;		/* boolean, fcb disk map of words	*/
{
    if (wrdfcb)
	return( swap(fcbp->dskmap.big[index]) );
    else return( UBWORD(fcbp->dskmap.small[index]) );
}


setblk(fcbp, index, wrdfcb, block)
/* put block number into fcb */

REG struct fcb	*fcbp;		/* pointer to fcb			*/
REG WORD	index;		/* index into disk map of fcb 		*/
WORD		wrdfcb;		/* boolean, fcb disk map of words	*/
REG UWORD	block;		/* block number				*/
{
    fcbp->s2 &= 0x7f;		/* set file write flag			*/
    if (wrdfcb)
	fcbp->dskmap.big[index] = swap(block);
    else fcbp->dskmap.small[index] = (UBYTE)block;
}


/***************************
*  disk read/write routine *
***************************/

UWORD do_io(block, rcrd, parm)

UWORD	block;		/* block number		*/
UBYTE	rcrd;		/* record number	*/
REG WORD parm;		/* write parameter	*/
{
    REG LONG lsec;
    REG struct dpb *dparmp;
    BSETUP

    dparmp = GBL.parmp;				/* init dpb pointer */
    lsec = ((LONG)block << (dparmp->bsh)) +
		(LONG)(rcrd & (dparmp->blm));
    return( rdwrt(lsec, GBL.dmaadr, parm) );
}


/*******************************************
*  routine for crossing extent boundaries  *
*******************************************/

WORD new_ext(fcbp, reading, ran)
/*  If sequential I/O, open the next extent			*/
/*  If random I/O, compute new extent from random record field	*/

REG struct fcb	*fcbp;		/* pointer to fcb  */
BOOLEAN reading;		/* read/write flag */
WORD  ran;			/* random I/O flag */
{
    REG UBYTE mod;		/* module number   */
    REG UBYTE ext;		/* extent number   */
    REG UBYTE t_mod;		/* temp mod number */
    REG UBYTE t_ext;		/* temp extent     */
    BSETUP

    if (ran)
    {
	mod = ( (fcbp->ran0) << 4) | ( (fcbp->ran1) >> 4);
	ext = ( ((fcbp->ran1) & 0x0f) << 1);
	if ((fcbp->ran2) & 0x80) ext |= 1;
		/* the calculation of ext was coded this way because of a */
		/* compiler bug from Alcyon */
    }
    else
    {
	mod = (fcbp->s2) & 0x3f;
	ext = (fcbp->extent) + 1;	/* for sequential, incr extent	*/
    }
    if (ext >= 32)
    {
	ext = 0;
	mod += 1;
    }
    if (mod >= 64) return(6);		/* past maximum file size */
    if ( mod == ((fcbp->s2) & 0x3f) )
	if ( ! ((ext ^ (fcbp->extent)) & ~((GBL.parmp)->exm) & 0x1f) )
	{				/* we're in same logical extent */
	    fcbp->extent = ext;
	    return(0);
	}
    /* Extent or Module numbers don't match	*/
    /* Close the old extent and open a  one	*/
    if ( close_fi(fcbp) >= 255 ) return(3);
					/* can't close old extent */
    t_mod = fcbp->s2;
    t_ext = fcbp->extent;
    fcbp->s2 = mod;
    fcbp->extent = ext;
    if ( dirscan(openfile, fcbp, 0) >= 255 )  /* open  extent */
    {
	if (reading)
	{				/* reading unwritten extent */
	    fcbp->s2 = t_mod;
	    fcbp->extent = t_ext;
	    return(4);
	}
	if ( dirscan(create, fcbp, 8) >= 255 )
	    return(5);			/* can't create new extent */
    }
    return(0);
}


/************************************
* Routine to calculate the maximum  *
* extent number of an FCB in a	    *
* extent-folded environment	    *
************************************/

UWORD calcext(fcbp)

REG struct fcb *fcbp;

{
    REG UWORD i;
    REG BYTE *p;
    BSETUP

    i = 15;
    p = &(fcbp->dskmap.small[16]);
    do
    {
	if (*--p) break;
	i -= 1;
    } while (i);
/* Now i contains the index of the last non-zero block in the FCB */
    if ((GBL.parmp)->dsm > 255) i >>= 1;
    i >>= 7 - ((GBL.parmp)->bsh);
    return ( (fcbp->extent) & ~((GBL.parmp)->exm) & 0x1f | i );
}


/*********************************
* Routine to get the actual	 *
* record count of the currently  *
* active logical extent of a FCB *
*********************************/

UWORD get_rc(fcbp)

REG struct fcb *fcbp;

{
    REG UWORD ext;

    ext = calcext(fcbp);	/* find last active extent in fcb */
    if (ext == fcbp->extent) return(UBWORD(fcbp->rcdcnt));
			/* if this is the last active fcb, return fcb's rc */
    else if (ext > fcbp->extent) return(128);
			/* if the fcb has more extents past this one, then */
			/* the current one is logically full	*/
    else return (0);
			/* if we seeked past the last active extent, rc = 0 */
}


/************************
*  bdosrw entry point	*
************************/

UWORD bdosrw(fcbp, reading, random)

REG struct fcb *fcbp;		/* fcbp is a pointer to a fcb */
REG BOOLEAN reading;		/* boolean to tell whether to read or write */
WORD random;			/* 0 = sequential, 1 = random (normal),  */
				/* 2 = random with zero fill	*/
{
    REG UWORD	block;		/* block number from fcb	*/
    REG WORD	index;		/* index into disk map of fcb	*/
    REG BYTE	*old_dma;	/* temp holding spot for dmaadr */
    REG WORD	parm;		/* parameter to do-io		*/
    REG WORD	bigfile;	/* file system is in word mode	*/
    REG UWORD	rtn;		/* return parameter		*/
    REG UBYTE	rc;		/* temp storage for rcdcnt	*/
    BSETUP

    bigfile = ((GBL.parmp)->dsm) & ~0xff;
    if ( ( ! reading) && (fcbp->ftype[robit] & 0x80) )
		 ro_err(fcbp,((GBL.dpbp)->dpbp)->drm);
				/* check for read-only file */
    if (random)
    {
	if ( rtn = new_ext(fcbp, reading, TRUE) ) return(rtn);
			/* open new extent if necessary, return if error */
	fcbp->cur_rec = (fcbp->ran2) & 0x7f;
    }
    else		/* sequential */
	if (fcbp->cur_rec == 128)
	{			/* time to try next extent */
	    if ( new_ext(fcbp, reading, FALSE) )
		return(1);	/* if can't open new extent, error */
	    fcbp->cur_rec = 0;	/* opened new extent, zero cur_rec */
	}

    /* record is now in active fcb */
    rc = fcbp->rcdcnt;
    if ( UBWORD(fcbp->cur_rec) >= get_rc(fcbp) )
    {
	if (reading) return(1);		/* reading unwritten data */
	fcbp->s2 &= 0x7f;		/* set file write flag */
	rc = fcbp->cur_rec + 1;
    }
    index = blkindx(fcbp);		/* get index into fcb disk map */
    block = blknum(fcbp, index, bigfile);
    if (block) parm = (reading ? 0 : 1);
    else		/* if allocated block, parm is just read or write */
    {					/* unallocated block	*/
	if (reading) return(1);		/* reading unwritten data */

	/* Writing to new block	*/
	/* The parm passed to getaloc is the previously allocated block	*/
	/*	or 0, if the previous block is not allocated		*/

	block = getaloc(blknum(fcbp, (index ? (index - 1) : 0), bigfile));
	if (block == ~0) return(2);	/* out of space	*/
	setblk(fcbp, index, bigfile, block);
	parm = 3;
	if (random == 2)
	{				/* Write random with zero fill	*/
	    old_dma = GBL.dmaadr;
	    GBL.dmaadr = GBL.dirbufp;	/* Do DMA from dir_buf */
	    index = SECLEN;
	    do GBL.dmaadr[--index] = 0;
		while (index);		/* zero the dma buffer */
	    for (index = 0; index <= ((GBL.parmp)->blm); index++)
	    {
		do_io(block, (UBYTE)index, parm);
					/* write zeros to the block */
		parm = 1;		/* next write is not to new block */
	    }
	    GBL.dmaadr = old_dma;	/* restore dma address	*/
	}
    }
    rtn = do_io(block, fcbp->cur_rec, parm);
    if ( rtn == 0 )
    {
	fcbp->rcdcnt = rc;
	if ( ! random ) fcbp->cur_rec += 1;
    }
    return(rtn);
}
