mirror of
https://github.com/SEPPDROID/Digital-Research-Source-Code.git
synced 2025-10-26 18:04:07 +00:00
1363 lines
44 KiB
C
1363 lines
44 KiB
C
/* Library of commonly-used functions */
|
||
/* 09:00 02/24/83 */
|
||
|
||
#include <LIBRARY.H> /* standard defines and structures */
|
||
|
||
/* Configuration Block Access */
|
||
|
||
/*==============================================================*/
|
||
char
|
||
*get_cba(code) /* get configuration block address */
|
||
/*==============================================================*/
|
||
/* This function makes a call to a "private" entry in the BIOS
|
||
Jump vector to return the address of a specific data object in
|
||
the BIOS. The code indicates which object is required.
|
||
Strictly speaking, each program using this function could make
|
||
a direct call to the BIOS using the biosh() function provided
|
||
by BDS C. This function serves a common point to which debug
|
||
code can be added to display the addresses handed back. */
|
||
/* Entry Parameters */
|
||
int code; /* code that specifies the object whose address is required */
|
||
|
||
/* Exit Parameters
|
||
The address returned by the BIOS routine. */
|
||
{
|
||
char *retval; /* value returned by the BIOS */
|
||
|
||
retval = biosh(CBGADDR,code);
|
||
/* printf("\nget_cba : code %d address %4x",code,retval); */
|
||
return retval;
|
||
}
|
||
|
||
|
||
|
||
/* Character manipulation functions */
|
||
|
||
/*==============================================================*/
|
||
strscn(string,key) /* String Scan */
|
||
/*==============================================================*/
|
||
/* This function scans a 00-terminated character string looking
|
||
for a key string in it. If the key string is found withing the
|
||
string, the function returns a pointer to it, otherwise it
|
||
returns a value of 0. */
|
||
/* Entry Parameters */
|
||
char *string; /* string to be searched */
|
||
char *key; /* key string to be searched for */
|
||
|
||
/* Exit Parameters
|
||
Pointer to key string within searched string, or
|
||
0 if key not found.
|
||
*/
|
||
|
||
{
|
||
while (*string) /* for all non-null chars in string */
|
||
{
|
||
if ((*string == *key) && /* first char matches */
|
||
(sstrcmp(string,key) == 0) /* perform substring compare on rest */
|
||
)
|
||
return string; /* substring matches, return pointer */
|
||
|
||
string++; /* move to next char in string */
|
||
}
|
||
return 0; /* indicate no match found */
|
||
} /* end of strscn */
|
||
|
||
|
||
/*==============================================================*/
|
||
ustrcmp(string1,string2) /* UPPER CASE string compare */
|
||
/*==============================================================*/
|
||
/* This function is similar to the normal strcmp function;
|
||
it differs only in that the characters are folded to upper
|
||
case before comparison is made. */
|
||
/* Entry Parameters */
|
||
char *string1; /* pointer to first string */
|
||
char *string2; /* pointer to second string */
|
||
|
||
{
|
||
/* Exit Parameters
|
||
0 - if string 1 = string 2
|
||
-ve integer if string 1 > string 2
|
||
+ve integer if string 1 < string 2
|
||
*/
|
||
int count; /* used to access chars in both strings */
|
||
|
||
count = 0; /* start with the first character of both */
|
||
|
||
/* while string 1 characters are non-null, and
|
||
match their counterparts in string 2. */
|
||
while (string1[count] == string2[count])
|
||
{
|
||
if (string1[++count] == '\0') /* last char in string1 */
|
||
return 0; /* indicate equality */
|
||
}
|
||
return string2[count] - string1[count]; /* "compare" chars */
|
||
} /* end of sstrcmp */
|
||
|
||
|
||
|
||
/*==============================================================*/
|
||
sstrcmp(string,substring) /* substring compare */
|
||
/*==============================================================*/
|
||
/* This function compares two strings. The first, string, need not
|
||
be 00-terminated. The second, substring, must be 00-terminated.
|
||
It is very similar to the standard function strcmp, except
|
||
that in this function, the length of the substring controls
|
||
how many characters are compared. */
|
||
/* Entry Parameters */
|
||
char *string; /* pointer to main string */
|
||
char *substring; /* pointer to substring */
|
||
|
||
/* Exit Parameters
|
||
0 - substring matches corresponding characters in string
|
||
-ve integer if char in string is > char in substring
|
||
+ve integer if char in string is < char in substring
|
||
*/
|
||
{
|
||
int count; /* used to access chars in both string & substring */
|
||
|
||
count = 0; /* start with the first character of both */
|
||
|
||
/* while substring characters are non-null, and
|
||
match their counterparts in string. */
|
||
while (string[count] == substring[count])
|
||
{
|
||
if (substring[++count] == '\0') /* last char in substring */
|
||
return 0; /* indicate equality */
|
||
}
|
||
return substring[count] - string[count]; /* "compare" chars */
|
||
} /* end of sstrcmp */
|
||
|
||
|
||
|
||
/*==============================================================*/
|
||
usstrcmp(string,substring) /* UPPER CASE substring compare */
|
||
/*==============================================================*/
|
||
/* This function compares two strings. The first, string, need not
|
||
be 00-terminated. The second, substring, must be 00-terminated.
|
||
It is very similar to the substring compare above, except all
|
||
characters are folded to UPPER CASE. */
|
||
/* Entry Parameters */
|
||
char *string; /* pointer to main string */
|
||
char *substring; /* pointer to substring */
|
||
|
||
/* Exit Parameters
|
||
0 - substring matches corresponding characters in string
|
||
-ve integer if char in string is > char in substring
|
||
+ve integer if char in string is < char in substring
|
||
*/
|
||
{
|
||
int count; /* used to access chars in both string & substring */
|
||
|
||
count = 0; /* start with the first character of both */
|
||
|
||
/* while substring characters are non-null, and
|
||
match their counterparts in string. */
|
||
while (toupper(string[count]) == toupper(substring[count]))
|
||
{
|
||
if (substring[++count] == '\0') /* last char in substring */
|
||
return 0; /* indicate equality */
|
||
}
|
||
return substring[count] - string[count]; /* "compare" chars */
|
||
} /* end of usstrcmp */
|
||
|
||
|
||
/*==============================================================*/
|
||
comp_fname(scb,name) /* Compare file names */
|
||
/*==============================================================*/
|
||
/* This function compares a possibly ambiguous file name
|
||
to the name in the specified character string. The number of
|
||
bytes compared are determined by the number of characters in
|
||
the mask.
|
||
This function can be used to compare file names and types,
|
||
or by appending an extra byte to the mask, the file names,
|
||
types, and extent numbers.
|
||
In the case of file directory entries, an extra byte can be
|
||
prefixed to the mask and the function used to compare
|
||
user number, file name, type and extent.
|
||
Note that a '?' in the first character of the mask will NOT
|
||
match with a value of 0xE5 (this value is used to indicate
|
||
an inactive directory entry). */
|
||
/* Entry Parameters */
|
||
struct _scb *scb; /* pointer to search control block */
|
||
char *name; /* pointer to file name */
|
||
|
||
/* Exit Parameter
|
||
NAME_EQ if the names match the mask
|
||
NAME_LT if the name is less than the mask
|
||
NAME_GT if the name is greater than the mask
|
||
NAME_NE if the name is not equal to the mask (but the outcome
|
||
is ambiguous because of the wildcards in the mask)
|
||
*/
|
||
|
||
{
|
||
int count; /* count of the number of chars processed */
|
||
short ambiguous; /* NZ when the mask is ambiguous */
|
||
char *mask; /* pointer to bytes at front of scb */
|
||
|
||
|
||
/* set pointer to characters at front of search control block */
|
||
mask = scb;
|
||
|
||
/* ambiguous match on user number matches
|
||
only users 0 - 15, and not inactive entries */
|
||
if (mask[0] == '?')
|
||
{
|
||
if (name[0] == 0xE5)
|
||
return NAME_NE; /* indicate inequality */
|
||
}
|
||
else /* first char. of mask is not '?' */
|
||
{
|
||
if (mask[0] != name[0]) /* user numbers do not match */
|
||
return NAME_NE; /* indicate inequality */
|
||
}
|
||
|
||
/* no check the name (and, if the length is such, the extent */
|
||
for (count = 1; /* start with first name character */
|
||
count <= scb -> scb_length; /* for all required characters */
|
||
count++) /* move to next character */
|
||
{
|
||
if (mask[count] == '?') /* wildcard character in mask */
|
||
{
|
||
ambiguous = 1; /* indicate ambiguous name in mask */
|
||
continue; /* do not make any comparisons */
|
||
}
|
||
if (mask[count] != (name[count] & 0x7F))
|
||
{ /* mask char not equal to fcb char */
|
||
if (ambiguous) /* if previous wildcard, indicate NE */
|
||
return NAME_NE;
|
||
else
|
||
/* compare chars to determine relationship */
|
||
return (mask[count] > name[count] ?
|
||
NAME_LT : NAME_GT);
|
||
}
|
||
}
|
||
/* If control reaches here, then all characters of the
|
||
mask and name have been processed, and either there
|
||
were wildcards in the mask, or they all matched. */
|
||
return NAME_EQ; /* indicate mask and name are "equal" */
|
||
} /* end of comp_fname */
|
||
|
||
|
||
|
||
/*==============================================================*/
|
||
conv_fname(fcb,fn) /* Convert file name for output */
|
||
/*==============================================================*/
|
||
/* This function converts the contents of a file control
|
||
block and converts into a printable string 'D:FILENAME.TYP'. */
|
||
/* Entry Parameters */
|
||
struct _fcb *fcb; /* Pointer to file control block */
|
||
char *fn; /* Pointer to area to receive name */
|
||
|
||
{
|
||
/* if the disk specification in the
|
||
fcb is 0, use the current disk */
|
||
*fn++ = (fcb -> fcb_disk) ? (fcb -> fcb_disk + ('A'-1)) :
|
||
(bdos(GETDISK) + 'A');
|
||
|
||
*fn++ = ':'; /* insert disk id. delimiter */
|
||
|
||
movmem(&fcb -> fcb_fname,fn,8); /* move file name */
|
||
fn += 8; /* update pointer */
|
||
*fn++ = '.'; /* insert file name/type delimiter */
|
||
movmem(&fcb -> fcb_fname+8,fn,3); /* move file type */
|
||
*fn++ &= 0x7F; /* remove any attribute bits */
|
||
*fn++ &= 0x7F; /* remove any attribute bits */
|
||
*fn++ &= 0x7F; /* remove any attribute bits */
|
||
*fn = '\0'; /* terminator */
|
||
|
||
} /* end of conv_fname */
|
||
|
||
|
||
/*==============================================================*/
|
||
conv_dfname(disk,dir,fn) /* Convert directory file name for output */
|
||
/*==============================================================*/
|
||
/* This function converts the contents of a file directory entry
|
||
block and converts into a printable string 'D:FILENAME.TYP'. */
|
||
/* Entry Parameters */
|
||
short disk; /* Disk Id. (A = 0, B = 1) */
|
||
struct _dir *dir; /* Pointer to file control block */
|
||
char *fn; /* Pointer to area to receive name */
|
||
|
||
{
|
||
/* convert user number and disk id. */
|
||
sprintf(fn,"%2d/%c:",dir -> de_userno,disk + 'A');
|
||
fn += 5; /* update pointer to file name */
|
||
|
||
movmem(&dir -> de_fname,fn,8); /* move file name */
|
||
fn += 8; /* update pointer */
|
||
*fn++ = '.'; /* insert file name/type delimiter */
|
||
movmem(&dir -> de_fname+8,fn,3); /* move file type */
|
||
*fn++ &= 0x7F; /* remove any attribute bits */
|
||
*fn++ &= 0x7F; /* remove any attribute bits */
|
||
*fn++ &= 0x7F; /* remove any attribute bits */
|
||
*fn = '\0'; /* terminator */
|
||
|
||
} /* end of conv_dfname */
|
||
|
||
|
||
/*==============================================================*/
|
||
get_nfn(amb_fname,next_fname) /* get next file name */
|
||
/*==============================================================*/
|
||
/* This function sets the FCB at 'next_fname' to contain the
|
||
directory entry found that matches the ambiguous file name
|
||
in 'amb_fname'.
|
||
On the first entry for a given file name, the most significant
|
||
bit in the FCB's disk field must be set to 1 (this causes a
|
||
Search First BDOS call to be made). */
|
||
|
||
/* Entry Parameters */
|
||
struct _fcb *amb_fname; /* ambiguous file name */
|
||
struct _fcb *next_fname;/* first byte must have ms bit set for
|
||
first time entry)*/
|
||
|
||
/* Exit Parameters
|
||
0 = No further name found
|
||
1 = Further name found (and setup in next_fname)
|
||
*/
|
||
{
|
||
char bdos_func; /* set to either Search First or Next */
|
||
char *pfname; /* pointer to file name in directory entry */
|
||
|
||
/* initialize tail-end of next file FCB to 0 */
|
||
setmem(&next_fname -> fcb_extent,FCBSIZE-12,0);
|
||
|
||
bdos_func = SEARCHF; /* assume a Search First must be given */
|
||
|
||
if (!(next_fname -> fcb_disk & 0x80)) /* if not first time */
|
||
{
|
||
/* Search First on previous name */
|
||
srch_file(next_fname,SEARCHF);
|
||
bdos_func = SEARCHN; /* then do a Search Next */
|
||
}
|
||
else /* first time */
|
||
next_fname -> fcb_disk &= 0x7F; /* reset first time flag */
|
||
|
||
/* refresh next_fname from ambiguous file name
|
||
(move disk, name, type) */
|
||
movmem(amb_fname,next_fname,12);
|
||
|
||
/* if first time, issue Search First, otherwise
|
||
issue a Search Next call. 'srch_file' returns
|
||
a pointer to the directory entry that matches
|
||
the ambiguous file name, or 0 if no match */
|
||
if (!(pfname = srch_file(next_fname,bdos_func)) )
|
||
{
|
||
return 0; /* indicate no match */
|
||
}
|
||
/* move file name and type */
|
||
movmem(pfname,&next_fname -> fcb_fname,11);
|
||
return 1; /* indicate match found */
|
||
} /* end of get_nfn */
|
||
|
||
|
||
/*==============================================================*/
|
||
char *srch_file(fcb,bdos_code)
|
||
/*==============================================================*/
|
||
/* This function issues either a Search First or Search Next
|
||
BDOS call. */
|
||
/* Entry Parameters */
|
||
struct _fcb *fcb; /* pointer to file control block */
|
||
short bdos_code; /* either SEARCHF or SEARCHN */
|
||
|
||
/* Exit Parameters
|
||
0 = No match found
|
||
NZ = Pointer to entry matched (currently in buffer)
|
||
*/
|
||
|
||
{
|
||
unsigned r_code; /* Return code from Search Function.
|
||
This is either 255 for no match, or 0, 1, 2 or 3
|
||
being the ordinal of the 32-byte entry in the
|
||
buffer that matched the name. */
|
||
char *dir_entry; /* pointer to directory entry */
|
||
|
||
/* the BDS C compiler always sets the BDOS DMA
|
||
to location 0x80 */
|
||
|
||
r_code = bdos(bdos_code,fcb); /* issue the bdos call */
|
||
if (r_code == 255) /* no match found */
|
||
return 0;
|
||
|
||
/* set a pointer to the matching
|
||
entry by multiplying return code by 128
|
||
and adding on to the buffer address (0x80),
|
||
also add on 1 to point to first character of name */
|
||
|
||
return (r_code << 5) + 0x81;
|
||
} /* end of srch_file */
|
||
|
||
|
||
/*==============================================================*/
|
||
rd_disk(drb) /* read disk (via BIOS) */
|
||
/*==============================================================*/
|
||
/* This function uses the parameters previously setup in the
|
||
incoming request block, and using the BIOS directly,
|
||
executes the disk read. */
|
||
|
||
/* Entry Parameters */
|
||
struct _drb *drb; /* disk request block (disk, track, sector, buffer */
|
||
|
||
/* Exit Parameters
|
||
0 = No data available
|
||
1 = Data available
|
||
*/
|
||
{
|
||
if (!set_disk(drb)) /* call SELDSK, SETTRK, SETSEC */
|
||
return 0; /* if SELDSK fails, indicate no data available */
|
||
if (bios(DREAD)) /* execute BIOS read */
|
||
return 0; /* indicate no data available if error returned */
|
||
|
||
return 1; /* indicate data available */
|
||
} /* end of rd_disk */
|
||
|
||
|
||
/*==============================================================*/
|
||
wrt_disk(drb) /* write disk (via BIOS) */
|
||
/*==============================================================*/
|
||
/* This function uses the parameters previously setup in the
|
||
incoming request block, and using the BIOS directly,
|
||
executes the disk write. */
|
||
|
||
/* Entry Parameters */
|
||
struct _drb *drb; /* disk request block (disk, track, sector, buffer) */
|
||
|
||
/* Exit Parameters
|
||
0 = Error during write
|
||
1 = Data written OK
|
||
*/
|
||
{
|
||
if (!set_disk(drb)) /* call SELDSK, SETTRK, SETSEC, SETDMA */
|
||
return 0; /* if SELDSK fails, indicate no data written */
|
||
if (bios(DWRITE)) /* execute BIOS write */
|
||
return 0; /* indicate error returned */
|
||
|
||
return 1; /* indicate data written */
|
||
} /* end of wrt_disk */
|
||
|
||
|
||
|
||
/*==============================================================*/
|
||
short set_disk(drb) /* set disk parameters */
|
||
/*==============================================================*/
|
||
/* This function sets up the BIOS variables in anticipation of
|
||
a subsequent disk read or write. */
|
||
/* Entry Parameters */
|
||
struct _drb *drb; /* disk request block (disk, track, sector, buffer) */
|
||
|
||
/* Exit Parameters
|
||
0 = Invalid disk (do NOT perform Read/Write)
|
||
1 = BIOS now setup for Read/Write
|
||
*/
|
||
{
|
||
/* the sector in the disk request block contains a
|
||
LOGICAL sector. If necessary (as determined by the
|
||
value in the disk parameter header), this must be
|
||
converted into the PHYSICAL sector.
|
||
NOTE: skewtab is declared as a pointer to a pointer to
|
||
a short integer (single byte). */
|
||
short **skewtab; /* skewtab -> disk parameter header -> skewtable */
|
||
short phy_sec; /* physical sector */
|
||
|
||
/* Call the SELDSK BIOS entry point. If this returns
|
||
a 0, then the disk is invalid. Otherwise, it returns
|
||
a pointer to the pointer to the skewtable */
|
||
if ( !(skewtab = biosh(SELDSK,drb -> dr_disk)) )
|
||
return 0; /* invalid disk */
|
||
|
||
bios(SETTRK,drb -> dr_track); /* set track */
|
||
|
||
/* Note that the biosh function puts the sector into
|
||
registers BC, and a pointer to the skewtable in
|
||
registers HL. It returns the value in HL on exit
|
||
from the BIOS */
|
||
phy_sec = biosh(SECTRN,drb -> dr_sector,*skewtab); /* get physical sector */
|
||
bios(SETSEC,phy_sec); /* set sector */
|
||
bios(SETDMA,drb -> dr_buffer); /* set buffer address */
|
||
|
||
return 1; /* indicate no problems */
|
||
} /* end of setp_disk */
|
||
|
||
|
||
/* Directory Management Functions */
|
||
|
||
|
||
/*==============================================================*/
|
||
get_nde(dir_pb) /* get next directory entry */
|
||
/*==============================================================*/
|
||
/* This function returns a pointer to the next directory entry.
|
||
If the directory has not been opened, it opens it.
|
||
When necessary, the next directory sector is read in.
|
||
If the current sector has been modified and needs to be written back
|
||
on to the disk, this will be done before reading in the next sector. */
|
||
/* Entry Parameters */
|
||
struct _dirpb *dir_pb; /* pointer to the disk parameter block */
|
||
|
||
/* Exit Parameters
|
||
Returns a pointer to the next directory entry in the buffer.
|
||
The "directory open" and "write sector" flags in the parameter
|
||
block are reset as necessary. */
|
||
|
||
{
|
||
|
||
if(!dir_pb -> dp_open) /* directory not yet opened */
|
||
{
|
||
if (!open_dir(dir_pb)) /* initialize and open directory */
|
||
{
|
||
err_dir(O_DIR,dir_pb); /* report error on open */
|
||
exit();
|
||
}
|
||
/* deliberately set the directory entry pointer to the end
|
||
of the buffer to force a read of a directory sector */
|
||
dir_pb -> dp_entry = dir_pb -> dp_buffer + DIR_BSZ;
|
||
dir_pb -> dp_write = 0; /* reset write sector flag */
|
||
}
|
||
|
||
/* update the directory entry pointer to the next entry in
|
||
the buffer and check if the pointer is now "off the end"
|
||
of the buffer and another sector needs to be read. */
|
||
if (++dir_pb -> dp_entry < dir_pb -> dp_buffer + DIR_BSZ)
|
||
{
|
||
return dir_pb -> dp_entry; /* return pointer to next entry */
|
||
}
|
||
|
||
/* need to move to next sector and read it in */
|
||
|
||
/* do not check if at end of directory or move to
|
||
the next sector if the directory has just been
|
||
opened (but the opened flag has not yet been set */
|
||
if (!dir_pb -> dp_open)
|
||
dir_pb -> dp_open = 1; /* indicate that the directory is now open */
|
||
else
|
||
{
|
||
/* check if the sector currently in the buffer needs to be
|
||
written back out to the disk (it having been changed). */
|
||
if (dir_pb -> dp_write)
|
||
{
|
||
dir_pb -> dp_write = 0; /* reset the flag */
|
||
if(!rw_dir(W_DIR,dir_pb)) /* write the directory sector */
|
||
{
|
||
err_dir(W_DIR,dir_pb); /* report error on writing */
|
||
exit();
|
||
}
|
||
}
|
||
|
||
/* count down on number of directory entries left to process.
|
||
always 4 32-byte entries per 128-byte sector */
|
||
dir_pb -> dp_entrem -= 4;
|
||
|
||
/* set directory end flag true if number of entries now < 0 */
|
||
if (dir_pb -> dp_entrem == 0) /* now at end of directory */
|
||
{
|
||
dir_pb -> dp_end = 1; /* indicate end */
|
||
dir_pb -> dp_open = 0; /* indicate directory now closed */
|
||
return 0; /* indicate no more entries */
|
||
}
|
||
|
||
/* update sector (and if need be track and sector) */
|
||
if (++dir_pb -> dp_sector == dir_pb -> dp_sptrk)
|
||
{
|
||
++dir_pb -> dp_track; /* update track */
|
||
dir_pb -> dp_sector = 0; /* reset sector */
|
||
}
|
||
}
|
||
|
||
if(!rw_dir(R_DIR,dir_pb)) /* read next directory sector */
|
||
{
|
||
err_dir(R_DIR,dir_pb); /* report error on reading */
|
||
exit();
|
||
}
|
||
|
||
/* reset directory entry pointer to the first entry in the buffer */
|
||
return dir_pb -> dp_entry = dir_pb -> dp_buffer;
|
||
|
||
} /* end of get_nde */
|
||
|
||
|
||
|
||
/*==============================================================*/
|
||
open_dir(dir_pb) /* open directory */
|
||
/*==============================================================*/
|
||
/* This function "opens" up the file directory
|
||
on a specified disk for subsequent processing
|
||
by rw_dir, next_dir functions. */
|
||
/* Entry Parameters */
|
||
struct _dirpb *dir_pb; /* pointer to directory parameter block */
|
||
|
||
/* Exit Parameters
|
||
0 = Error, directory not opened
|
||
1 = Directory open for processing
|
||
*/
|
||
{
|
||
struct _dpb *dpb; /* CP/M Disk Parameter Block */
|
||
|
||
/* get disk parameter block address for the disk specified in
|
||
the directory parameter block */
|
||
if ((dpb = get_dpb(dir_pb -> dp_disk)) == 0)
|
||
return 0; /* return indicating no dpb for this disk */
|
||
|
||
/* set the remaining fields in the parameter block */
|
||
dir_pb -> dp_sptrk = dpb -> dpb_sptrk; /* sectors per track */
|
||
dir_pb -> dp_track = dpb -> dpb_trkoff; /* track offset of the directory */
|
||
dir_pb -> dp_sector = 0; /* beginning of directory */
|
||
dir_pb -> dp_nument = dpb -> dpb_maxden+1; /* # of directory entries */
|
||
dir_pb -> dp_entrem = dir_pb -> dp_nument; /* entries remaining to process */
|
||
dir_pb -> dp_end = 0; /* indicate not at end */
|
||
|
||
/* set number of allocation blocks per directory entry to
|
||
either 8 or 16 depending on the number of allocation blocks */
|
||
dir_pb -> dp_nabpde = (dpb -> dpb_maxabn > 255 ? 8 : 16);
|
||
/* set number of allocation blocks (1 more than number of
|
||
highest block */
|
||
dir_pb -> dp_nab = dpb -> dpb_maxabn;
|
||
|
||
/* set the allocation block size based on the block shift.
|
||
The possible values are: 3 = 1k, 4 = 2K, 5 = 4K, 6 = 8K, 7 = 16K.
|
||
So a value of 16 is shifted right by (7 - bshift) bits. */
|
||
dir_pb -> dp_absize = 16 >> (7 - dpb -> dpb_bshift);
|
||
|
||
return 1; /* indicate that directory now opened */
|
||
} /* end of open_dir */
|
||
|
||
|
||
|
||
|
||
/*==============================================================*/
|
||
rw_dir(read_op,dir_pb) /* read/write directory */
|
||
/*==============================================================*/
|
||
/* This function reads/writes the next 128-byte
|
||
sector from/to the currently open directory. */
|
||
/* Entry Parameters */
|
||
short read_op; /* true to read, false (0) to write */
|
||
struct _dirpb *dir_pb; /* directory parameter block */
|
||
|
||
/* Exit Parameters
|
||
0 = Error - operation not performed
|
||
1 = Operation completed
|
||
*/
|
||
{
|
||
struct _drb drb; /* disk request (for BIOS read/write) */
|
||
|
||
|
||
drb.dr_disk = dir_pb -> dp_disk; /* set up disk request */
|
||
drb.dr_track = dir_pb -> dp_track;
|
||
drb.dr_sector = dir_pb -> dp_sector;
|
||
drb.dr_buffer = dir_pb -> dp_buffer;
|
||
|
||
if (read_op)
|
||
{
|
||
if (!rd_disk(&drb)) /* issue read command */
|
||
return 0; /* indicate error - no data available */
|
||
}
|
||
else
|
||
{
|
||
if (!wrt_disk(&drb)) /* issue write command */
|
||
return 0; /* indicate error - no data written */
|
||
}
|
||
return 1; /* indicate operation complete */
|
||
} /* end of rd_dir */
|
||
|
||
|
||
/*==============================================================*/
|
||
err_dir(opcode,dir_pb)
|
||
/*==============================================================*/
|
||
/* This function displays an error message to report an error
|
||
detected in the directory management functions open_dir and rw_dir. */
|
||
short opcode; /* operation being attempted */
|
||
struct _dirpb *dir_pb; /* pointer to directory parameter block */
|
||
{
|
||
|
||
printf("\n\007Error during ");
|
||
|
||
switch(opcode)
|
||
{
|
||
case R_DIR:
|
||
printf("Reading");
|
||
break;
|
||
case W_DIR:
|
||
printf("Writing");
|
||
break;
|
||
case O_DIR:
|
||
printf("Opening");
|
||
break;
|
||
default:
|
||
printf("Unknown Operation (%d) on",opcode);
|
||
}
|
||
|
||
printf(" Directory on disk %c:. ",dir_pb -> dp_disk + 'A');
|
||
} /* end of err_dir */
|
||
|
||
/*==============================================================*/
|
||
setscb(scb,fname,user,extent,length) /* set search control block */
|
||
/*==============================================================*/
|
||
/* This function sets up a search control block according
|
||
to the filename specified. The file name can take the
|
||
following forms :
|
||
|
||
filename
|
||
filename.typ
|
||
d:filename.typ
|
||
*:filename.typ (meaning "all disks")
|
||
ABCD...NOP:filename.typ (meaning "just the specified disks")
|
||
|
||
It sets the bit map according to which disks should be searched.
|
||
For each selected disk, it checks to see if an error is generated
|
||
when selecting the disk (i.e. if there are disk tables in the BIOS
|
||
for the disk). */
|
||
/* Entry Parameters */
|
||
struct _scb *scb; /* pointer to search control block */
|
||
char *fname; /* pointer to the file name */
|
||
short user; /* user number to search for */
|
||
short extent; /* extent number to search for */
|
||
int length; /* number of bytes to compare */
|
||
|
||
/* Exit Parameters
|
||
None.
|
||
*/
|
||
{
|
||
int disk; /* disk number currently being checked */
|
||
unsigned adisks; /* bit map for active disks */
|
||
|
||
adisks = 0; /* assume no disks to search */
|
||
|
||
if (strscn(fname,":")) /* check if ":" in file name */
|
||
{
|
||
if (*fname == '*') /* check if "all disks" */
|
||
{
|
||
adisks = 0xFFFF; /* set all bits */
|
||
}
|
||
else /* set specific disks */
|
||
{
|
||
while(*fname != ':') /* until ":" reached */
|
||
{
|
||
/* build the bit map by getting the next disk
|
||
id. (A - P), converting it to a number
|
||
in the range 0 - 15, and shifting a 1-bit
|
||
left that many places and OR-ing it into
|
||
the current active disks. */
|
||
adisks |= 1 << (toupper(*fname) - 'A');
|
||
++fname; /* move to next character */
|
||
}
|
||
++fname; /* bypass colon */
|
||
}
|
||
}
|
||
else /* use only current default disk */
|
||
{
|
||
/* set just the bit corresponding to the current disk */
|
||
adisks = 1 << bdos(GETDISK);
|
||
}
|
||
|
||
setfcb(scb,fname); /* set search control block as though it
|
||
is a file control block. */
|
||
|
||
/* Make calls to the BIOS SELDSK routine to make sure that
|
||
all of the active disk drives indeed do have disk tables
|
||
for them in the BIOS. If they don't, turn off the corresponding
|
||
bits in the bit map. */
|
||
|
||
for (disk = 0; /* start with disk A: */
|
||
disk < 16; /* until disk P: */
|
||
disk++) /* use next disk */
|
||
{
|
||
if ( !((1 << disk) & adisks) ) /* avoid unnecessary selects */
|
||
continue;
|
||
if (biosh(SELDSK,disk) == 0) /* make BIOS SELDSK call */
|
||
{ /* returns 0 if invalid disk */
|
||
/* turn OFF corresponding bit in mask
|
||
by ANDing it with bit mask having
|
||
all the other bits set = 1. */
|
||
adisks &= ((1 << disk) ^ 0xFFFF);
|
||
}
|
||
}
|
||
|
||
scb -> scb_adisks = adisks; /* set bit map in scb */
|
||
scb -> scb_userno = user; /* set user number */
|
||
scb -> scb_extent = extent; /* set extent number */
|
||
scb -> scb_length = length; /* set number of bytes to compare */
|
||
} /* end setscb */
|
||
|
||
|
||
/*==============================================================*/
|
||
dm_clr(disk_map) /* disk map clear (to zeroes) */
|
||
/*==============================================================*/
|
||
/* This function clears all elements of the disk map to 0. */
|
||
/* Entry Parameters */
|
||
unsigned disk_map[16][18]; /* pointer to array of unsigned integers */
|
||
|
||
/* Exit Parameters
|
||
None.
|
||
*/
|
||
{
|
||
/* WARNING - The 576 in the setmem call below is based on the
|
||
fact that the disk map array is [16][18] - i.e. 288 unsigned
|
||
integers, hence 576 bytes. */
|
||
setmem(disk_map,576,'\0'); /* fill array with 0's */
|
||
|
||
} /* end of dm_clr */
|
||
|
||
|
||
|
||
/*==============================================================*/
|
||
dm_disp(disk_map,adisks) /* disk map display */
|
||
/*==============================================================*/
|
||
/* This function displays the elements of the disk map, showing
|
||
the count in each element. A zero value element is shown as
|
||
blanks. For example:
|
||
|
||
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Used Free
|
||
A: 123 20 98 202 199 101 211 954 70
|
||
|
||
Lines will only be printed for active disks (as indicated by
|
||
the bit map). */
|
||
/* Entry Parameters */
|
||
unsigned disk_map[16][18]; /* pointer to disk map array */
|
||
unsigned adisks; /* bit map of active disks */
|
||
{
|
||
#define USED_COUNT 16 /* "user" number for used entities */
|
||
#define FREE_COUNT 17 /* "user" number for free entities */
|
||
|
||
int disk; /* current disk number */
|
||
int userno; /* current user number */
|
||
unsigned dsum; /* sum of entries for given disk */
|
||
|
||
printf("\n 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Used Free");
|
||
|
||
for (disk = 0; /* start with disk A: */
|
||
disk < 16; /* until disk P: */
|
||
disk++) /* next disk */
|
||
{
|
||
if (!(adisks & (1 << disk))) /* check if disk is active */
|
||
continue; /* no - so bypass this one */
|
||
|
||
printf("\n%c: ",disk + 'A'); /* display disk number */
|
||
|
||
dsum = 0; /* reset sum for this disk */
|
||
for (userno = 0; /* start with user 0 */
|
||
userno < 16; /* until user 15 */
|
||
userno++) /* next user number */
|
||
{
|
||
dsum += disk_map[disk][userno]; /* build sum */
|
||
}
|
||
|
||
if (dsum) /* check if any output for this disk,
|
||
and if not, display d: None */
|
||
{
|
||
/* print either number or blanks */
|
||
for (userno = 0; /* start with user 0 */
|
||
userno < 16; /* until user 15 */
|
||
userno++) /* next user number */
|
||
{
|
||
if (disk_map[disk][userno])
|
||
printf("%4d",disk_map[disk][userno]);
|
||
else
|
||
printf(" ");
|
||
}
|
||
}
|
||
else /* no output for this disk */
|
||
{
|
||
printf( " -- None -- ");
|
||
}
|
||
printf(" %4d %4d",disk_map[disk][USED_COUNT],disk_map[disk][FREE_COUNT]);
|
||
}
|
||
} /* end dm_disp */
|
||
|
||
|
||
/*==============================================================*/
|
||
get_dpb(disk) /* get disk parameter block address */
|
||
/*==============================================================*/
|
||
/* This function returns the address of the disk parameter
|
||
block (located in the BIOS). */
|
||
|
||
/* Entry Parameters */
|
||
char disk; /* logical disk for which DPB address is needed */
|
||
|
||
/* Exit Parameters
|
||
0 = Invalid logical disk
|
||
NZ = Pointer to disk parameter block
|
||
*/
|
||
{
|
||
|
||
if (biosh(SELDSK,disk) == 0) /* make BIOS SELDSK call */
|
||
return 0; /* invalid disk */
|
||
|
||
bdos(SETDISK,disk); /* use BDOS SETDISK function */
|
||
return bdos(GETDPARM); /* get the disk parameter block */
|
||
} /* end of get_dpb */
|
||
|
||
|
||
/* code table functions */
|
||
|
||
/* Most programs that interact with a user need to be able
|
||
to accept parameters from the user by name and translate
|
||
the name into some internal code value.
|
||
They also need to be able to work in reverse, examining
|
||
the setting of a variable, and determing what (in terms
|
||
of its ASCII name) it has been set to.
|
||
|
||
An example, is setting Baud Rates. The user may want to
|
||
enter "19200", and have this translated into some magic
|
||
number to be output to a chip. Alternatively, a previously
|
||
set baud rate variable may need to be examined, and the
|
||
string "19200" generated in order display its current
|
||
setting to the user.
|
||
|
||
A code table is used to make this task easier.
|
||
Each element in the table logically consists of:
|
||
|
||
A code value (unsigned integer)
|
||
An ASCII character string (actually a pointer to it) */
|
||
|
||
/*==============================================================*/
|
||
ct_init(entry,code,string) /* initialize code table */
|
||
/*==============================================================*/
|
||
/* This function initializes a specific entry in a code table
|
||
with a code value and string pointer.
|
||
|
||
IMPORTANT NOTE: By convention, the LAST entry in a given
|
||
code table will have a code value of CT_SNF (String Not Found). */
|
||
/* Entry Parameters */
|
||
struct _ct *entry; /* pointer to code table entry */
|
||
int code; /* code value to store in entry */
|
||
char *string; /* pointer to string for entry */
|
||
|
||
/* Exit Parameters
|
||
None.
|
||
*/
|
||
{
|
||
entry -> _ct_code = code; /* set _ct_code */
|
||
entry -> _ct_sp = string; /* set string pointer */
|
||
}
|
||
|
||
/*==============================================================*/
|
||
unsigned
|
||
ct_parc(table,string) /* parameter - return code */
|
||
/*==============================================================*/
|
||
/* This function searches the specified table for a
|
||
matching string, and returns the code value that corresponds to it.
|
||
If only one match is found in the table, then this function returns
|
||
that code value. If no match, or more than one match is found,
|
||
then it returns the error value, CT_SNF (string not found).
|
||
This function is specifically designed for processing
|
||
parameters on a command tail.
|
||
Note that the comparison is done after folding to UPPER CASE
|
||
(i.e. "STRING" matches "string"), and a SUBstring compare is used
|
||
so that only the minimum number of characters for an unambiguous
|
||
response need be entered. For example, if the table contained:
|
||
|
||
Code Value
|
||
1 "APPLES"
|
||
2 "ORANGES"
|
||
3 "APRICOTS"
|
||
|
||
A response of "O" would return code = 2, but "A" or "AP" would
|
||
be ambiguous. "APR" or "APP" would be required. */
|
||
struct _ct *table; /* pointer to table */
|
||
char *string; /* pointer to key string */
|
||
{
|
||
int mcode; /* matched code to return */
|
||
int mcount; /* count of number of matches found */
|
||
|
||
|
||
mcode = CT_SNF; /* assume error */
|
||
mcount = 0; /* reset match count */
|
||
|
||
while(table -> _ct_code != CT_SNF) /* not at end of table */
|
||
{
|
||
/* Compare keyboard response to table entry using
|
||
UPPER CASE substring compare. */
|
||
if (usstrcmp(table -> _ct_sp,string) == 0)
|
||
{
|
||
mcount++; /* update match count */
|
||
mcode = table -> _ct_code; /* save code */
|
||
}
|
||
table++; /* move to next entry */
|
||
}
|
||
|
||
if (mcount == 1) /* only one match found */
|
||
return mcode; /* return matched code */
|
||
else /* illegal or ambiguous */
|
||
return CT_SNF;
|
||
|
||
} /* end ct_parc */
|
||
|
||
/*==============================================================*/
|
||
unsigned
|
||
ct_code(table,string) /* return code for string */
|
||
/*==============================================================*/
|
||
/* This function searches the specified table, searching for
|
||
the specified string. If a match occurs, it returns the
|
||
corresponding code value, otherwise it returns CT_SNF
|
||
(String Not Found).
|
||
Unlike ct_parc, this function compares every character in the
|
||
key string, and will return the code on the first match found. */
|
||
/* Entry Parameters */
|
||
struct _ct *table; /* pointer to table */
|
||
char *string; /* pointer to string */
|
||
|
||
/* Exit Parameters
|
||
Code value - if string found
|
||
CT_SNF - if string not found
|
||
*/
|
||
|
||
{
|
||
while(table -> _ct_code != CT_SNF) /* for all entries in table */
|
||
{
|
||
if (ustrcmp(table -> _ct_sp,string) == 0) /* compare strings */
|
||
return table -> _ct_code; /* return code */
|
||
table++; /* move to next entry */
|
||
}
|
||
return CT_SNF; /* String Not Found */
|
||
} /* end ct_code */
|
||
|
||
|
||
/*==============================================================*/
|
||
ct_disps(table) /* displays all strings in specified table */
|
||
/*==============================================================*/
|
||
/* This function displays all of the strings in a given table.
|
||
It is used to indicate valid responses for operator input. */
|
||
|
||
/* Entry Parameters */
|
||
struct _ct *table; /* pointer to table */
|
||
|
||
/* Exit Parameters
|
||
None.
|
||
*/
|
||
|
||
{
|
||
|
||
while(table -> _ct_code != CT_SNF) /* not end of table */
|
||
{
|
||
printf("\n\t\t%s",table -> _ct_sp); /* print string */
|
||
table++; /* move to next entry */
|
||
}
|
||
putchar('\n'); /* add final return */
|
||
}
|
||
|
||
|
||
/*==============================================================*/
|
||
ct_index(table,string) /* returns index for a given string */
|
||
/*==============================================================*/
|
||
/* This function searches the specified table, and returns
|
||
the INDEX of the entry containing a matching string.
|
||
All characters of the string are used for the comparison
|
||
after they have been folded to UPPER CASE. */
|
||
|
||
/* Entry Parameters */
|
||
struct _ct *table; /* pointer to table */
|
||
char *string; /* pointer to string */
|
||
|
||
/* Exit Parameters
|
||
Index of entry matching string, or
|
||
CT_SNF if String Not Found.
|
||
*/
|
||
|
||
{
|
||
int index; /* current value of index */
|
||
|
||
index = 0; /* initialise index */
|
||
|
||
while(table -> _ct_code != CT_SNF) /* not at end of table */
|
||
{
|
||
|
||
if (ustrcmp(table -> _ct_sp,string) == 0)
|
||
return index; /* return index */
|
||
table++; /* move to next table entry */
|
||
index++; /* update index */
|
||
}
|
||
return CT_SNF; /* String Not Found */
|
||
}
|
||
|
||
|
||
/*==============================================================*/
|
||
char *ct_stri(table,index) /* Get string according to index */
|
||
/*==============================================================*/
|
||
/* This function returns a pointer to the string in the
|
||
table entry specified by the index. */
|
||
/* Entry Parameters */
|
||
struct _ct *table; /* pointer to table */
|
||
int index; /* index into table */
|
||
{
|
||
struct _ct *entry; /* entry pointer */
|
||
entry = table[index]; /* point to entry */
|
||
return entry -> _ct_sp; /* return pointer to string */
|
||
} /* end of ct_stri */
|
||
|
||
|
||
/*==============================================================*/
|
||
char *ct_strc(table,code) /* Get string according to code value */
|
||
/*==============================================================*/
|
||
/* This function searches the specified table and returns a
|
||
pointer to the character string in the entry with the
|
||
matching code value or a pointer to a string of "Unknown"
|
||
if the code value is not found. */
|
||
|
||
/* Entry Parameters */
|
||
struct _ct *table; /* pointer to table */
|
||
unsigned code; /* code value */
|
||
{
|
||
while(table -> _ct_code != CT_SNF) /* until end of table */
|
||
{
|
||
if (table -> _ct_code == code) /* check code matches */
|
||
return table -> _ct_sp; /* yes, return ptr to str */
|
||
table++; /* no, move to next entry */
|
||
}
|
||
return "Unknown";
|
||
}
|
||
|
||
|
||
|
||
/* Bit Vector Functions */
|
||
|
||
/* These functions manipulate bit vectors. A bit vector is a group
|
||
of adjacent bits, packed 8 per byte. Each bit vector has the
|
||
structure defined in the LIBRARY.H file.
|
||
|
||
Bit vectors are used primarily to manipulate such things as
|
||
the Operating System's allocation vectors, and any other
|
||
values that can best be represented as a series of bits. */
|
||
|
||
/*==============================================================*/
|
||
bv_make(bv,bytes) /* make a bit vector and clear to zeros */
|
||
/*==============================================================*/
|
||
/* This function uses C's built-in memory allocation, alloc,
|
||
to allocate the necessary amount of memory, and then
|
||
sets the vector to 0-bits. */
|
||
|
||
/* Entry Parameters */
|
||
struct _bv *bv; /* pointer to a bit vector */
|
||
unsigned bytes; /* number of BYTES in bit vector */
|
||
|
||
/* Exit Parameter
|
||
NZ = Vector created
|
||
0 = Insufficient memory to create vector
|
||
*/
|
||
{
|
||
if(!(bv -> bv_bits = alloc(bytes))) /* request memory */
|
||
return 0; /* request failed */
|
||
|
||
bv -> bv_bytes = bytes; /* set length */
|
||
bv -> bv_end = bv -> bv_bits + bytes; /* set pointer to end */
|
||
|
||
bv_fill(bv,0); /* fill with 0's */
|
||
return 1;
|
||
}
|
||
|
||
/*==============================================================*/
|
||
bv_fill(bv,value) /* fill bit vector with value */
|
||
/*==============================================================*/
|
||
/* This function fills the specified bit vector with the
|
||
specified value.
|
||
This function exist only for consistency's sake and
|
||
to isolate the main body of code from standard
|
||
functions like setmem. */
|
||
|
||
/* Entry Parameters */
|
||
struct _bv *bv; /* pointer to bit vector */
|
||
char value; /* value to fill vector with */
|
||
|
||
/* Exit Parameters
|
||
None.
|
||
*/
|
||
{
|
||
/* address length value */
|
||
setmem(bv -> bv_bits,bv -> bv_bytes,value);
|
||
}
|
||
|
||
|
||
/*==============================================================*/
|
||
bv_set(bv,bitnum) /* set the specified bit number */
|
||
/*==============================================================*/
|
||
/* This function sets the specified bit number in the bit vector
|
||
to a 1-bit. */
|
||
|
||
/* Entry Parameters */
|
||
struct _bv *bv; /* pointer to bit vector */
|
||
unsigned bitnum; /* bit number to be set */
|
||
|
||
/* Exit Parameters
|
||
None.
|
||
*/
|
||
{
|
||
unsigned byte_offset; /* byte offset into the bit vector */
|
||
|
||
if ((byte_offset = bitnum >> 3) > bv -> bv_bytes)
|
||
return 0; /* bitnum is 'off the end' of the vector */
|
||
|
||
/* set the appropriate bit in the vector. The byte offset
|
||
has already been calculated. The bit number in the byte
|
||
is calculated by AND-ing the bit number with 0x07.
|
||
The specified bit is then OR-red into the vector */
|
||
|
||
bv -> bv_bits[byte_offset] |= (1 << (bitnum & 0x7));
|
||
|
||
return 1; /* indicate completion */
|
||
}
|
||
|
||
|
||
|
||
/*==============================================================*/
|
||
bv_test(bv,bitnum) /* test the specified bit number */
|
||
/*==============================================================*/
|
||
/* This function returns a value that reflects the current
|
||
setting of the specified bit. */
|
||
|
||
/* Entry Parameters */
|
||
struct _bv *bv; /* pointer to bit vector */
|
||
unsigned bitnum; /* bit number to be set */
|
||
|
||
/* Exit Parameters
|
||
None.
|
||
*/
|
||
{
|
||
unsigned byte_offset; /* byte offset into the bit vector */
|
||
|
||
if ((byte_offset = bitnum >> 3) > bv -> bv_bytes)
|
||
return 0; /* bitnum is 'off the end' of the vector */
|
||
|
||
/* set the appropriate bit in the vector. The byte offset
|
||
has already been calculated. The bit number in the byte
|
||
is calculated by AND-ing the bit number with 0x07.
|
||
The specified bit is then OR-red into the vector */
|
||
|
||
return bv -> bv_bits[byte_offset] & (1 << (bitnum & 0x7));
|
||
}
|
||
|
||
|
||
/*==============================================================*/
|
||
bv_nz(bv) /* test bit vector non-zero */
|
||
/*==============================================================*/
|
||
/* This function tests each byte in the specified vector,
|
||
and returns indicating whether there are any bits set
|
||
in the vector. */
|
||
|
||
/* Entry Parameters */
|
||
struct _bv *bv; /* pointer to bit vector */
|
||
|
||
/* Exit Parameters
|
||
NZ = One or more bits are set in the vector
|
||
0 = all bits are off.
|
||
*/
|
||
|
||
{
|
||
char *bits; /* pointer to bits in bit vector */
|
||
|
||
bits = bv -> bv_bits; /* set working pointer */
|
||
|
||
while (bits != bv -> bv_end) /* for entire bit vector */
|
||
{
|
||
if (*bits++) /* if non-zero */
|
||
return bits--; /* return pointer to NZ byte */
|
||
}
|
||
return 0; /* indicate vector is 0 */
|
||
}
|
||
|
||
|
||
/*==============================================================*/
|
||
bv_and(bv3,bv1,bv2) /* bv3 = bv1 & bv2 */
|
||
/*==============================================================*/
|
||
/* This function performs a boolean AND between the bytes
|
||
of bit vector 1 and 2, storing the result in bit vector 3. */
|
||
|
||
/* Entry Parameters */
|
||
struct _bv *bv1; /* pointer to input bit vector */
|
||
struct _bv *bv2; /* pointer to input bit vector */
|
||
|
||
/* Exit Parameters */
|
||
struct _bv *bv3; /* pointer to output bit vector */
|
||
|
||
{
|
||
char *bits1, *bits2, *bits3; /* working pointers to bit vectors */
|
||
|
||
bits1 = bv1 -> bv_bits; /* initialize working pointers */
|
||
bits2 = bv2 -> bv_bits;
|
||
bits3 = bv3 -> bv_bits;
|
||
|
||
/* The AND-ing will proceed until the end of any one of the bit
|
||
vectors is reached. */
|
||
while (bits1 != bv1 -> bv_end &&
|
||
bits2 != bv2 -> bv_end &&
|
||
bits3 != bv3 -> bv_end)
|
||
{
|
||
*bits3++ = *bits1++ & *bits2++; /* bv3 = bv1 & bv2 */
|
||
}
|
||
}
|
||
|
||
|
||
/*==============================================================*/
|
||
bv_or(bv3,bv1,bv2) /* bv3 = bv1 or bv2 */
|
||
/*==============================================================*/
|
||
/* This function performs a boolean inclusive-OR between the bytes
|
||
of bit vector 1 and 2, storing the result in bit vector 3. */
|
||
|
||
/* Entry Parameters */
|
||
struct _bv *bv1; /* pointer to input bit vector */
|
||
struct _bv *bv2; /* pointer to input bit vector */
|
||
|
||
/* Exit Parameters */
|
||
struct _bv *bv3; /* pointer to output bit vector */
|
||
|
||
{
|
||
char *bits1, *bits2, *bits3; /* working pointers to bit vectors */
|
||
|
||
bits1 = bv1 -> bv_bits; /* initialize working pointers */
|
||
bits2 = bv2 -> bv_bits;
|
||
bits3 = bv3 -> bv_bits;
|
||
|
||
/* The OR-ing will proceed until the end of any one of the bit
|
||
vectors is reached. */
|
||
while (bits1 != bv1 -> bv_end &&
|
||
bits2 != bv2 -> bv_end &&
|
||
bits3 != bv3 -> bv_end)
|
||
{
|
||
*bits3++ = *bits1++ | *bits2++; /* bv3 = bv1 or bv2 */
|
||
}
|
||
}
|
||
|
||
|
||
/*==============================================================*/
|
||
bv_disp(title,bv) /* bit vector display */
|
||
/*==============================================================*/
|
||
/* This function displays the contents of the specified bit vector
|
||
in hexadecimal. It is normally only used for debugging purposes. */
|
||
|
||
/* Entry Parameters */
|
||
char *title; /* Title for the display */
|
||
struct _bv *bv; /* pointer to the bit vector */
|
||
|
||
/* Exit Parameters
|
||
None.
|
||
*/
|
||
|
||
{
|
||
char *bits; /* working pointer */
|
||
unsigned byte_count; /* count used for formatting display */
|
||
unsigned bit_count; /* count for processing bits in a byte */
|
||
char byte_value; /* value to be displayed */
|
||
|
||
printf("\nBit Vector : %s",title); /* display title */
|
||
|
||
bits = bv -> bv_bits; /* set working pointer */
|
||
byte_count = 0; /* initialize count */
|
||
|
||
while (bits != bv -> bv_end) /* for the entire vector */
|
||
{
|
||
if (byte_count % 5 == 0) /* chk if new line */
|
||
printf("\n%4d : ",byte_count << 3); /* display bit number */
|
||
|
||
byte_value = *bits++; /* get the next byte from the vector */
|
||
|
||
for (bit_count = 0; bit_count < 8; bit_count++)
|
||
{
|
||
/* display the left-most bit, then shift the value
|
||
left one bit */
|
||
if (bit_count == 4) putchar(' '); /* separator */
|
||
putchar((byte_value & 0x80) ? '1' : '0');
|
||
byte_value <<= 1; /* shift value left */
|
||
}
|
||
printf(" "); /* separator */
|
||
|
||
byte_count++; /* update byte count */
|
||
}
|
||
}
|
||
|
||
/* End of LIBRARY.C */
|
||
|