Files
Digital-Research-Source-Code/CONTRIBUTIONS/cpm-handbook/cpmsrc/LIBRARY.C
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

1363 lines
44 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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