mirror of
https://github.com/SEPPDROID/Digital-Research-Source-Code.git
synced 2025-12-13 00:13:25 +00:00
Upload
Digital Research
This commit is contained in:
88
CONTRIBUTIONS/cpm-handbook/READ-ME.txt
Normal file
88
CONTRIBUTIONS/cpm-handbook/READ-ME.txt
Normal file
@@ -0,0 +1,88 @@
|
||||
[READ.ME]
|
||||
The Programmer's CP/M Handbook Source Code Examples
|
||||
Version 1.0 August 18, 1983
|
||||
(c) 1983 Johnson-Laird Inc.
|
||||
|
||||
We have tried to include as many of the examples from the CP/M
|
||||
Programmer's Handbook as was possible to fit onto 2 single-sided
|
||||
single-density diskettes (or alternately, 1 single-density
|
||||
double-sided "flippy" diskette). Our original intention was to
|
||||
include the source code for every figure in the book as well as all
|
||||
the example listings in Chapter 5. Since this proved to be impossible
|
||||
to fit onto two diskettes, we have chosen those figures and examples
|
||||
which we felt would be the most useful. The only major omission is
|
||||
Figure 9-4 which deals with bad sector management.
|
||||
|
||||
We have fixed one bug and added one line to Figure 8-10. This is
|
||||
noted in the source code. The lines which were left out in the book
|
||||
after page 261 are also included. You may notice some minor
|
||||
differences in capitalization, indentation, etc. in the comments where
|
||||
we have not included changes made in copyediting.
|
||||
|
||||
We would appreciate hearing from you, especially about any bugs,
|
||||
typos, other horrible goofs, suggestions for improvements.
|
||||
|
||||
Contents of Diskette No 1
|
||||
|
||||
Filename Page No. Figure Title
|
||||
|
||||
FIG5-2.ASM 70 Equates for BDOS function code numbers
|
||||
FIG5-3.ASM 74 Write console byte example, output null-byte
|
||||
terminated message from specified address
|
||||
FIG5-4.ASM 74 Write console byte example, output null-byte
|
||||
terminated message following call to subroutine
|
||||
FIG5-5.ASM 76 Read line from reader device
|
||||
FIG5-6.ASM 78 Write line to punch device
|
||||
FIG5-7.ASM 79 Write line to list device
|
||||
FIG5-8.ASM 81 Read/Write string from/to console using raw I/O
|
||||
FIG5-10.ASM 86 IOBYTE equates
|
||||
FIG5-11.ASM 87 Simple terminal emulator
|
||||
FIG5-12.ASM 89 Display $-terminated message on console
|
||||
FIG5-13.ASM 92 Read console string for keyboard options
|
||||
FIG5-14.ASM 95 Determine the CP/M version number
|
||||
FIG5-15.ASM 96 Reset requested disk drive
|
||||
FIG5-16.ASM 100 Open file request
|
||||
FIG5-17.ASM 104 Search first/next calls for ambiguous filename
|
||||
FIG5-18.ASM 110 Read next character from sequential disk file
|
||||
FIG5-19.ASM 113 Write next character to sequential disk file
|
||||
FIG5-20.ASM 115 Create file request
|
||||
FIG5-21.ASM 117 Rename file request
|
||||
FIG5-22.ASM 122 Set file attributes
|
||||
FIG5-23.ASM 123 Get file attributes
|
||||
FIG5-24.ASM 126 Accessing disk parameter block data
|
||||
FUNCTN33.ASM 131 Example for function 33, read random
|
||||
FIG5-25.ASM 135 Create random file
|
||||
FIG5-26.ASM 136 Read/write variable length records randomly
|
||||
FIG6-4.ASM 159 Simple BIOS listing
|
||||
FIG7-5.ASM 191 Example PUTCP/M
|
||||
FIG7-7.ASM 198 Example CP/M cold bootstrap loader
|
||||
FIG8-6.ASM 226 Device table equates
|
||||
FIG10-5.ASM 363 Testbed for real time clock driver in the BIOS
|
||||
FIG10-6.ASM 365 Testbed for disk I/O drivers in the BIOS
|
||||
ERASE.C 410 Figure 11-3, requests confirmation before erasing
|
||||
UNERASE.C 412 Figure 11-4, "revives" erased files
|
||||
FIND.C 417 Figure 11-5, locates specific files or groups of files
|
||||
SPACE.C 420 Figure 11-6, displays how much disk storage is used
|
||||
or available
|
||||
MOVE.C 424 Figure 11-7, "moves" files from one user to another
|
||||
MAKE.C 428 Figure 11-8, makes files "invisible" and protected
|
||||
or makes them "visible," accessible and unprotected
|
||||
SPEED.C 431 Figure 11-9, sets the baud rate for a specific device
|
||||
PROTOCOL.C 435 Figure 11-10, sets the protocol governing input and output
|
||||
of a specified serial device
|
||||
ASSIGN.C 439 Figure 11-11, assigns a logical devices input and output
|
||||
to two or more physical devices
|
||||
DATE.C 443 Figure 11-12, makes the current date part of the system
|
||||
TIME.C 444 Figure 11-13, makes the current time part of the system
|
||||
FUNKEY.C 446 Figure 11-14, sets the character strings associated with
|
||||
specific function keys
|
||||
|
||||
Contents of Diskette 2
|
||||
|
||||
FIG8-10.ASM 237 Enhanced BIOS listing
|
||||
FIG9-5.ASM 312 User-friendly disk error processor
|
||||
FIG10-2.ASM 331 Debug subroutines
|
||||
FIG10-4.ASM 355 Testbed for character I/O drivers
|
||||
LIBRARY.C 372 Figure 11-1, commonly used functions in C
|
||||
LIBRARY.H 390 Figure 11-2, code to be included at the beginning of
|
||||
any program that calls LIBRARY functions in Figure 11-1
|
||||
121
CONTRIBUTIONS/cpm-handbook/README.MARKDOWN
Normal file
121
CONTRIBUTIONS/cpm-handbook/README.MARKDOWN
Normal file
@@ -0,0 +1,121 @@
|
||||
Source code examples from "The Programmers CP/M Handbook": by Andy Johnson-Laird, copyright 1983 by Osborne/McGraw-Hill. Andy has given his permission to share this code for non-commercial use.
|
||||
|
||||
/READ-ME.txt
|
||||
|
||||
[READ.ME]
|
||||
The Programmer's CP/M Handbook Source Code Examples
|
||||
Version 1.0 August 18, 1983
|
||||
(c) 1983 Johnson-Laird Inc.
|
||||
|
||||
We have tried to include as many of the examples from the CP/M
|
||||
Programmer's Handbook as was possible to fit onto 2 single-sided
|
||||
single-density diskettes (or alternately, 1 single-density
|
||||
double-sided "flippy" diskette). Our original intention was to
|
||||
include the source code for every figure in the book as well as all
|
||||
the example listings in Chapter 5. Since this proved to be impossible
|
||||
to fit onto two diskettes, we have chosen those figures and examples
|
||||
which we felt would be the most useful. The only major omission is
|
||||
Figure 9-4 which deals with bad sector management.
|
||||
|
||||
We have fixed one bug and added one line to Figure 8-10. This is
|
||||
noted in the source code. The lines which were left out in the book
|
||||
after page 261 are also included. You may notice some minor
|
||||
differences in capitalization, indentation, etc. in the comments where
|
||||
we have not included changes made in copyediting.
|
||||
|
||||
We would appreciate hearing from you, especially about any bugs,
|
||||
typos, other horrible goofs, suggestions for improvements.
|
||||
|
||||
Contents of Diskette No 1
|
||||
|
||||
Filename Page No. Figure Title
|
||||
|
||||
FIG5-2.ASM 70 Equates for BDOS function code numbers
|
||||
FIG5-3.ASM 74 Write console byte example, output null-byte
|
||||
terminated message from specified address
|
||||
FIG5-4.ASM 74 Write console byte example, output null-byte
|
||||
terminated message following call to subroutine
|
||||
FIG5-5.ASM 76 Read line from reader device
|
||||
FIG5-6.ASM 78 Write line to punch device
|
||||
FIG5-7.ASM 79 Write line to list device
|
||||
FIG5-8.ASM 81 Read/Write string from/to console using raw I/O
|
||||
FIG5-10.ASM 86 IOBYTE equates
|
||||
FIG5-11.ASM 87 Simple terminal emulator
|
||||
FIG5-12.ASM 89 Display $-terminated message on console
|
||||
FIG5-13.ASM 92 Read console string for keyboard options
|
||||
FIG5-14.ASM 95 Determine the CP/M version number
|
||||
FIG5-15.ASM 96 Reset requested disk drive
|
||||
FIG5-16.ASM 100 Open file request
|
||||
FIG5-17.ASM 104 Search first/next calls for ambiguous filename
|
||||
FIG5-18.ASM 110 Read next character from sequential disk file
|
||||
FIG5-19.ASM 113 Write next character to sequential disk file
|
||||
FIG5-20.ASM 115 Create file request
|
||||
FIG5-21.ASM 117 Rename file request
|
||||
FIG5-22.ASM 122 Set file attributes
|
||||
FIG5-23.ASM 123 Get file attributes
|
||||
FIG5-24.ASM 126 Accessing disk parameter block data
|
||||
FUNCTN33.ASM 131 Example for function 33, read random
|
||||
FIG5-25.ASM 135 Create random file
|
||||
FIG5-26.ASM 136 Read/write variable length records randomly
|
||||
FIG6-4.ASM 159 Simple BIOS listing
|
||||
FIG7-5.ASM 191 Example PUTCP/M
|
||||
FIG7-7.ASM 198 Example CP/M cold bootstrap loader
|
||||
FIG8-6.ASM 226 Device table equates
|
||||
FIG10-5.ASM 363 Testbed for real time clock driver in the BIOS
|
||||
FIG10-6.ASM 365 Testbed for disk I/O drivers in the BIOS
|
||||
ERASE.C 410 Figure 11-3, requests confirmation before erasing
|
||||
UNERASE.C 412 Figure 11-4, "revives" erased files
|
||||
FIND.C 417 Figure 11-5, locates specific files or groups of files
|
||||
SPACE.C 420 Figure 11-6, displays how much disk storage is used
|
||||
or available
|
||||
MOVE.C 424 Figure 11-7, "moves" files from one user to another
|
||||
MAKE.C 428 Figure 11-8, makes files "invisible" and protected
|
||||
or makes them "visible," accessible and unprotected
|
||||
SPEED.C 431 Figure 11-9, sets the baud rate for a specific device
|
||||
PROTOCOL.C 435 Figure 11-10, sets the protocol governing input and output
|
||||
of a specified serial device
|
||||
ASSIGN.C 439 Figure 11-11, assigns a logical devices input and output
|
||||
to two or more physical devices
|
||||
DATE.C 443 Figure 11-12, makes the current date part of the system
|
||||
TIME.C 444 Figure 11-13, makes the current time part of the system
|
||||
FUNKEY.C 446 Figure 11-14, sets the character strings associated with
|
||||
specific function keys
|
||||
|
||||
Contents of Diskette 2
|
||||
|
||||
FIG8-10.ASM 237 Enhanced BIOS listing
|
||||
FIG9-5.ASM 312 User-friendly disk error processor
|
||||
FIG10-2.ASM 331 Debug subroutines
|
||||
FIG10-4.ASM 355 Testbed for character I/O drivers
|
||||
LIBRARY.C 372 Figure 11-1, commonly used functions in C
|
||||
LIBRARY.H 390 Figure 11-2, code to be included at the beginning of
|
||||
any program that calls LIBRARY functions in Figure 11-1
|
||||
|
||||
|
||||
|
||||
|
||||
/_dedication.txt
|
||||
|
||||
The included files are the source code examples from Andy Johnson-Laird's book, "The Programmers CP/M Handbook", copyright 1983 by Osborne/McGraw-Hill.
|
||||
|
||||
Andy has given his permission to share this code for non-commercial use and asked that the following information be included with this distribution -
|
||||
|
||||
*******************************************
|
||||
|
||||
Please give me credit for the code.
|
||||
|
||||
You might even consider putting in a two line tribute to the late Gary Kildall.
|
||||
|
||||
Andy has requested that those who use this code pause to consider where we would
|
||||
be if the late Gary Kildall, a pioneer's pioneer, had not taken the time to write CP/M.
|
||||
|
||||
If you want to know the *real* story, I can recommend the book "They Made America" by Sir Harold Evans. It will tell you just how significant Gary's work was, and how Mr. Gates got to where he is.
|
||||
The second edition has a far more complete story.
|
||||
|
||||
Regards
|
||||
Andy
|
||||
|
||||
********************************************
|
||||
|
||||
Posted 10/8/07
|
||||
Jack Rubin
|
||||
23
CONTRIBUTIONS/cpm-handbook/_dedication.txt
Normal file
23
CONTRIBUTIONS/cpm-handbook/_dedication.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
The included files are the source code examples from Andy Johnson-Laird's book, "The Programmers CP/M Handbook", copyright 1983 by Osborne/McGraw-Hill.
|
||||
|
||||
Andy has given his permission to share this code for non-commercial use and asked that the following information be included with this distribution -
|
||||
|
||||
*******************************************
|
||||
|
||||
Please give me credit for the code.
|
||||
|
||||
You might even consider putting in a two line tribute to the late Gary Kildall.
|
||||
|
||||
Andy has requested that those who use this code pause to consider where we would
|
||||
be if the late Gary Kildall, a pioneer's pioneer, had not taken the time to write CP/M.
|
||||
|
||||
If you want to know the *real* story, I can recommend the book "They Made America" by Sir Harold Evans. It will tell you just how significant Gary's work was, and how Mr. Gates got to where he is.
|
||||
The second edition has a far more complete story.
|
||||
|
||||
Regards
|
||||
Andy
|
||||
|
||||
********************************************
|
||||
|
||||
Posted 10/8/07
|
||||
Jack Rubin
|
||||
230
CONTRIBUTIONS/cpm-handbook/cpmsrc/ASSIGN.C
Normal file
230
CONTRIBUTIONS/cpm-handbook/cpmsrc/ASSIGN.C
Normal file
@@ -0,0 +1,230 @@
|
||||
#define VN "\nASSIGN Vn 1.0 02/17/83"
|
||||
|
||||
#include <LIBRARY.H>
|
||||
|
||||
struct _ct ct_pdev[MAXPDEV + 2]; /* physical device table */
|
||||
|
||||
|
||||
/* names of logical devices */
|
||||
#define LN_C "CONSOLE"
|
||||
#define LN_A "AUXILIARY"
|
||||
#define LN_L "LIST"
|
||||
struct _ct ct_ldev[4]; /* logical device table */
|
||||
|
||||
struct _ct ct_io[3]; /* input, output */
|
||||
|
||||
/* parameters on the command line */
|
||||
#define LDEV argv[1] /* logical device */
|
||||
#define IO argv[2] /* input/output */
|
||||
|
||||
|
||||
main(argc,argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
|
||||
printf(VN); /* display signon message */
|
||||
setup(); /* setup code tables */
|
||||
chk_use(argc); /* check correct usage */
|
||||
|
||||
/* check if request to show current settings */
|
||||
if (usstrcmp("SHOW",argv[1]))
|
||||
{ /* no, assume a set is required */
|
||||
/* NOTE : the number of physical devices to
|
||||
process is given by argc -3 */
|
||||
set_assign(get_ldev(LDEV),get_io(IO),argc - 3,argv);
|
||||
}
|
||||
show_assign();
|
||||
|
||||
}
|
||||
|
||||
setup() /* setup the code tables for this program */
|
||||
{
|
||||
/* initialize the physical device table */
|
||||
ct_init(ct_pdev[0],0,PN_T); /* terminal */
|
||||
ct_init(ct_pdev[1],1,PN_P); /* printer */
|
||||
ct_init(ct_pdev[2],2,PN_M); /* modem */
|
||||
ct_init(ct_pdev[3],CT_SNF,"*"); /* terminator */
|
||||
|
||||
/* initialize the logical device table */
|
||||
ct_init(ct_ldev[0],0,LN_C); /* terminal */
|
||||
ct_init(ct_ldev[1],1,LN_A); /* auxiliary */
|
||||
ct_init(ct_ldev[2],2,LN_L); /* list */
|
||||
ct_init(ct_ldev[3],CT_SNF,"*"); /* terminator */
|
||||
|
||||
/* initialize the input/output table */
|
||||
ct_init(ct_io[0],0,"INPUT");
|
||||
ct_init(ct_io[1],1,"OUTPUT");
|
||||
ct_init(ct_io[2],CT_SNF,"*"); /* terminator */
|
||||
|
||||
}
|
||||
|
||||
unsigned
|
||||
get_ldev(pldev) /* get logical device */
|
||||
/* This function returns the logical device code
|
||||
specified by the user in the command line. */
|
||||
char *pldev; /* pointer to character string */
|
||||
{
|
||||
unsigned retval; /* return value */
|
||||
retval = ct_parc(ct_ldev,pldev); /* get code for ASCII string */
|
||||
if (retval == CT_SNF) /* if string not found */
|
||||
{
|
||||
printf("\n\007Logical Device '%s' is invalid or ambiguous.",
|
||||
pldev);
|
||||
printf("\nLegal Logical Devices are : ");
|
||||
ct_disps(ct_ldev); /* display all values */
|
||||
exit();
|
||||
}
|
||||
return retval; /* return code */
|
||||
}
|
||||
|
||||
unsigned
|
||||
get_io(pio) /* get input/output parameter */
|
||||
char *pio; /* pointer to character string */
|
||||
{
|
||||
unsigned retval; /* return value */
|
||||
|
||||
retval = ct_parc(ct_io,pio); /* get code for ASCII string */
|
||||
if (retval == CT_SNF) /* if string not found */
|
||||
{
|
||||
printf("\n\007Input/Output direction '%s' is invalid or ambiguous.",
|
||||
pio);
|
||||
printf("\nLegal values are : ");
|
||||
ct_disps(ct_io); /* display all values */
|
||||
exit();
|
||||
}
|
||||
return retval; /* return code */
|
||||
}
|
||||
|
||||
set_assign(ldevc,output,argc,argv) /* set assignment (i/o redirection) */
|
||||
int ldevc; /* logical device code */
|
||||
int output; /* i/o redirection code */
|
||||
int argc; /* count of arguments to process */
|
||||
char *argv[]; /* replica of parameter to main function */
|
||||
{
|
||||
unsigned *redir; /* pointer to redirection word */
|
||||
int pdevc; /* physical device code */
|
||||
unsigned rd_val; /* redirection value */
|
||||
|
||||
/* get the address of the i/o redirection word.
|
||||
this code assumes that get_cba code values
|
||||
are ordered :
|
||||
Device #0, Input & Output
|
||||
Device #1, Input & Output
|
||||
Device #2, Input & Output.
|
||||
the get_cba code is computed by multiplying the
|
||||
logical device code * 2 (that is, shift left 1)
|
||||
and added onto the code for Device #0, Input.
|
||||
then the output variable (0 = Input, 1 = Output)
|
||||
is added on. */
|
||||
redir = get_cba(CB_CI + (ldevc << 1) + output);
|
||||
|
||||
rd_val = 0; /* initialize redirection value */
|
||||
|
||||
/* for output, assignment can be made to several physical
|
||||
devices, so this code may be executed several times. */
|
||||
do
|
||||
{
|
||||
/* get code for ASCII string */
|
||||
/* NOTE : the physical device parameters start
|
||||
with parameter #3 (argv[3]). However argc
|
||||
is a decreasing count of the number of physical
|
||||
devices to be processed - therefore argc + 2
|
||||
causes them to be processed in reverse order
|
||||
(i.e. from right to left on the command line) */
|
||||
|
||||
pdevc = ct_parc(ct_pdev,argv[argc + 2]);
|
||||
|
||||
if (pdevc == CT_SNF) /* if string not found */
|
||||
{
|
||||
printf("\n\007Physical Device '%s' is invalid or ambiguous.",
|
||||
argv[argc + 2]);
|
||||
printf("\nLegal Physical Devices are : ");
|
||||
ct_disps(ct_pdev); /* display all values */
|
||||
exit();
|
||||
}
|
||||
/* repeat this loop for as long as there are
|
||||
more parameters (for output only) */
|
||||
else
|
||||
{
|
||||
/* build new redirection value by OR'ing in
|
||||
a 1-bit shifted left pdevc places. */
|
||||
rd_val |= (1 << pdevc);
|
||||
}
|
||||
} while (--argc && output);
|
||||
|
||||
*redir = rd_val; /* set the value into the config. block */
|
||||
}
|
||||
|
||||
show_assign() /* show current baud rate */
|
||||
{
|
||||
int rd_code; /* redirection code for get_cba */
|
||||
int ldevn; /* logical device number */
|
||||
int pdevn; /* physical device number */
|
||||
unsigned rd_val; /* re-direction value */
|
||||
unsigned *prd_val; /* pointer to the redirection value */
|
||||
|
||||
/* Note : the respective codes for accessing the redirection values
|
||||
via the get_cba (get configuration block address) function are:
|
||||
Device #0 Console Input - 5
|
||||
Device #0 Console Output - 6
|
||||
Device #1 Auxiliary Input - 7
|
||||
Device #1 Auxiliary Output - 8
|
||||
Device #2 List Input - 9
|
||||
Device #2 List Output - 10
|
||||
|
||||
This function uses this mathematical relationship. */
|
||||
|
||||
printf("\nCurrent Device Assignments are :");
|
||||
|
||||
|
||||
/* for all get_cba codes */
|
||||
for (rd_code = CB_CI; rd_code <= CB_LO; rd_code++)
|
||||
{
|
||||
/* set pointer to redirection value */
|
||||
prd_val = get_cba(rd_code);
|
||||
/* get the input redirection value */
|
||||
rd_val = *prd_val; /* this also performs byte-reversal */
|
||||
|
||||
/* display device name. rd_code is converted to a
|
||||
device number by subtracting the first code number
|
||||
from it and dividing by 2 (shift right one place).
|
||||
the input/output direction is derived from the
|
||||
least significant bit of the rd_code. */
|
||||
|
||||
printf("\n\t%s %s is assigned to - ",
|
||||
ct_strc(ct_ldev,(rd_code - CB_CI) >> 1),
|
||||
ct_strc(ct_io,((rd_code & 0x01) ^ 1)));
|
||||
|
||||
/* for all physical devices */
|
||||
for (pdevn = 0; pdevn < 16; pdevn++)
|
||||
{
|
||||
/* check if current physical device is assigned
|
||||
by ANDing with a 1-bit shifted left pdevn times */
|
||||
if (rd_val & (1 << pdevn)) /* is device active */
|
||||
{ /* display physical device name */
|
||||
printf(" %s",ct_strc(ct_pdev,pdevn) );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
chk_use(argc) /* check for correct usage */
|
||||
int argc; /* argument count on commmand line */
|
||||
{
|
||||
if (argc == 1)
|
||||
{
|
||||
printf("\nASSIGN sets the Input/Output redirection.");
|
||||
printf("\n\tASSIGN logical-device INPUT physical-device");
|
||||
printf("\n\tASSIGN logical-device OUTPUT physical-dev1 {phy_dev2..}");
|
||||
printf("\n\tASSIGN SHOW (to show current assignments)");
|
||||
printf("\n\nLegal logical devices are :");
|
||||
ct_disps(ct_ldev);
|
||||
printf("\nLegal physical devices are :");
|
||||
ct_disps(ct_pdev);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
79
CONTRIBUTIONS/cpm-handbook/cpmsrc/DATE.C
Normal file
79
CONTRIBUTIONS/cpm-handbook/cpmsrc/DATE.C
Normal file
@@ -0,0 +1,79 @@
|
||||
#define VN "\nDATE Vn 1.0 02/18/83"
|
||||
/* This utility accepts the current date from the command tail
|
||||
validates it, and set the internal system date in the BIOS.
|
||||
Alternatively, it can be requested just to display the current
|
||||
system date. */
|
||||
|
||||
#include <LIBRARY.H>
|
||||
|
||||
char *date; /* pointer to the date in the config. block */
|
||||
char *date_flag; /* pointer to date set flag */
|
||||
int mm,dd,yy; /* variables to hold month, day, year */
|
||||
int mcount; /* match count of numeric values entered */
|
||||
int count; /* count used to add leading 0's to date */
|
||||
|
||||
main(argc,argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
printf(VN); /* display signon message */
|
||||
date = get_cba(CB_DATE); /* set pointer to date */
|
||||
date_flag = get_cba(CB_DTFLAGS); /* set pointer to "date set" flag */
|
||||
|
||||
if (argc != 2) /* check if help requested (or needed) */
|
||||
show_use(); /* display correct usage and exit */
|
||||
|
||||
if (usstrcmp("SHOW",argv[1])) /* check if not SHOW option */
|
||||
{
|
||||
/* convert specified time into month, day, year */
|
||||
mcount = sscanf(argv[1],"%d/%d/%d",&mm,&dd,&yy);
|
||||
if (mcount != 3) /* input not numeric */
|
||||
show_use(); /* display correct usage and exit */
|
||||
|
||||
/* NOTE : The following validity checking is VERY
|
||||
simplistic - this could be expanded to accomodate
|
||||
more context-sensitive checking, days in the month,
|
||||
leap years, etc. */
|
||||
if (mm > 12 || mm < 1) /* check valid month, day, year */
|
||||
{
|
||||
printf("\nMonth = %d is illegal.",mm);
|
||||
show_use(); /* display correct usage and exit */
|
||||
}
|
||||
if (dd > 31 || dd < 1)
|
||||
{
|
||||
printf("\nDay = %d is illegal.",dd);
|
||||
show_use(); /* display correct usage and exit */
|
||||
}
|
||||
if (yy > 90 || yy < 83) /* <=== NOTE ! */
|
||||
{
|
||||
printf("\nYear = %d is illegal.",yy);
|
||||
show_use(); /* display correct usage and exit */
|
||||
}
|
||||
|
||||
/* convert integers back into a formatted string */
|
||||
sprintf(date,"%2d/%2d/%2d",mm,dd,yy);
|
||||
date[8] = 0x0A; /* terminate with line feed */
|
||||
date[9] = '\0'; /* new string terminator */
|
||||
|
||||
/* change " 1/ 2/ 3" into "01/02/03" */
|
||||
for (count = 0; count < 7; count+=3)
|
||||
{
|
||||
if (date[count] == ' ')
|
||||
date[count] = '0';
|
||||
}
|
||||
|
||||
/* turn flag on to indicate that the user has set the date */
|
||||
*date_flag |= DATE_SET;
|
||||
}
|
||||
printf("\n\tCurrent Date is %s",date);
|
||||
}
|
||||
|
||||
show_use() /* display correct usage and exit */
|
||||
{
|
||||
printf("\nDATE sets the system date. Usage is :");
|
||||
printf("\n\tDATE mm/dd/yy");
|
||||
printf("\n\tDATE SHOW (to display current date)\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
107
CONTRIBUTIONS/cpm-handbook/cpmsrc/ERASE.C
Normal file
107
CONTRIBUTIONS/cpm-handbook/cpmsrc/ERASE.C
Normal file
@@ -0,0 +1,107 @@
|
||||
#define VN "1.0 02/24/83"
|
||||
/* ERASE
|
||||
This utility erases the specified file(s). It performs a logical
|
||||
erasure by using a BDOS Delete function. */
|
||||
|
||||
#include <LIBRARY.H>
|
||||
|
||||
struct _fcb amb_fcb; /* ambiguous name file control block */
|
||||
struct _fcb fcb; /* used for BDOS Search Functions */
|
||||
|
||||
char file_name[20]; /* formatted for display : d:FILENAME.TYP */
|
||||
short cur_disk; /* current logical disk at start of program */
|
||||
/* ERASE saves up the FCB's of the all the
|
||||
files that need to be erased in the
|
||||
following array. */
|
||||
#define MAXERA 1024
|
||||
struct _fcb era_fcb[MAXERA];
|
||||
int ecount; /* count of number of files to be erased */
|
||||
int count; /* used to access era_fcb during erasing */
|
||||
|
||||
main(argc,argv)
|
||||
short argc; /* argument count */
|
||||
char *argv[]; /* argument vector (pointer to an array of chars) */
|
||||
{
|
||||
|
||||
printf("\nERASE Version %s (Library %s)",VN,LIBVN);
|
||||
chk_use(argc); /* check usage */
|
||||
cur_disk = bdos(GETDISK); /* get current default disk */
|
||||
|
||||
ecount = 0; /* initialize count of files to erase */
|
||||
|
||||
setfcb(amb_fcb,argv[1]); /* set ambiguous file name */
|
||||
if (amb_fcb.fcb_disk) /* check if default disk to be used */
|
||||
{
|
||||
bdos(SETDISK,amb_fcb.fcb_disk + 1); /* set to specified disk */
|
||||
}
|
||||
|
||||
/* convert ambiguous file name for output */
|
||||
conv_fname(amb_fcb,file_name);
|
||||
printf("\n\nSearching for file(s) matching %s.",file_name);
|
||||
|
||||
/* set the file control block to indicate a "first" search */
|
||||
fcb.fcb_disk |= 0x80; /* OR in the ms bit */
|
||||
|
||||
/* while not at the end of the directory, set the FCB
|
||||
to the next name that matches */
|
||||
while(get_nfn(amb_fcb,fcb))
|
||||
{
|
||||
conv_fname(fcb,file_name);
|
||||
/* ask whether to erase file or not */
|
||||
printf("\n\tErase %s y/n? ",file_name);
|
||||
if (toupper(getchar()) == 'Y')
|
||||
{
|
||||
printf(" <== Will be Erased!");
|
||||
/* add current fcb to array of fcb's */
|
||||
movmem(fcb,&era_fcb[ecount++],FCBSIZE);
|
||||
/* check that the table is not full */
|
||||
if (ecount == MAXERA)
|
||||
{
|
||||
printf("\nWarning : Internal Table now full. No more files can be erased");
|
||||
printf("\n until those already specified have been erased.");
|
||||
break; /* break out of while loop */
|
||||
}
|
||||
}
|
||||
} /* all directory entries processed */
|
||||
|
||||
if (ecount)
|
||||
printf("\n\nErasing files now...");
|
||||
|
||||
/* now process each fcb in the array, erasing the files */
|
||||
for (count = 0; /* starting with the first file in the array */
|
||||
count < ecount; /* until all active entries processed */
|
||||
count++) /* move to next fcb */
|
||||
{
|
||||
conv_fname(&era_fcb[count],file_name);
|
||||
if (bdos(DELETEF,&era_fcb[count]) == -1) /* error? */
|
||||
printf("\n\007Error trying to erase %s",file_name);
|
||||
else /* file erased */
|
||||
printf("\n\tFile %s erased.",file_name);
|
||||
}
|
||||
bdos(SETDISK,cur_disk); /* reset to current disk */
|
||||
}
|
||||
|
||||
|
||||
chk_use(argc) /* check usage */
|
||||
/* This function checks that the correct number of
|
||||
parameters has been specified, outputting instructions
|
||||
if not. */
|
||||
|
||||
/* Entry Parameter */
|
||||
int argc; /* Count of the number of arguments on the command line */
|
||||
{
|
||||
|
||||
/* The minimum value of argc is 1 (for the program name itself),
|
||||
so argc is always one greater than the number of parameters
|
||||
on the command line */
|
||||
|
||||
if (argc != 2)
|
||||
{
|
||||
printf("\nUsage :");
|
||||
printf("\n\tERASE {d:}file_name.typ");
|
||||
exit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
1423
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG10-2.ASM
Normal file
1423
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG10-2.ASM
Normal file
File diff suppressed because it is too large
Load Diff
95
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG10-4.ASM
Normal file
95
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG10-4.ASM
Normal file
@@ -0,0 +1,95 @@
|
||||
; Figure 10-4
|
||||
;
|
||||
; Testbed for Character I/O drivers in the BIOS.
|
||||
;
|
||||
; The complete source file consists of three components:
|
||||
;
|
||||
; 1. The testbed code shown here.
|
||||
; 2. The Character I/O drivers destined for the BIOS.
|
||||
; 3. The debug package shown in Figure 10-2.
|
||||
;
|
||||
TRUE EQU 0FFFFH
|
||||
FALSE EQU NOT TRUE
|
||||
|
||||
DEBUG EQU TRUE ;For conditional assembly of RST
|
||||
; instructions in place of IN and
|
||||
; OUT instructions in the drivers.
|
||||
RST6 EQU 30H ;Use RST 6 for fake incoming character
|
||||
; interrupt.
|
||||
ORG 100H
|
||||
START:
|
||||
LXI SP,Test$Stack ;Use a local stack
|
||||
CALL DB$Init ;Initialize the debug package
|
||||
MVI A,JMP ;Setup RST 6 with JMP opcode
|
||||
STA RST6
|
||||
LXI H,Character$Interrupt ;Setup RST 6 JMP address
|
||||
SHLD RST6 + 1
|
||||
;
|
||||
; Make repeated entry to Character Interrupt routine
|
||||
; to ensure that characters can be captured and stored in
|
||||
; an input buffer
|
||||
;
|
||||
Testbed$Loop:
|
||||
MVI A,0AAH ;Set registers to known pattern
|
||||
LXI B,0BBCCH
|
||||
LXI D,0DDEEH
|
||||
LXI H,0FF11H
|
||||
RST 6 ;Fake interrupt for incoming character
|
||||
|
||||
CALL DB$MSGI ;Display in-line message
|
||||
DB 0DH,0AH,'Enter I to Input Char., O to Output, D to enter '
|
||||
DB 'DDT : ',0
|
||||
|
||||
CALL DB$CONINU ;Get upper case character
|
||||
CPI 'I' ;CONIN?
|
||||
JZ Go$CONIN
|
||||
CPI 'D' ;DDT?
|
||||
JZ Go$DDT
|
||||
CPI 'O' ;CONOUT?
|
||||
JZ Go$CONOUT
|
||||
JMP Testbed$Loop ;Loop back to interrupt again
|
||||
Go$DDT:
|
||||
RST 7 ;Enter DDT (RST 7 setup by DDT)
|
||||
JMP Testbed$Loop
|
||||
Go$CONIN:
|
||||
CALL CONST ;Get Console Status
|
||||
JZ Testbed$Loop ;No Data waiting
|
||||
CALL CONIN ;Get data from buffer
|
||||
|
||||
CALL DB$Display ;Display character returned
|
||||
DB DB$A ; in A register
|
||||
DB 'CONIN returned',0
|
||||
|
||||
JMP Go$CONIN ;Repeat CONIN loop until no chars.
|
||||
; waiting.
|
||||
;
|
||||
Go$CONOUT:
|
||||
CALL CONST ;Get Console Status
|
||||
JZ Testbed$Loop ;No data waiting
|
||||
CALL CONIN
|
||||
MOV C,A ;Ready for output
|
||||
CALL CONOUT ;Output to Console
|
||||
JMP Go$CONOUT ;Repeat while there is still data
|
||||
;
|
||||
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
|
||||
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
|
||||
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
|
||||
Test$Stack:
|
||||
;
|
||||
; Dummy routines for those shown in other figures
|
||||
;
|
||||
; BIOS routines (Figure 8-10)
|
||||
;
|
||||
CONST: ;BIOS Console Status
|
||||
CONIN: ;BIOS Console Input
|
||||
CONOUT: ;BIOS Console Output;
|
||||
Character$Interrupt: ;Interrupt service routine for incoming chars.
|
||||
;
|
||||
; Debug routines (Figure 10-2)
|
||||
;
|
||||
DB$Init: ;Debug initialization
|
||||
DB$MSGI: ;Display message in-line
|
||||
DB$CONINU: ;Get upper case character from keyboard
|
||||
DB$Display: ;Main debug display routine
|
||||
DB$A EQU 02 ;Display code for DB$Display
|
||||
|
||||
83
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG10-5.ASM
Normal file
83
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG10-5.ASM
Normal file
@@ -0,0 +1,83 @@
|
||||
; Figure 10-5
|
||||
;
|
||||
; Testbed for Real Time Clock driver in the BIOS.
|
||||
;
|
||||
; The complete source file consists of three components:
|
||||
;
|
||||
; 1. The testbed code shown here.
|
||||
; 2. The Real Time Clock driver destined for the BIOS.
|
||||
; 3. The debug package shown in Figure 10-2.
|
||||
;
|
||||
TRUE EQU 0FFFFH
|
||||
FALSE EQU NOT TRUE
|
||||
|
||||
DEBUG EQU TRUE ;For conditional assembly of RST
|
||||
; instructions in place of IN and
|
||||
; OUT instructions in the drivers.
|
||||
RST6 EQU 30H ;Use RST 6 for fake clock tick.
|
||||
|
||||
ORG 100H
|
||||
START:
|
||||
LXI SP,Test$Stack ;Use local stack
|
||||
CALL DB$Init ;Initialize the debug package
|
||||
MVI A,JMP ;Setup RST 6 with JMP opcode
|
||||
STA RST6
|
||||
LXI H,RTC$Interrupt ;Setup RST 6 JMP address
|
||||
SHLD RST6 + 1
|
||||
|
||||
JMP Testbed$Loop ;<=== REMOVE THIS JMP WHEN READY TO
|
||||
; TEST WATCHDOG ROUTINES
|
||||
|
||||
LXI B,50 ;50 ticks before timeout
|
||||
LXI H,WD$Timeout ;Address to transfer to
|
||||
CALL Set$Watchdog ;Set the watchdog timer
|
||||
;
|
||||
;
|
||||
; Make repeated entry to RTC Interrupt routine
|
||||
; to ensure that clock is correctly updated.
|
||||
;
|
||||
Testbed$Loop:
|
||||
MVI A,0AAH ;Set registers to known pattern
|
||||
LXI B,0BBCCH
|
||||
LXI D,0DDEEH
|
||||
LXI H,0FF11H
|
||||
RST 6 ;Fake interrupt clock
|
||||
|
||||
CALL DB$MSGI ;Display in-line message
|
||||
DB 'Clock =',0
|
||||
|
||||
LXI H,Time$In$ASCII ;Get address of clock in driver
|
||||
CALL DB$MSG ;Display current clock value
|
||||
; (Note : Time$In$ASCII already has
|
||||
; a Line Feed character in it)
|
||||
CALL DB$MSGI ;Display in-line message
|
||||
DB 0DH,0 ;Carriage Return
|
||||
|
||||
JMP Testbed$Loop
|
||||
;
|
||||
; Control arrives here when the watchdog timer times
|
||||
; out.
|
||||
WD$Timeout:
|
||||
CALL DB$MSGI
|
||||
DB 0DH,0AH,'Watchdog timed out',0
|
||||
RET ;Return to watchdog routine
|
||||
;
|
||||
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
|
||||
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
|
||||
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
|
||||
Test$Stack:
|
||||
;
|
||||
; Dummy routines for those shown in other figures
|
||||
;
|
||||
; BIOS routines (Figure 8-10)
|
||||
;
|
||||
RTC$Interrupt: ;Interrupt service routine for clock tick
|
||||
Set$Watchdog: ;Set Watchdog timer
|
||||
Time$In$ASCII: ;ASCII string of HH:MM:SS, LF, 0
|
||||
;
|
||||
; Debug routines (Figure 10-2)
|
||||
;
|
||||
DB$Init: ;Debug initialization
|
||||
DB$MSGI: ;Display message in-line
|
||||
DB$MSG: ;Display message
|
||||
|
||||
168
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG10-6.ASM
Normal file
168
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG10-6.ASM
Normal file
@@ -0,0 +1,168 @@
|
||||
; Figure 10-6
|
||||
;
|
||||
; Testbed for Disk I/O drivers in the BIOS.
|
||||
;
|
||||
; The complete source file consists of three components:
|
||||
;
|
||||
; 1. The testbed code shown here.
|
||||
; 2. The Disk I/O drivers destined for the BIOS.
|
||||
; 3. The debug package shown in Figure 10-2.
|
||||
;
|
||||
TRUE EQU 0FFFFH
|
||||
FALSE EQU NOT TRUE
|
||||
|
||||
DEBUG EQU TRUE ;For conditional assembly of RST
|
||||
; instructions in place of IN and
|
||||
; OUT instructions in the drivers.
|
||||
ORG 100H
|
||||
START:
|
||||
LXI SP,Test$Stack ;Use a local stack
|
||||
CALL DB$Init ;Initialize the debug package
|
||||
;
|
||||
; Make calls to SELDSK, SETTRK, SETSEC and SETDMA,
|
||||
; then either a Read or Write routine.
|
||||
;
|
||||
Testbed$Loop:
|
||||
LXI SP,Test$Stack ;Use local stack
|
||||
|
||||
LDA Logical$Disk ;Setup for SELDSK call
|
||||
MOV C,A
|
||||
CALL SELDSK
|
||||
|
||||
CALL DB$Display ;Display return value in HL
|
||||
DB DB$HL
|
||||
DB 'SELDSK returned',0
|
||||
|
||||
SHLD DPH$Start ;Setup to display Disk Parameter Header
|
||||
LXI D,16 ;Compute end address
|
||||
DAD D
|
||||
SHLD DPH$End ;Store into debug call
|
||||
|
||||
CALL DB$Display ;Display DPH
|
||||
DB DB$M ;Memory
|
||||
DPH$Start:
|
||||
DW 0
|
||||
DPH$End:
|
||||
DW 0
|
||||
DB 'Selected DPH',0
|
||||
|
||||
LHLD Track ;Call SETTRK
|
||||
PUSH H
|
||||
POP B ;SETTRK needs track in BC
|
||||
CALL SETTRK
|
||||
|
||||
LDA Sector ;Call SETSEC
|
||||
MOV C,A ;SETSEC need sector in C
|
||||
CALL SETSEC
|
||||
|
||||
LXI B,Test$Buffer ;Set DMA address
|
||||
CALL SETDMA
|
||||
|
||||
LDA Write$Disk ;Check if reading or writing
|
||||
ORA A
|
||||
JNZ Test$Write
|
||||
|
||||
CALL Read$No$Deblock ;*** or Read$Physical depending on which
|
||||
;*** drivers you are testing.
|
||||
CALL DB$Display ;Display return code
|
||||
DB DB$A
|
||||
DB 'Test Read returned',0
|
||||
|
||||
CALL Check$Ripple ;Check if ripple pattern in buffer
|
||||
JZ Testbed$Loop ;Yes, it is correct
|
||||
|
||||
CALL DB$MSGI ;Indicate problem
|
||||
DB DB$HL ;Display HL (points to offending byte)
|
||||
DB 'Ripple pattern incorrect. HL -> failure.',0
|
||||
|
||||
CALL DB$Display ;Display test buffer
|
||||
CALL DB$M ;Memory
|
||||
DW Test$Buffer
|
||||
DW Test$Buffer$Size
|
||||
DB 'Contents of Test$Buffer',0
|
||||
|
||||
JMP Testbed$Loop
|
||||
|
||||
Test$Write:
|
||||
CALL Fill$Ripple ;Fill the test buffer with ripple pattern
|
||||
CALL Write$No$Deblock;*** or Write$Physical depending on which
|
||||
;*** drivers you are testing.
|
||||
|
||||
CALL DB$Display ;Display return code
|
||||
DB DB$A
|
||||
DB 'Test Write returned',0
|
||||
|
||||
JMP Testbed$Loop
|
||||
|
||||
Fill$Ripple: ;Fills the Test$Buffer with a pattern
|
||||
; formed by putting into each byte, the
|
||||
; least significant 8-bits of the byte's
|
||||
; address.
|
||||
LXI B,Test$Buffer$Size
|
||||
LXI H,Test$Buffer
|
||||
FR$Loop:
|
||||
MOV M,L ;Set pattern value into buffer
|
||||
INX H ;Update buffer pointer
|
||||
DCX B ;Down date count
|
||||
MOV A,C ;Check if count zero
|
||||
ORA B
|
||||
JNZ FR$Loop ;Repeat until zero
|
||||
RET
|
||||
;
|
||||
Check$Ripple: ;Check that the buffer is filled with the
|
||||
; correct ripple pattern.
|
||||
; Returns with Zero status if this is true,
|
||||
; non-zero status if the ripple is not
|
||||
; correct. HL point to the offending byte
|
||||
; (which should = L)
|
||||
LXI B,Test$Buffer$Size
|
||||
LXI H,Test$Buffer
|
||||
CR$Loop:
|
||||
MOV A,L ;Get correct value
|
||||
CMP M ;Compare to that in the buffer
|
||||
RNZ ;Mismatch - Non-zero already indicated
|
||||
INX H ;Update buffer pointer
|
||||
DCX B ;Down date count
|
||||
MOV A,C ;Check count zero
|
||||
ORA B
|
||||
JNZ CR$Loop ;Repeat until zero
|
||||
RET ;Zero flag will already be set
|
||||
;
|
||||
; Testbed variables
|
||||
;
|
||||
Logical$Disk: DB 0 ;A = 0, B = 1,...
|
||||
Track: DW 0 ;Disk track number
|
||||
Sector: DB 0 ;Disk sector number
|
||||
Write$Disk: DB 0 ;NZ to write to disk
|
||||
;
|
||||
Test$Buffer$Size EQU 512 ;<=== Alter as required
|
||||
Test$Buffer: DS Test$Buffer$Size
|
||||
;
|
||||
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
|
||||
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
|
||||
DW 9999H,9999H,9999H,9999H,9999H,9999H,9999H,9999H
|
||||
Test$Stack:
|
||||
;
|
||||
; Dummy routines for those shown in other figures
|
||||
;
|
||||
; BIOS routines (Figure 8-10)
|
||||
;
|
||||
SELDSK: ;Select logical disk
|
||||
SETTRK: ;Set track number
|
||||
SETSEC: ;Set sector number
|
||||
SETDMA: ;Set DMA address
|
||||
Read$No$Deblock: ;Driver read routines
|
||||
Read$Physical:
|
||||
Write$No$Deblock: ;Driver write routines
|
||||
Write$Physical:
|
||||
|
||||
;
|
||||
; Debug routines (Figure 10-2)
|
||||
;
|
||||
DB$Init: ;Debug initialization
|
||||
DB$MSGI: ;Display message in-line
|
||||
DB$Display: ;Main debug display routine
|
||||
DB$A EQU 02 ;Display codes for DB$Display
|
||||
DB$HL EQU 20
|
||||
DB$M EQU 24
|
||||
|
||||
40
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-10.ASM
Normal file
40
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-10.ASM
Normal file
@@ -0,0 +1,40 @@
|
||||
; Figure 5-10
|
||||
;
|
||||
; IOBYTE Equates
|
||||
; These are for accessing the IOBYTE.
|
||||
;
|
||||
; Mask Values to isolate specific devices.
|
||||
; (These can also be inverted to preserve all BUT the
|
||||
; specific device)
|
||||
;
|
||||
IO$CONM EQU 0000$0011B ;Console Mask
|
||||
IO$RDRM EQU 0000$1100B ;Reader Mask
|
||||
IO$PUNM EQU 0011$0000B ;Punch Mask
|
||||
IO$LSTM EQU 1100$0000B ;List Mask
|
||||
;
|
||||
;Console Values
|
||||
IO$CTTY EQU 0 ;Console -> TTY:
|
||||
IO$CCRT EQU 1 ;Console -> CRT:
|
||||
IO$CBAT EQU 2 ;Console Input <- RDR:
|
||||
;Console Output -> LST:
|
||||
IO$CUC1 EQU 3 ;Console -> UC1: (User console 1)
|
||||
;
|
||||
;Reader Values
|
||||
IO$RTTY EQU 0 SHL 2 ;Reader <- TTY:
|
||||
IO$RRDR EQU 1 SHL 2 ;Reader <- RDR:
|
||||
IO$RUR1 EQU 2 SHL 2 ;Reader <- UR1: (User Reader 1)
|
||||
IO$RUR2 EQU 3 SHL 2 ;Reader <- UR2: (user Reader 2)
|
||||
;
|
||||
;Punch Values
|
||||
IO$PTTY EQU 0 SHL 4 ;Punch -> TTY:
|
||||
IO$PPUN EQU 1 SHL 4 ;Punch -> PUN:
|
||||
IO$PUP1 EQU 2 SHL 4 ;Punch -> UP1: (User Punch 1)
|
||||
IO$PUP2 EQU 3 SHL 4 ;Punch -> UP2: (User Punch 2)
|
||||
;
|
||||
;List Values
|
||||
IO$LTTY EQU 0 SHL 6 ;List -> TTY:
|
||||
IO$LCRT EQU 1 SHL 6 ;List -> CRT:
|
||||
IO$LLPT EQU 2 SHL 6 ;List -> LPT: (Physical Line Printer)
|
||||
IO$LUL1 EQU 3 SHL 6 ;List -> UL1: (User List 1)
|
||||
;
|
||||
|
||||
75
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-11.ASM
Normal file
75
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-11.ASM
Normal file
@@ -0,0 +1,75 @@
|
||||
; Figure 5-11
|
||||
;
|
||||
; This example shows how to use the Get and Set IOBYTE
|
||||
; functions to implement a simple Terminal emulator.
|
||||
; For this example to work, the BIOS must detect the
|
||||
; Console Value being set to 3 (IO$CUC1) and connect
|
||||
; Console Status, Input and Output functions to the
|
||||
; Communications line.
|
||||
;
|
||||
B$DIRCONIO EQU 6 ;Direct Console Input/Output
|
||||
B$GETIO EQU 7 ;Get IOBYTE
|
||||
B$SETIO EQU 8 ;Set IOBYTE
|
||||
B$CONST EQU 11 ;Get Console Status (sneak preview)
|
||||
BDOS EQU 5 ;BDOS entry point
|
||||
;
|
||||
IO$CONM EQU 0000$0011B ;Console Mask for IOBYTE
|
||||
IO$CCRT EQU 1 ;Console -> CRT:
|
||||
IO$CUC1 EQU 3 ;Console -> User Console #1
|
||||
;
|
||||
TERM:
|
||||
CALL SETCRT ;Connect Console -> CRT:
|
||||
TERM$CKS:
|
||||
CALL CONST ;Get CRT status
|
||||
JZ TERM$NOKI ;No console input
|
||||
CALL CONIN ;Get keyboard character
|
||||
CALL SETCOMM ;Connect Console -> Comm. Line
|
||||
CALL CONOUT ;Output to Comm. Line
|
||||
TERM$CCS: ;Check Comm. Status
|
||||
CALL CONST ;Get "Console" status
|
||||
JZ TERM ;No incoming Comm. character
|
||||
CALL CONIN ;Get incoming Comm. character
|
||||
CALL SETCRT ;Connect Console -> CRT:
|
||||
CALL CONOUT ;Output to CRT
|
||||
JMP TERM$CKS ;Loop back to check keyboard status
|
||||
TERM$NOKI:
|
||||
CALL SETCOMM ;Connect Console -> Comm. Line
|
||||
JMP TERM$CCS ;Loop back to check Comm. status
|
||||
;
|
||||
SETCRT: ;Connect Console -> CRT:
|
||||
PUSH PSW ;Save possible data character
|
||||
MVI B,IO$CCRT ;Connect Console -> CRT:
|
||||
JMP SETCON ;Common Code
|
||||
|
||||
SETCOMM: ;Connect Console -> Comm. Line
|
||||
PUSH PSW ;Save possible data character
|
||||
MVI B,IO$CUC1 ;Connect Console -> Comm. Line
|
||||
;Drop into SETCON
|
||||
|
||||
SETCON: ;Set Console Device
|
||||
;New code in B (in bits 1,0)
|
||||
PUSH B ;Save code
|
||||
MVI C,B$GETIO ;Get current IOBYTE
|
||||
CALL BDOS
|
||||
ANI (NOT IO$CONM) AND 0FFH ;Preserve all but console
|
||||
POP B ;Recover required code
|
||||
ORA B ;OR in new bits
|
||||
MOV E,A ;Ready for setting
|
||||
MVI C,B$SETIO ;Function code
|
||||
CALL BDOS
|
||||
POP PSW ;Recover possible data character
|
||||
RET
|
||||
CONOUT:
|
||||
MOV E,A ;Get data byte for output
|
||||
MVI C,B$DIRCONIO ;Function code
|
||||
JMP BDOS ;BDOS returns to CONOUT's caller
|
||||
CONIN:
|
||||
MVI C,B$DIRCONIO ;Function Code
|
||||
MVI E,0FFH ;Indicate Console Input
|
||||
JMP BDOS ;BDOS returns to CONIN's caller
|
||||
CONST:
|
||||
MVI C,B$CONST ;Function Code
|
||||
CALL BDOS
|
||||
ORA A ;Set Z-flag to result
|
||||
RET
|
||||
|
||||
79
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-12.ASM
Normal file
79
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-12.ASM
Normal file
@@ -0,0 +1,79 @@
|
||||
; Figure 5-12
|
||||
;
|
||||
; OM
|
||||
; Output Message
|
||||
; This subroutine selects one of several messages based on
|
||||
; the contents of the A register on entry. It then displays
|
||||
; this message on the console.
|
||||
;
|
||||
; Each message is declared with a '$' as its last character.
|
||||
; If the A register contains a value larger than the number
|
||||
; of messages declared, OM will output "Unknown Message".
|
||||
;
|
||||
; As an option, OM can output Carriage Return / Line Feed
|
||||
; prior to outputting the message text.
|
||||
;
|
||||
; Entry Parameters
|
||||
;
|
||||
; HL -> Message Table
|
||||
; This has the form :
|
||||
; DB 3 ;Number of Messages in table
|
||||
; DW MSG0 ;Address of text (A = 0)
|
||||
; DW MSG1 ;(A = 1)
|
||||
; DW MSG2 ;(A = 2)
|
||||
;
|
||||
; MSG0: DB 'Message text$'
|
||||
; ...etc.
|
||||
;
|
||||
; A = Message Code (from 0 on up)
|
||||
; B = Output CR/LF if Non-zero
|
||||
;
|
||||
; Calling sequence
|
||||
;
|
||||
; LXI H,MSG$TABLE
|
||||
; LDA MSGCODE
|
||||
; MVI B,0 ;Suppress CR/LF
|
||||
; CALL OM
|
||||
;
|
||||
B$PRINTS EQU 9 ;Print $-terminated string
|
||||
BDOS EQU 5 ;BDOS Entry Point
|
||||
;
|
||||
CR EQU 0DH ;Carriage Return
|
||||
LF EQU 0AH ;Line Feed
|
||||
;
|
||||
OM$CRLF: DB CR,LF,'$'
|
||||
OM$UM: DB 'Unknown Message$'
|
||||
;
|
||||
;
|
||||
OM:
|
||||
PUSH PSW ;Save message code
|
||||
PUSH H ;Save message table pointer
|
||||
MOV A,B ;Check if CR/LF required
|
||||
ORA A
|
||||
JZ OM$NOCR ;No
|
||||
LXI D,OM$CRLF ;Output CR/LF
|
||||
MVI C,B$PRINTS
|
||||
CALL BDOS
|
||||
OM$NOCR:
|
||||
POP H ;Recover message table pointer
|
||||
POP PSW ;Recover message code
|
||||
CMP M ;Compare message to max. value
|
||||
JNC OM$ERR ;Error - code not <= max.
|
||||
INX H ;Bypass max. value in table
|
||||
ADD A ;Message Code * 2
|
||||
MOV E,A ;Make Code * 2 a word value
|
||||
MVI D,0
|
||||
DAD D ;HL -> Address of message text
|
||||
MOV E,M ;Get LS Byte
|
||||
INX H ;HL -> MS Byte
|
||||
MOV D,M ;Get MS Byte
|
||||
;DE -> Message text itself
|
||||
OM$PS: ;Print string entry point
|
||||
MVI C,B$PRINTS ;Function Code
|
||||
CALL BDOS
|
||||
RET ;Return to caller
|
||||
;
|
||||
OM$ERR: ;Error
|
||||
LXI D,OM$UM ;Point to "Unknown Message"
|
||||
JMP OM$PS ;Print string
|
||||
|
||||
159
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-13.ASM
Normal file
159
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-13.ASM
Normal file
@@ -0,0 +1,159 @@
|
||||
; Figure 5-13
|
||||
;
|
||||
; RSA
|
||||
; Return Subprocessor Address
|
||||
; This subroutine returns one of several addresses selected
|
||||
; from a table by matching keyboard input against
|
||||
; specified strings. It is normally used to switch control
|
||||
; to a particular subprocessor according to an option entered
|
||||
; by the operator from the keyboard.
|
||||
;
|
||||
; Character string comparisons are performed with case-folding,
|
||||
; that is lower-case letters are converted to upper-case.
|
||||
;
|
||||
; If the operator input fails to match any of the specified
|
||||
; strings, then the Carry flag is set, otherwise it is cleared.
|
||||
;
|
||||
; Entry Parameters
|
||||
;
|
||||
; HL -> Subprocessor Select Table
|
||||
; This has the form :
|
||||
; DW TEXT0,SUBPROC0
|
||||
; DW TEXT1,SUBPROC1
|
||||
; DW 0 ;Terminator
|
||||
;
|
||||
; TEXT0: DB 'add',0 ;00H-byte terminated
|
||||
; TEXT1: DB 'subtract',0
|
||||
;
|
||||
; SUBPROC0:
|
||||
; Code for processing ADD function.
|
||||
; SUBPROC1:
|
||||
; Code for processing SUBTRACT function.
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; DE -> operator input string (00H-terminated input string).
|
||||
; Carry Clear, HL -> Subprocessor.
|
||||
; Carry Set, HL = 0000H.
|
||||
;
|
||||
; Calling Sequence
|
||||
;
|
||||
; LXI H,SUBPROCTAB ;Subprocessor Table
|
||||
; CALL RSA
|
||||
; JC ERROR ;Carry set only on error
|
||||
; LXI D,RETURN ;Fake CALL instruction
|
||||
; PUSH D ;Push Return address on stack
|
||||
; PCHL ;"CALL" to Subprocessor
|
||||
; RETURN:
|
||||
;
|
||||
B$READCONS EQU 10 ;Read Console String into Buffer
|
||||
BDOS EQU 5 ;BDOS Entry Point
|
||||
;
|
||||
RSA$BL EQU 80 ;Buffer Length
|
||||
RSA$BUF: DB RSA$BL ;Max. no. of characters
|
||||
RSA$ACTC: DB 0 ;Actual no. of characters
|
||||
RSA$BUFC: DS RSA$BL ;Buffer characters
|
||||
DB 0 ;Safety terminator
|
||||
;
|
||||
;
|
||||
RSA:
|
||||
DCX H ;Adjust Subproc. pointer for code below
|
||||
DCX H
|
||||
PUSH H ;Top of Stack (TOS) -> Subproc. table - 2
|
||||
MVI C,B$READCONS ;Function Code
|
||||
LXI D,RSA$BUF ;DE -> Buffer
|
||||
CALL BDOS ;Read operator input
|
||||
;Convert input to 00H-terminated
|
||||
LXI H,RSA$ACTC ;HL -> Actual no. of chars input
|
||||
MOV E,M ;Get Actual no. of chars input
|
||||
MVI D,0 ;Make into word value
|
||||
INX H ;HL -> first data character
|
||||
DAD D ;HL -> first UNUSED character in buffer
|
||||
MVI M,0 ;Make input buffer 00H-terminated
|
||||
;Compare input to specified values
|
||||
;Main loop
|
||||
RSA$ML:
|
||||
POP H ;Recover subprocessor table pointer
|
||||
INX H ;Move to top of next entry
|
||||
INX H ;HL -> Text Address
|
||||
MOV E,M ;Get text address
|
||||
INX H
|
||||
MOV D,M ;DE -> text
|
||||
|
||||
MOV A,D ;Check if at end of subproc. table
|
||||
ORA E
|
||||
JZ RSA$NFND ;Match not Found
|
||||
|
||||
INX H ;HL -> subprocessor address
|
||||
PUSH H ;Save ptr to subprocessor table
|
||||
LXI H,RSA$BUFC ;HL -> input characters
|
||||
CALL FSCMP ;Folded string compare
|
||||
JNZ RSA$ML ;No match, move to next entry
|
||||
POP H ;Match found, recover subproc. ptr
|
||||
MOV E,M ;Get actual subprocessor address
|
||||
INX H
|
||||
MOV D,M ;DE -> Subprocessor code
|
||||
XCHG ;HL -> Subprocessor code
|
||||
ORA A ;Clear carry (match found)
|
||||
RET
|
||||
;
|
||||
RSA$NFND:
|
||||
LXI H,0 ;Indicate no match found
|
||||
STC ;Set carry
|
||||
RET
|
||||
;
|
||||
;
|
||||
; FSCMP
|
||||
; Folded (lower case to upper) string compare.
|
||||
; This subroutine compares two 00H-byte terminated
|
||||
; strings and returns with the condition flags set
|
||||
; to indicate their relationship.
|
||||
;
|
||||
; Entry parameters
|
||||
;
|
||||
; DE -> String 1
|
||||
; HL -> String 2
|
||||
;
|
||||
; Exit parameters
|
||||
;
|
||||
; Flags set (based on String1 - String2, on a
|
||||
; character-by-character basis)
|
||||
;
|
||||
;
|
||||
FSCMP:
|
||||
LDAX D ;Get String 1 character
|
||||
CALL FOLD ;Fold to upper case
|
||||
PUSH PSW ;Save String 1 character
|
||||
MOV A,M ;Get String 2 character
|
||||
CALL FOLD ;Fold to upper case
|
||||
MOV B,A ;Save String 2 character
|
||||
POP PSW ;Recover String 1 character
|
||||
CMP B ;String 1 - String 2
|
||||
RNZ ;Return if not equal
|
||||
ORA A ;Equal, so check if end of strings
|
||||
RZ ;Yes
|
||||
INX D ;No, update String 1 pointer
|
||||
INX H ; and String 2 pointer
|
||||
JMP FSCMP ;Check next character
|
||||
;
|
||||
;
|
||||
; FOLD
|
||||
; Folds a lower case letter (a-z) to UPPER CASE (A-Z)
|
||||
;
|
||||
; The character to be folded is in A on entry and on exit.
|
||||
;
|
||||
FOLD:
|
||||
MOV C,A ;Preserve input character
|
||||
MVI A,'a'-1 ;Check if folding needed
|
||||
CMP C ;Compare to input character
|
||||
JNC FOLDX ;No, char. is <= 'a'
|
||||
MVI A,'z' ;Check if < 'z'
|
||||
CMP C
|
||||
JC FOLDX ;No, char. is > 'z'
|
||||
MVI A,0DFH ;Fold character
|
||||
ANA C
|
||||
RET
|
||||
FOLDX:
|
||||
MOV A,C ;Recover original input char.
|
||||
RET
|
||||
|
||||
46
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-14.ASM
Normal file
46
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-14.ASM
Normal file
@@ -0,0 +1,46 @@
|
||||
; Figure 5-14
|
||||
;
|
||||
; CCPM
|
||||
; Check if CP/M
|
||||
; This subroutine determines the version number of the
|
||||
; Operating System and, if not CP/M version 2, displays
|
||||
; an error message and executes a warm boot.
|
||||
;
|
||||
; Entry and Exit Parameters
|
||||
;
|
||||
; None
|
||||
;
|
||||
; Calling sequence
|
||||
;
|
||||
; CALL CCPM ;Warm boots if not CP/M 2
|
||||
;
|
||||
B$PRINTS EQU 9 ;Display $-terminated string
|
||||
B$GETVER EQU 12 ;Get version number
|
||||
BDOS EQU 5 ;BDOS Entry Point
|
||||
;
|
||||
CR EQU 0DH ;Carriage Return
|
||||
LF EQU 0AH ;Line Feed
|
||||
;
|
||||
CCPMM: DB CR,LF
|
||||
DB 'This program can only run under CP/M version 2.'
|
||||
DB CR,LF,'$'
|
||||
;
|
||||
;
|
||||
CCPM:
|
||||
MVI C,B$GETVER ;Get version number
|
||||
CALL BDOS
|
||||
MOV A,H ;H must be 0 for CP/M
|
||||
ORA A
|
||||
JNZ CCPME ;Must be MP/M
|
||||
MOV A,L ;L = version number of CP/M
|
||||
ANI 0F0H ;Version number in MS Nibble
|
||||
CPI 20H ;Check if version 2
|
||||
JNZ CCPME ;Must be an earlier version
|
||||
RET ;Yes, we under CP/M version 2
|
||||
;
|
||||
CCPME: ;Error
|
||||
MVI C,B$PRINTS ;Display error message
|
||||
LXI D,CCPMM
|
||||
CALL BDOS
|
||||
JMP 0 ;Warm Boot
|
||||
|
||||
50
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-15.ASM
Normal file
50
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-15.ASM
Normal file
@@ -0,0 +1,50 @@
|
||||
; Figure 5-15
|
||||
;
|
||||
; CDISK
|
||||
; Change Disk
|
||||
; This subroutine displays a message requesting the
|
||||
; to change the specified logical disk, then waits for
|
||||
; a Carriage Return to be pressed. It then issues
|
||||
; a Disk Reset and returns to the caller.
|
||||
;
|
||||
; Entry Parameters
|
||||
;
|
||||
; A = Logical disk to be changed (A = 0, B = 1)
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; None
|
||||
;
|
||||
; Calling Sequence
|
||||
;
|
||||
; MVI A,0 ;Change drive A:
|
||||
; CALL CDISK
|
||||
;
|
||||
;
|
||||
B$DSKRESET EQU 13 ;Disk Reset function code
|
||||
B$PRINTS EQU 9 ;Print $-terminated string
|
||||
B$CONIN EQU 1 ;Get console input
|
||||
BDOS EQU 5 ;BDOS Entry Point
|
||||
;
|
||||
CR EQU 0DH
|
||||
LF EQU 0AH
|
||||
;
|
||||
CDISKM: DB CR,LF,'Change logical disk '
|
||||
CDISKD: DB 0
|
||||
DB ': and press Carriage Return to continue$'
|
||||
;
|
||||
CDISK:
|
||||
ADI 'A'-1 ;Convert to Letter
|
||||
STA CDISKD ;Store into message
|
||||
MVI C,B$PRINTS ;Display Message
|
||||
LXI D,CDISKM
|
||||
CALL BDOS
|
||||
CDISKW:
|
||||
MVI C,B$CONIN ;Get keyboard character
|
||||
CALL BDOS
|
||||
CPI CR
|
||||
JNZ CDISKW
|
||||
MVI C,B$DSKRESET ;Now reset disk system
|
||||
CALL BDOS
|
||||
RET
|
||||
|
||||
176
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-16.ASM
Normal file
176
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-16.ASM
Normal file
@@ -0,0 +1,176 @@
|
||||
; Figure 5-16
|
||||
;
|
||||
; OPENF
|
||||
; Open File
|
||||
;
|
||||
; Given a pointer to a 00H-byte terminated file name,
|
||||
; and an area that can be used for a File Control Block,
|
||||
; this subroutine builds a valid File Control Block, and
|
||||
; attempts to open the File.
|
||||
;
|
||||
; If the file is opened, it returns with Carry Flag clear.
|
||||
; If the file cannot be opened, this subroutine returns
|
||||
; with the Carry Flag set.
|
||||
;
|
||||
; Entry Parameters
|
||||
;
|
||||
; DE -> 36-byte area for File Control Block
|
||||
; HL -> 00H-byte terminated file name of the form
|
||||
; {disk:} Name {.typ}
|
||||
; (That is, the disk and typ are optional)
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; Carry Clear : File opened correctly.
|
||||
; Carry Set : File not opened.
|
||||
;
|
||||
; Calling Sequence
|
||||
;
|
||||
; LXI D,FCB
|
||||
; LXI H,FNAME
|
||||
; CALL OPENF
|
||||
; JC ERROR
|
||||
;
|
||||
; where
|
||||
;
|
||||
; FCB: DS 36 ;Space for File Control Block
|
||||
; FNAME: DB 'A:TESTFILE.DAT',0
|
||||
;
|
||||
;
|
||||
B$OPEN EQU 15 ;File Open Function Code
|
||||
BDOS EQU 5 ;BDOS Entry Point
|
||||
;
|
||||
;
|
||||
OPENF:
|
||||
PUSH D ;Preserve pointer to FCB
|
||||
CALL BF ;Build File Control Block (FCB)
|
||||
MVI C,B$OPEN
|
||||
POP D ;Recover Pointer to FCB
|
||||
CALL BDOS
|
||||
RAL ;If A=0FFH, Carry set
|
||||
;otherwise Carry Clear
|
||||
RET
|
||||
;
|
||||
; BF
|
||||
; Build File Control Block
|
||||
; This subroutine formats a 00H-byte terminated string
|
||||
; (presumed to be a File Name) into an FCB, setting the
|
||||
; Disk, File name and Type and clearing the remainder of the
|
||||
; FCB to 0's.
|
||||
;
|
||||
; Entry Parameters
|
||||
;
|
||||
; DE -> File Control Block (36 Bytes)
|
||||
; HL -> File Name String (00H-byte terminated)
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; The built File Control block.
|
||||
;
|
||||
; Calling Sequence
|
||||
;
|
||||
; LXI D,FCB
|
||||
; LXI H,FILENAME
|
||||
; CALL BF
|
||||
;
|
||||
;
|
||||
BF:
|
||||
INX H ;Check if 2nd char. is ":"
|
||||
MOV A,M ;Get character from file name
|
||||
DCX H ;HL -> now back at 1st char
|
||||
CPI ':' ;If ":", then Disk specified
|
||||
JNZ BF$ND ;No disk
|
||||
MOV A,M ;Get disk letter
|
||||
ANI 0001$1111B ;A (41H) -> 1, B (42H) -> 2 ...
|
||||
INX H ;Bypass disk letter
|
||||
INX H ;Bypass ":"
|
||||
JMP BF$SD ;Store disk in FCB
|
||||
BF$ND: ;No disk present
|
||||
XRA A ;Indicate default disk
|
||||
BF$SD:
|
||||
STAX D ;Store disk in FCB
|
||||
INX D ;DE -> 1st char. of name in FCB
|
||||
MVI C,8 ;File name length
|
||||
CALL BF$GT ;Get token
|
||||
;Note - at this point, BF$GT will
|
||||
;have advanced the string pointer to
|
||||
;either a '.' or 00H byte
|
||||
CPI '.' ;Check terminating character
|
||||
JNZ BF$NT ;No file type specified
|
||||
INX H ;Bypass '.' in file name
|
||||
BF$NT:
|
||||
MVI C,3 ;File Type Length
|
||||
CALL BF$GT ;Get Token
|
||||
;Note - if no file type is present
|
||||
;BF$GT will merely spacefill the FCB
|
||||
MVI B,0 ;0-fill the remainder of the FCB
|
||||
MVI C,24 ;36 - 12 (Disk, Name, Type = 12 chars.)
|
||||
CALL BF$FT ;Re-use Fill Token S/R
|
||||
RET
|
||||
;
|
||||
; BF$GT
|
||||
; Build FCB - Get Token
|
||||
;
|
||||
; This subroutine scans a file name string,
|
||||
; placing characters into a File Control Block.
|
||||
; On encountering a terminator character ("." or 00H),
|
||||
; the remainder of the token is space filled.
|
||||
; If an "*" is encountered, the remainder of the token
|
||||
; is filled with "?".
|
||||
;
|
||||
; Entry Parameters
|
||||
;
|
||||
; DE -> Into File Control Block
|
||||
; HL -> Into File Name String
|
||||
; C = Maximum no. of characters in token
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; File Control Block contains next token.
|
||||
; A = Terminating Character.
|
||||
BF$GT:
|
||||
MOV A,M ;Get next string character
|
||||
ORA A ;Check if end of string
|
||||
JZ BF$SFT ;Yes, space fill token
|
||||
CPI '*' ;Check if ?-fill required
|
||||
JZ BF$QFT ;Yes, fill with ?
|
||||
CPI '.' ;Assume current token is Filename
|
||||
;check if file type coming up
|
||||
;(If current token is File type this
|
||||
;check is benignly redundant)
|
||||
JZ BF$SFT ;Yes, space fill token
|
||||
STAX D ;None of the above, so store in FCB
|
||||
INX D ;Update FCB Pointer
|
||||
INX H ;Update String Pointer
|
||||
DCR C ;Countdown on token length
|
||||
JNZ BF$GT ;Still more characters to go
|
||||
BF$SKIP: ;Skip chars. until '.' or 00H
|
||||
MOV A,M ;Get next string character
|
||||
ORA A ;Check if 00H
|
||||
RZ ;Yes
|
||||
CPI '.' ;Check if '.'
|
||||
RZ ;Yes
|
||||
INX H ;Update string pointer (only)
|
||||
JMP BF$SKIP ;Try next character
|
||||
|
||||
BF$SFT: ;Space fill token
|
||||
MVI B,' '
|
||||
JMP BF$FT ;Common Fill Token code
|
||||
;BF$FT returns to caller
|
||||
|
||||
BF$QFT: ;Question Mark Fill Token
|
||||
MVI B,'?'
|
||||
CALL BF$FT ;Common Fill Token Code
|
||||
JMP BF$SKIP ;Bypass multiple '*' etc.
|
||||
|
||||
BF$FT: ;Fill Token
|
||||
PUSH PSW ;Save terminating character
|
||||
MOV A,B ;Get fill characer
|
||||
BF$FTL: ;Inner loop
|
||||
STAX D ;Store in FCB
|
||||
INX D ;Update FCB Pointer
|
||||
DCR C ;Downdate residual count
|
||||
JNZ BF$FTL ;Keep going
|
||||
POP PSW ;Recover terminating character
|
||||
RET
|
||||
|
||||
189
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-17.ASM
Normal file
189
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-17.ASM
Normal file
@@ -0,0 +1,189 @@
|
||||
; Figure 5-17
|
||||
;
|
||||
; GNF
|
||||
; This subroutine returns an FCB setup with either the first
|
||||
; file matched by an ambiguous file name, or (if specified
|
||||
; by entry parameter) the next file name.
|
||||
;
|
||||
; Note : this subroutine is context sensitive. You must not
|
||||
; have more than one ambiguous file name sequence in
|
||||
; process at any given time.
|
||||
;
|
||||
;>>> Warning : This subroutine changes the DMA Address inside
|
||||
;>>> the BDOS.
|
||||
;
|
||||
; Entry Parameters
|
||||
;
|
||||
; DE -> Possibly Ambiguous File Name
|
||||
; (00-byte terminated)
|
||||
; (Only needed for FIRST request)
|
||||
; HL -> File Control Block
|
||||
; A = 0 : Return FIRST file name that matches
|
||||
; = NZ : Return NEXT file name that matches
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; Carry set : A = FF, no file name matches
|
||||
; A not = 0FFH, error in input file name
|
||||
; Carry clear : FCB setup with next name
|
||||
; HL -> Directory Entry returned by
|
||||
; Search First/Next
|
||||
;
|
||||
; Calling sequence
|
||||
;
|
||||
; LXI D,FILENAME
|
||||
; LXI H,FCB
|
||||
; MVI A,0 ;or MVI A,1 for NEXT
|
||||
; CALL GNF
|
||||
;
|
||||
B$SEARCHF EQU 17 ;Search for First file name
|
||||
B$SEARCHN EQU 18 ;Search for Next file name
|
||||
B$SETDMA EQU 26 ;Setup DMA Address
|
||||
BDOS EQU 5 ;BDOS Entry Point
|
||||
;
|
||||
GNFDMA EQU 80H ;Default DMA Address
|
||||
GNFSVL EQU 13 ;Save Length (no. of chars to move)
|
||||
GNFFCL EQU 36 ;File Control Block length
|
||||
GNFSV: DS GNFSVL ;Save area for file name/type
|
||||
;
|
||||
GNF:
|
||||
PUSH H ;Save FCB pointer
|
||||
PUSH D ;Save File Name pointer
|
||||
PUSH PSW ;Save First/Next flag
|
||||
|
||||
LXI D,GNFDMA ;Set DMA to known address
|
||||
MVI C,B$SETDMA ;Function code
|
||||
CALL BDOS
|
||||
POP PSW ;Recover First/Next flag
|
||||
POP H ;Recover File Name pointer
|
||||
POP D ;Recover FCB Pointer
|
||||
PUSH D ;Re-save FCB Pointer
|
||||
|
||||
ORA A ;Check if FIRST or NEXT
|
||||
JNZ GNFN ;NEXT
|
||||
CALL BF ;Build File Control BLock
|
||||
POP H ;Recover FCB Pointer (to balance stack)
|
||||
RC ;Return if error in File Name
|
||||
PUSH H ;Resave FCB pointer
|
||||
|
||||
;Move Ambiguous File Name to save area
|
||||
;HL -> FCB
|
||||
LXI D,GNFSV ;DE -> save area
|
||||
MVI C,GNFSVL ;Get save length
|
||||
CALL MOVE
|
||||
POP D ;Recover FCB Pointer
|
||||
PUSH D ;and re-save
|
||||
|
||||
MVI C,B$SEARCHF ;Search FIRST
|
||||
CALL BDOS
|
||||
POP H ;Recover FCB Pointer
|
||||
CPI 0FFH ;Check for error
|
||||
JZ GNFEX ;Error exit
|
||||
JMP GNFC ;Common code
|
||||
;
|
||||
GNFN:
|
||||
;Execute search FIRST to re-establish
|
||||
;contact with previous file
|
||||
;User's FCB still has Name/Type in it
|
||||
CALL GNFZF ;Zero-fill all but File Name/Type
|
||||
POP D ;Recover FCB address
|
||||
PUSH D ;and re-save
|
||||
MVI C,B$SEARCHF ;Re-find the file
|
||||
CALL BDOS
|
||||
POP D ;Recover FCB Pointer
|
||||
PUSH D ;and re-save
|
||||
LXI H,GNFSV ;Move File Name from save area into FCB
|
||||
MVI C,GNFSVL ;Save area length
|
||||
CALL MOVE
|
||||
|
||||
MVI C,B$SEARCHN ;Search NEXT
|
||||
CALL BDOS
|
||||
POP H ;Recover FCB address
|
||||
CPI 0FFH ;Check for error
|
||||
JZ GNFEX ;Error exit
|
||||
GNFC:
|
||||
PUSH H ;Save FCB Address
|
||||
ADD A ;Multiply BDOS Return Code * 32
|
||||
ADD A ;* 4
|
||||
ADD A ;* 8
|
||||
ADD A ;* 16
|
||||
ADD A ;* 32
|
||||
LXI H,GNFDMA ;HL -> DMA Address
|
||||
MOV E,A ;Make Code * 32 a word value in DE
|
||||
MVI D,0
|
||||
DAD D ;HL -> File's directory entry
|
||||
|
||||
;Move File Name into FCB
|
||||
POP D ;Recover FCB Address
|
||||
PUSH H ;Save Directory Entry pointer
|
||||
PUSH D ;and re-save
|
||||
MVI C,GNFSVL ;Length of save area
|
||||
CALL MOVE
|
||||
LDA GNFSV ;Get Disk Drive from save area
|
||||
POP D ;Recover FCB Address
|
||||
STAX D ;Overwrite user number in FCB
|
||||
|
||||
;Setup to zero-fill tail end of FCB
|
||||
CALL GNFZF ;Zero-fill
|
||||
POP H ;Recover Directory Entry Pointer
|
||||
XRA A ;Clear Carry
|
||||
RET
|
||||
;
|
||||
GNFEX:
|
||||
STC ;Set Carry to indicate error
|
||||
RET
|
||||
|
||||
;
|
||||
; GNFZF
|
||||
; Get Next File - Zero Fill
|
||||
; This subroutine Zero-fills the rest of the bytes in an FCB
|
||||
; that follow the File Name and Type.
|
||||
;
|
||||
; Entry Parameters
|
||||
;
|
||||
; DE -> File Control Block
|
||||
;
|
||||
GNFZF:
|
||||
LXI H,GNFSVL ;Bypass area that holds file name
|
||||
DAD D ;HL -> FCB + GNFSVL
|
||||
MOV D,H ;DE -> FCB + GNFSVL
|
||||
MOV E,L
|
||||
INX D ;DE -> FCB + GNFSVL + 1
|
||||
MVI M,0 ;FCB + GNFSVL = 0
|
||||
MVI C,GNFFCL-GNFSVL ;Remainder of File Control Block
|
||||
; Drop into MOVE ;Spread 0's through remainder of FCB
|
||||
; v
|
||||
;
|
||||
; MOVE
|
||||
; This subroutine moves C bytes from (HL) to (DE).
|
||||
;
|
||||
MOVE:
|
||||
MOV A,M ;Get source byte
|
||||
STAX D ;Save destination byte
|
||||
INX D ;Increment destination pointer
|
||||
INX H ;Increment source pointer
|
||||
DCR C ;Downdate count
|
||||
JNZ MOVE ;Go back for more
|
||||
RET
|
||||
;
|
||||
; BF
|
||||
; Build File Control Block
|
||||
;
|
||||
; This subroutine formats a 00H-byte terminated string
|
||||
; (presumed to be a File Name) into an FCB, setting the
|
||||
; Disk, File name and Type and clearing the remainder of the
|
||||
; FCB to 0's.
|
||||
;
|
||||
; Entry Parameters
|
||||
;
|
||||
; DE -> File Control Block (36 Bytes)
|
||||
; HL -> File Name String (00H-byte terminated)
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; The built File Control block.
|
||||
;
|
||||
; This subroutine is shown in full in Figure 5.32
|
||||
;
|
||||
BF: RET ;Dummy subroutine for this example
|
||||
|
||||
73
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-18.ASM
Normal file
73
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-18.ASM
Normal file
@@ -0,0 +1,73 @@
|
||||
; Figure 5-18
|
||||
;
|
||||
; GETC
|
||||
; This subroutine gets the next character from a
|
||||
; Sequential disk file. It assumes that the file has
|
||||
; already been opened.
|
||||
;
|
||||
;>>> Note : this subroutine changes CP/M's DMA Address.
|
||||
;
|
||||
; Entry Parameters
|
||||
;
|
||||
; DE -> File Control Block.
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; A = next character from file.
|
||||
; (= 0FFH on physical End of File).
|
||||
; Note : 1AH is normal EOF character for
|
||||
; ASCII Files.
|
||||
;
|
||||
; Calling sequence
|
||||
;
|
||||
; LXI DE,FCB
|
||||
; CALL GETC
|
||||
; CPI 1AH
|
||||
; JZ EOFCHAR
|
||||
; CPI 0FFH
|
||||
; JZ ACTUALEOF
|
||||
;
|
||||
B$READSEQ EQU 20 ;Read Sequential
|
||||
B$SETDMA EQU 26 ;Set DMA Address
|
||||
BDOS EQU 5 ;BDOS Entry Point
|
||||
;
|
||||
GETCBS EQU 128 ;Buffer Size
|
||||
GETCBF: DS GETCBS ;Declare buffer
|
||||
GETCCC: DB 0 ;Char. count (initially 'empty')
|
||||
;
|
||||
GETC:
|
||||
LDA GETCCC ;Check if buffer is empty
|
||||
ORA A
|
||||
JZ GETCFB ;Yes, fill buffer
|
||||
GETCRE: ;Re-entry point after buffer filled
|
||||
DCR A ;No, downdate count
|
||||
STA GETCCC ;Save downdated count
|
||||
|
||||
MOV B,A ;Compute offset of next character
|
||||
MVI A,GETCBS-1 ;By subtracting
|
||||
SUB B ;(buffer size - downdated count)
|
||||
MOV E,A ;Make result into word value
|
||||
MVI D,0
|
||||
LXI H,GETCBF ;HL -> Base of Buffer
|
||||
DAD D ;HL -> Next character in buffer
|
||||
MOV A,M ;Get next character
|
||||
RET
|
||||
;
|
||||
GETCFB: ;Fill Buffer
|
||||
PUSH D ;Save FCB Pointer
|
||||
LXI D,GETCBF ;Set DMA Address to Buffer
|
||||
MVI C,B$SETDMA ;Function Code
|
||||
CALL BDOS
|
||||
POP D ;Recover FCB Pointer
|
||||
MVI C,B$READSEQ ;Read Sequential 'record' (sector)
|
||||
CALL BDOS
|
||||
ORA A ;Check if read unsuccessful (A = NZ)
|
||||
JNZ GETCX ;Yes
|
||||
MVI A,GETCBS ;Reset count
|
||||
STA GETCCC
|
||||
JMP GETCRE ;Re-enter subroutine
|
||||
;
|
||||
GETCX: ;Physical end of file
|
||||
MVI A,0FFH ;Indicate such
|
||||
RET
|
||||
|
||||
95
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-19.ASM
Normal file
95
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-19.ASM
Normal file
@@ -0,0 +1,95 @@
|
||||
; Figure 5-19
|
||||
;
|
||||
; PUTC
|
||||
; This subroutine either puts the next chararacter out
|
||||
; to a sequential file, writing out completed 'records'
|
||||
; (128-byte sectors) or, if requested to, will fill the
|
||||
; remainder of the current 'record' with 1AH's to indicate
|
||||
; End of File to CP/M.
|
||||
;
|
||||
; Entry Parameters
|
||||
;
|
||||
; DE -> File Control Block
|
||||
; B = 0, A = next data character to be output
|
||||
; B /= 0, fill the current 'record' with 1AH's
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; None.
|
||||
;
|
||||
; Calling sequence
|
||||
;
|
||||
; LXI D,FCB
|
||||
; MVI B,0 ;Not end of File
|
||||
; LDA CHAR
|
||||
; CALL PUTC
|
||||
; ..or
|
||||
; LXI D,FCB
|
||||
; MVI B,1 ;Indicate end of file
|
||||
; CALL PUTC
|
||||
;
|
||||
B$WRITESEQ EQU 21 ;Write Sequential
|
||||
B$SETDMA EQU 26 ;Set DMA Address
|
||||
BDOS EQU 5 ;BDOS Entry Point
|
||||
;
|
||||
PUTCBS EQU 128 ;Buffer Size
|
||||
PUTCBF: DS PUTCBS ;Declare buffer
|
||||
PUTCCC: DB 0 ;Char. count (initially 'empty')
|
||||
;
|
||||
PUTC:
|
||||
PUSH D ;Save FCB Address
|
||||
PUSH PSW ;Save data character
|
||||
MOV A,B ;Check if end of file requested
|
||||
ORA A
|
||||
JNZ PUTCEF ;Yes
|
||||
CALL PUTCGA ;No, get address of next free byte
|
||||
;HL -> next free byte
|
||||
;E = Current Char. count (as well as A)
|
||||
POP PSW ;Recover data character
|
||||
MOV M,A ;Save in buffer
|
||||
MOV A,E ;Get current character count
|
||||
INR A ;Update character count
|
||||
CPI PUTCBS ;Check if buffer full
|
||||
JZ PUTCWB ;Yes, write buffer
|
||||
STA PUTCCC ;No, save updated count
|
||||
POP D ;Dump FCB Address for return
|
||||
RET
|
||||
;
|
||||
PUTCEF: ;End of file
|
||||
POP PSW ;Dump data character
|
||||
CALL PUTCGA ;HL -> next free byte
|
||||
;A = Current character count
|
||||
PUTCCE: ;Copy EOF character
|
||||
CPI PUTCBS ;Check for end of buffer
|
||||
JZ PUTCWB ;Yes, write out the buffer
|
||||
MVI M,1AH ;No, store EOF in buffer
|
||||
INR A ;Update count
|
||||
INX H ;Update buffer pointer
|
||||
JMP PUTCCE ;Continue until end of buffer
|
||||
;
|
||||
PUTCWB: ;Write buffer
|
||||
XRA A ;Reset character count to 0
|
||||
STA PUTCCC
|
||||
LXI D,PUTCBF ;DE -> Buffer
|
||||
MVI C,B$SETDMA ;Set DMA Address -> Buffer
|
||||
CALL BDOS
|
||||
POP D ;Recover FCB Address
|
||||
MVI C,B$WRITESEQ ;Write Sequential Record
|
||||
CALL BDOS
|
||||
ORA A ;Check if error
|
||||
JNZ PUTCX ;Yes if A = NZ
|
||||
RET ;No, return to caller
|
||||
;
|
||||
PUTCX: ;Error exit
|
||||
MVI A,0FFH ;Indicate such
|
||||
RET
|
||||
;
|
||||
PUTCGA: ;Return with HL -> next free char.
|
||||
;and A = current char. count
|
||||
LDA PUTCCC ;Get current character count
|
||||
MOV E,A ;Make word value in DE
|
||||
MVI D,0
|
||||
LXI H,PUTCBF ;HL -> Base of buffer
|
||||
DAD D ;HL -> next free character
|
||||
RET
|
||||
|
||||
47
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-2.ASM
Normal file
47
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-2.ASM
Normal file
@@ -0,0 +1,47 @@
|
||||
; Figure 5-2
|
||||
;
|
||||
; EQUates for BDOS Function Code numbers.
|
||||
;
|
||||
B$SYSRESET EQU 0 ;System Reset
|
||||
B$CONIN EQU 1 ;Read Console Byte
|
||||
B$CONOUT EQU 2 ;Write Console Byte
|
||||
B$READIN EQU 3 ;Read "Reader" Byte
|
||||
B$PUNOUT EQU 4 ;Write "Punch" Byte
|
||||
B$LISTOUT EQU 5 ;Write Printer Byte
|
||||
B$DIRCONIO EQU 6 ;Direct Console I/O
|
||||
B$GETIO EQU 7 ;Get IOBYTE
|
||||
B$SETIO EQU 8 ;Set IOBYTE
|
||||
B$PRINTS EQU 9 ;Print String
|
||||
B$READCONS EQU 10 ;Read Console String
|
||||
B$CONST EQU 11 ;Read Console Status
|
||||
B$GETVER EQU 12 ;Get CP/M Version Number
|
||||
B$DSKRESET EQU 13 ;Disk System Reset
|
||||
B$SELDSK EQU 14 ;Select Disk
|
||||
B$OPEN EQU 15 ;Open File
|
||||
B$CLOSE EQU 16 ;Close File
|
||||
B$SEARCHF EQU 17 ;Search for First Name Match
|
||||
B$SEARCHN EQU 18 ;Search for Next Name Match
|
||||
B$ERASE EQU 19 ;Erase (delete) File
|
||||
B$READSEQ EQU 20 ;Read Sequential
|
||||
B$WRITESEQ EQU 21 ;Write Sequential
|
||||
B$CREATE EQU 22 ;Create File
|
||||
B$RENAME EQU 23 ;Rename File
|
||||
B$GETACTDSK EQU 24 ;Get Active (Logged-in) Disks
|
||||
B$GETCURDSK EQU 25 ;Get Current Default Disk
|
||||
B$SETDMA EQU 26 ;Set DMA (Read/Write) Address
|
||||
B$GETALVEC EQU 27 ;Get Allocation Vector Address
|
||||
B$SETDSKRO EQU 28 ;Set Disk to Read Only
|
||||
B$GETRODSKS EQU 29 ;Get Read Only Disks
|
||||
B$SETFAT EQU 30 ;Set File Attributes
|
||||
B$GETDPB EQU 31 ;Get Disk Parameter Block Address
|
||||
B$SETGETUN EQU 32 ;Set/Get User Number
|
||||
B$READRAN EQU 33 ;Read Random
|
||||
B$WRITERAN EQU 34 ;Write Random
|
||||
B$GETFSIZ EQU 35 ;Get File Size
|
||||
B$SETRANREC EQU 36 ;Set Random Record number
|
||||
B$RESETD EQU 37 ;Reset Drive
|
||||
B$WRITERANZ EQU 40 ;Write Random with Zero Fill
|
||||
;
|
||||
BDOS EQU 5 ;BDOS Entry Point
|
||||
|
||||
|
||||
38
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-20.ASM
Normal file
38
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-20.ASM
Normal file
@@ -0,0 +1,38 @@
|
||||
; Figure 5-20
|
||||
;
|
||||
; CF
|
||||
; Create File
|
||||
; This subroutine creates a file. It erases any previous
|
||||
; File before creating the new one.
|
||||
;
|
||||
; Entry Parameters
|
||||
;
|
||||
; DE -> File Control Block for new file
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; Carry Clear if operation successful (A = 0,1,2,3)
|
||||
; Carry Set if error (A = 0FFH)
|
||||
;
|
||||
; Calling Sequence
|
||||
;
|
||||
; LXI D,FCB
|
||||
; CALL CF
|
||||
; JC ERROR
|
||||
;
|
||||
B$ERASE EQU 19 ;Erase File
|
||||
B$CREATE EQU 22 ;Create File
|
||||
BDOS EQU 5 ;BDOS Entry Point
|
||||
;
|
||||
;
|
||||
CF:
|
||||
PUSH D ;Preserve FCB Pointer
|
||||
MVI C,B$ERASE ;Erase any existing file
|
||||
CALL BDOS
|
||||
POP D ;Recover FCB Pointer
|
||||
MVI C,B$CREATE ;Create (and open new file)
|
||||
CALL BDOS
|
||||
CPI 0FFH ;Carry set if OK, Clear if Error
|
||||
CMC ;Complete to use Carry set if Error
|
||||
RET
|
||||
|
||||
84
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-21.ASM
Normal file
84
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-21.ASM
Normal file
@@ -0,0 +1,84 @@
|
||||
; Figure 5-21
|
||||
;
|
||||
; RF
|
||||
; Rename File
|
||||
; This subroutine renames a file.
|
||||
; It uses the BF (Build FCB) subroutine shown in Figure 5.32
|
||||
;
|
||||
; Entry Parameters
|
||||
;
|
||||
; *** No case-folding of file names occurs ***
|
||||
; HL -> Old File Name (00-byte terminated)
|
||||
; DE -> New File Name (00-byte terminated)
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; Carry clear if operation successful (A = 0,1,2,3)
|
||||
; Carry set if error (A =0FFH)
|
||||
;
|
||||
; Calling sequence
|
||||
;
|
||||
; LXI H,OLDNAME ;HL -> Old Name
|
||||
; LXI D,NEWNAME ;DE -> New Name
|
||||
; CALL RF
|
||||
; JC ERROR
|
||||
;
|
||||
B$OPEN EQU 15 ;Open File
|
||||
B$RENAME EQU 23 ;Rename File
|
||||
BDOS EQU 5 ;BDOS Entry Point
|
||||
;
|
||||
RFFCB: DW 0,0,0,0,0,0,0,0,0 ;1 1/2 FCB's long
|
||||
DW 0,0,0,0,0,0,0,0,0
|
||||
DW 0,0,0,0,0,0,0,0,0
|
||||
;
|
||||
;
|
||||
RF:
|
||||
PUSH D ;Save New Name pointer
|
||||
LXI D,RFFCB ;Build Old Name FCB
|
||||
;HL already -> Old Name
|
||||
CALL BF
|
||||
|
||||
POP H ;Recover New Name pointer
|
||||
LXI D,RFFCB+16 ;Build new name in second part
|
||||
CALL BF ;of File Control Block
|
||||
|
||||
LXI D,RFFCB+16 ;Experimentally try
|
||||
MVI C,B$OPEN ;to Open the new File
|
||||
CALL BDOS ;to ensure it does not
|
||||
CPI 0FFH ;already exist
|
||||
MVI A,0FEH ;Assume error (flags unchanged)
|
||||
RC ;Carry set if A was 0,1,2,3
|
||||
|
||||
LXI D,RFFCB ;Rename the file
|
||||
MVI C,B$RENAME
|
||||
CALL BDOS
|
||||
CPI 0FFH ;Carry set if OK, clear if error
|
||||
CMC ;Invert to use Carry set if error
|
||||
RET
|
||||
;
|
||||
; BF
|
||||
; Build File Control Block
|
||||
; This subroutine formats a 00H-byte terminated string
|
||||
; (presumed to be a File Name) into an FCB, setting the
|
||||
; Disk, File name and Type and clearing the remainder of the
|
||||
; FCB to 0's.
|
||||
;
|
||||
; Entry Parameters
|
||||
;
|
||||
; DE -> File Control Block (36 Bytes)
|
||||
; HL -> File Name String (00H-byte terminated)
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; The built File Control block.
|
||||
;
|
||||
; Calling Sequence
|
||||
;
|
||||
; LXI D,FCB
|
||||
; LXI H,FILENAME
|
||||
; CALL BF
|
||||
;
|
||||
;
|
||||
BF:
|
||||
RET ;Dummy subroutine : see Figure 5.32.
|
||||
|
||||
79
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-22.ASM
Normal file
79
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-22.ASM
Normal file
@@ -0,0 +1,79 @@
|
||||
; Figure 5-22
|
||||
;
|
||||
; SFA
|
||||
; Set File Attributes
|
||||
; This subroutine takes a compressed bit map of all the
|
||||
; File Attribute bits, expands them out into an existing
|
||||
; File Control Block and then requests CP/M to set the
|
||||
; Attributes in the File Directory.
|
||||
;
|
||||
; Entry Parameters
|
||||
;
|
||||
; DE -> File Control Block
|
||||
; HL = Bit Map. Only the most significant 11 bits
|
||||
; are used, and correspond directly with the
|
||||
; possible attribute bytes.
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; Carry Clear if operation successful (A = 0,1,2,3)
|
||||
; Carry Set if error (A = 0FFH)
|
||||
;
|
||||
; Calling Sequence
|
||||
;
|
||||
; LXI D,FCB
|
||||
; LXI H,0000$0000$1100$0000B ;Bit Map
|
||||
; CALL SFA
|
||||
; JC ERROR
|
||||
;
|
||||
; File Attribute Equates
|
||||
;
|
||||
FA$F1 EQU 1000$0000$0000$0000B ;F1' )
|
||||
FA$F2 EQU 0100$0000$0000$0000B ;F2' ) Available for use
|
||||
FA$F3 EQU 0010$0000$0000$0000B ;F3' ) by Application Programs
|
||||
FA$F4 EQU 0001$0000$0000$0000B ;F4' )
|
||||
FA$F5 EQU 0000$1000$0000$0000B ;F5' ]
|
||||
FA$F6 EQU 0000$0100$0000$0000B ;F6' ] Reserved for CP/M
|
||||
FA$F7 EQU 0000$0010$0000$0000B ;F7' ]
|
||||
FA$F8 EQU 0000$0001$0000$0000B ;F8' ]
|
||||
;
|
||||
FA$T1 EQU 0000$0000$1000$0000B ;T1' - Read/Only File
|
||||
FA$RO EQU FA$T1
|
||||
FA$T2 EQU 0000$0000$0100$0000B ;T2' - System Files
|
||||
FA$SYS EQU FA$T2
|
||||
FA$T3 EQU 0000$0000$0010$0000B ;T3' - Reserved for CP/M
|
||||
;
|
||||
;
|
||||
B$SETFAT EQU 30 ;Set File Attributes
|
||||
BDOS EQU 5 ;BDOS Entry Point
|
||||
;
|
||||
;
|
||||
SFA:
|
||||
PUSH D ;Save FCB Pointer
|
||||
INX D ;HL -> 1st character of file name
|
||||
MVI C,8+3 ;Loop Count for File Name and Type
|
||||
SFAL: ;Main Processing Loop
|
||||
XRA A ;Clear Carry and A
|
||||
DAD H ;Shift next MS Bit into Carry
|
||||
ACI 0 ;A = 0 or 1 depending on Carry
|
||||
RRC ;Rotate LS bit of A into MS bit
|
||||
MOV B,A ;Save result (00H or 80H)
|
||||
XCHG ;HL -> FCB character
|
||||
MOV A,M ;Get FCB character
|
||||
ANI 7FH ;Isolate all but Attribute bit
|
||||
ORA B ;Set Attribute with result
|
||||
MOV M,A ;and store back into FCB
|
||||
XCHG ;DE -> FCB, HL = remaining bit map
|
||||
INX D ;DE -> Next character in FCB
|
||||
DCR C ;Downdate character count
|
||||
JNZ SFAL ;Loop back for next character
|
||||
MVI C,B$SETFAT ;Set File Attribute function code
|
||||
POP D ;Recover FCB Pointer
|
||||
CALL BDOS
|
||||
CPI 0FFH ;Carry set if OK, clear if error
|
||||
CMC ;Invert to use Carry set if error
|
||||
RET
|
||||
|
||||
|
||||
|
||||
|
||||
34
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-3.ASM
Normal file
34
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-3.ASM
Normal file
@@ -0,0 +1,34 @@
|
||||
; Figure 5-3
|
||||
;
|
||||
; Useful subroutines using the Write Console Byte
|
||||
; BDOS Function.
|
||||
;
|
||||
; MSGOUT
|
||||
; Output null-byte terminated message
|
||||
;
|
||||
; Calling sequence
|
||||
;
|
||||
; MESSAGE: DB 'Message',0
|
||||
; :
|
||||
; LXI H,MESSAGE
|
||||
; CALL MSGOUT
|
||||
;
|
||||
; Exit Parameters
|
||||
; HL -> Null byte terminator
|
||||
;
|
||||
B$CONOUT EQU 2 ;Write console byte
|
||||
BDOS EQU 5 ;BDOS Entry Point
|
||||
;
|
||||
MSGOUT:
|
||||
MOV A,M ;Get next byte for output
|
||||
ORA A
|
||||
RZ ;Return when null-byte
|
||||
INX H ;Update message pointer
|
||||
PUSH H ;Save updated pointer
|
||||
MOV E,A ;Ready for BDOS
|
||||
MVI C,B$CONOUT ;Function code
|
||||
CALL BDOS
|
||||
POP H ;Recover message pointer
|
||||
JMP MSGOUT ;Go back for next character
|
||||
|
||||
|
||||
37
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-4.ASM
Normal file
37
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-4.ASM
Normal file
@@ -0,0 +1,37 @@
|
||||
; Figure 5-4
|
||||
;
|
||||
; Useful subroutines using the Write Console Byte
|
||||
; BDOS Function.
|
||||
;
|
||||
; MSGOUTI (Message Out In-line)
|
||||
; Output null-byte terminated message that follows the
|
||||
; CALL to MSGOUTI.
|
||||
;
|
||||
; Calling sequence
|
||||
;
|
||||
; CALL MSGOUTI
|
||||
; DB 'Message',0
|
||||
; ... next instruction
|
||||
;
|
||||
; Exit Parameters
|
||||
; HL -> instruction following message
|
||||
;
|
||||
B$CONOUT EQU 2 ;Write console byte
|
||||
BDOS EQU 5 ;BDOS Entry Point
|
||||
;
|
||||
MSGOUTI:
|
||||
POP H ;HL -> Message
|
||||
MOV A,M ;Get next data byte
|
||||
INX H ;Update message pointer
|
||||
ORA A ;Check if null byte
|
||||
JNZ MSGOUTIC ;No, continue
|
||||
PCHL ;Yes, return to next instruction
|
||||
;after in-line message
|
||||
MSGOUTIC:
|
||||
PUSH H ;Save message pointer
|
||||
MOV E,A ;Ready for BDOS
|
||||
MVI C,B$CONOUT ;Function code
|
||||
CALL BDOS
|
||||
JMP MSGOUTI ;Go back for next char.
|
||||
|
||||
|
||||
55
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-5.ASM
Normal file
55
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-5.ASM
Normal file
@@ -0,0 +1,55 @@
|
||||
; Figure 5-5
|
||||
;
|
||||
; RL$RDR
|
||||
; Read Line from Reader Device.
|
||||
; Carriage Returns are ignored, and input terminates
|
||||
; when specified number of characters have been read
|
||||
; or a Line Feed is input.
|
||||
;
|
||||
; Note : Potential weakness is that there is no
|
||||
; timeout in this subroutine. It will wait forever
|
||||
; if no more characters arrive at the Reader device.
|
||||
;
|
||||
; Calling sequence
|
||||
;
|
||||
; LXI H,BUFFER
|
||||
; LXI B,MAXCOUNT
|
||||
; CALL RL$RDR
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; HL -> 00H byte terminating string
|
||||
; BC = Residual Count (0 if Max. chars read)
|
||||
; E = Last Character Read
|
||||
;
|
||||
B$READIN EQU 3 ;Reader Input
|
||||
BDOS EQU 5 ;BDOS Entry Point
|
||||
;
|
||||
CR EQU 0DH ;Carriage Return
|
||||
LF EQU 0AH ;Line Feed (Terminator)
|
||||
;
|
||||
RL$RDR:
|
||||
MOV A,C ;Check if count 0
|
||||
ORA B ;If count 0 on entry,
|
||||
MOV E,A ; fake last char. read (00H)
|
||||
JZ RL$RDRX ;Yes, exit
|
||||
PUSH B ;Save Max. Chars count
|
||||
PUSH H ;Save Buffer Pointer
|
||||
RL$RDRI: ;Loop back here to ignore char.
|
||||
MVI C,B$READIN
|
||||
CALL BDOS ;A = Character input
|
||||
MOV E,A ;Preserve copy of characters
|
||||
CPI CR ;Check if Carriage Return
|
||||
JZ RL$RDRI ;Yes, ignore it
|
||||
POP H ;Recover Buffer pointer
|
||||
POP B ;Recover Max. Count
|
||||
CPI LF ;Check if Line Feed
|
||||
JZ RL$RDRX ;Yes, exit
|
||||
MOV M,A ;No, store character in buffer
|
||||
INX H ;Update Buffer pointer
|
||||
DCX B ;Downdate count
|
||||
JMP RL$RDR ;Loop back for next char.
|
||||
RL$RDRX:
|
||||
MVI M,0 ;Null byte terminate buffer
|
||||
RET
|
||||
|
||||
46
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-6.ASM
Normal file
46
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-6.ASM
Normal file
@@ -0,0 +1,46 @@
|
||||
; Figure 5-6
|
||||
;
|
||||
; WL$PUN
|
||||
; Write Line to Punch device. Output terminates
|
||||
; when a 00H byte is encountered.
|
||||
; A Carriage Return is output when a Line Feed is
|
||||
; encountered.
|
||||
;
|
||||
; Calling sequence
|
||||
;
|
||||
; LXI H,BUFFER
|
||||
; CALL WL$PUN
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; HL -> 00H byte terminator
|
||||
;
|
||||
B$PUNOUT EQU 4
|
||||
BDOS EQU 5
|
||||
;
|
||||
CR EQU 0DH ;Carriage Return
|
||||
LF EQU 0AH ;Line Feed
|
||||
;
|
||||
WL$PUN:
|
||||
PUSH H ;Save Buffer pointer
|
||||
MOV A,M ;Get next character
|
||||
ORA A ;Check if 00H
|
||||
JZ WL$PUNX ;Yes, exit
|
||||
CPI LF ;Check if Line Feed
|
||||
CZ WL$PUNLF ;Yes, O/P CR
|
||||
MOV E,A ;Character to be output
|
||||
MVI C,B$PUNOUT ;Function Code
|
||||
CALL BDOS ;Output character
|
||||
POP H ;Recover Buffer pointer
|
||||
INX H ;Update to next char.
|
||||
JMP WL$PUN ;Output next char
|
||||
WL$PUNLF: ;Line Feed encountered
|
||||
MVI C,B$PUNOUT ;Function Code
|
||||
MVI E,CR ;Output a CR
|
||||
CALL BDOS
|
||||
MVI A,LF ;Recreate Line Feed
|
||||
RET ;Output LF
|
||||
WL$PUNX: ;Exit
|
||||
POP H ;Balance the stack
|
||||
RET
|
||||
|
||||
46
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-7.ASM
Normal file
46
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-7.ASM
Normal file
@@ -0,0 +1,46 @@
|
||||
; Figure 5-7
|
||||
;
|
||||
; WL$LST
|
||||
; Write Line to List device. Output terminates
|
||||
; when a 00H byte is encountered.
|
||||
; A Carriage Return is output when a Line Feed is
|
||||
; encountered.
|
||||
;
|
||||
; Calling sequence
|
||||
;
|
||||
; LXI H,BUFFER
|
||||
; CALL WL$LST
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; HL -> 00H byte terminator
|
||||
;
|
||||
B$LSTOUT EQU 5
|
||||
BDOS EQU 5
|
||||
;
|
||||
CR EQU 0DH ;Carriage Return
|
||||
LF EQU 0AH ;Line Feed
|
||||
;
|
||||
WL$LST:
|
||||
PUSH H ;Save Buffer pointer
|
||||
MOV A,M ;Get next character
|
||||
ORA A ;Check if 00H
|
||||
JZ WL$LSTX ;Yes, exit
|
||||
CPI LF ;Check if Line Feed
|
||||
CZ WL$LSTLF ;Yes, O/P CR
|
||||
MOV E,A ;Character to be output
|
||||
MVI C,B$LSTOUT ;Function Code
|
||||
CALL BDOS ;Output character
|
||||
POP H ;Recover Buffer pointer
|
||||
INX H ;Update to next char.
|
||||
JMP WL$LST ;Output next char
|
||||
WL$LSTLF: ;Line Feed encountered
|
||||
MVI C,B$LSTOUT ;Function Code
|
||||
MVI E,CR ;Output a CR
|
||||
CALL BDOS
|
||||
MVI A,LF ;Recreate Line Feed
|
||||
RET ;Output LF
|
||||
WL$LSTX: ;Exit
|
||||
POP H ;Balance the stack
|
||||
RET
|
||||
|
||||
284
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-8.ASM
Normal file
284
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG5-8.ASM
Normal file
@@ -0,0 +1,284 @@
|
||||
; Figure 5-8
|
||||
;
|
||||
;--------------------------------------------------------------
|
||||
; TESTBED CODE
|
||||
; Because of the complexity of this subroutine, the actual
|
||||
; testbed code has been left in this example. It assumes
|
||||
; that DDT or ZSID will be used to checkout
|
||||
IF 1 ;Change to IF 0 to disable testbed
|
||||
ORG 100H
|
||||
JMP START ;Bypass "variables" setup by DDT
|
||||
OPTIONS: DB 0 ;Option Flags
|
||||
TERMS: DB 'A','E','I',0 ;Terminators
|
||||
BUFFER DB 5 ;Max. Characters in Buffer
|
||||
DB 0 ;Actual Count
|
||||
DB 99,99,99,99,99,99,99 ;Data Bytes
|
||||
START:
|
||||
LXI H,BUFFER ;Get address of buffer
|
||||
LXI D,TERMS ;Address of terminator table
|
||||
LDA OPTIONS ;Get options set by DDT
|
||||
MOV B,A ;Put in correct register
|
||||
CALL RCS ;Enter subroutine
|
||||
CALL 38H ;Force DDT breakpoint
|
||||
JMP START ;Test again
|
||||
ENDIF ;End of testbed x`ce
|
||||
;---------------------------------------------------------------
|
||||
;
|
||||
; RCS : Read Console String (using Raw Input)
|
||||
;
|
||||
; Reads a string of characters into a memory buffer using
|
||||
; Raw Input. Supports options :
|
||||
;
|
||||
; * to echo characters or not (when echoing, a Carriage
|
||||
; Return will be echoed followed by Line Feed)
|
||||
; * warm boot on input of Control-C or not
|
||||
; * terminating input either on :
|
||||
; - max. no of chars input
|
||||
; - matching terminator character
|
||||
;
|
||||
; Calling Sequence
|
||||
;
|
||||
; LXI H,BUFFER Buffer has structure
|
||||
; BUFFER: DB 10 Max. size
|
||||
; DB 0 Actual Read
|
||||
; DS 10+1 Buffer area
|
||||
; MVI B,OPTIONS Options required (see EQUates)
|
||||
; LXI D,TERMS Pointer to 00H-byte terminated
|
||||
; Chars, any one of which is a terminator.
|
||||
; CALL RCS
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; BUFFER : Updated with data bytes and actual character count
|
||||
; input. (Does not include the terminator).
|
||||
; A = Terminating Code
|
||||
; 0 = Maximum number of characters input.
|
||||
; NZ = Terminator character found.
|
||||
;
|
||||
RCS$ECHO EQU 0000$0001B ;Input characters to be echoed
|
||||
RCS$ABORT EQU 0000$0010B ;Abort on Control-C
|
||||
RCS$FOLD EQU 0000$0100B ;Fold lower case to upper
|
||||
RCS$TERM EQU 0000$1000B ;DE -> Term. char. set
|
||||
;
|
||||
B$DIRCONIO EQU 6 ;Direct console I/O
|
||||
BDOS EQU 5 ;BDOS entry point
|
||||
|
||||
CTL$C EQU 03H ;Control-C
|
||||
CR EQU 0DH ;Carriage Return
|
||||
LF EQU 0AH ;Line Feed
|
||||
BS EQU 08H ;Backspace
|
||||
|
||||
RCS$ST: ;Internal standard terminator table
|
||||
DB 0DH ;Carriage return
|
||||
DB 0AH ;Line feed
|
||||
DB 0 ;End of table
|
||||
;
|
||||
RCS$BSS: ;Destructive backspace sequence
|
||||
DB BS,' ',BS,0
|
||||
;
|
||||
RCS: ;<<<<< Main Entry
|
||||
INX H ;HL -> Actual count
|
||||
MVI M,0 ;Reset to initial state
|
||||
DCX H ;HL -> Max. count
|
||||
RCS$L:
|
||||
PUSH H ;Save buffer pointer
|
||||
CALL RCS$GC ;Get character and execute :
|
||||
; ECHO, ABORT, and FOLD options
|
||||
;C = character input
|
||||
POP H ;Recover buffer pointer
|
||||
MVI A,RCS$TERM ;Check if user-specified terminator
|
||||
ANA B ;B = options
|
||||
JNZ RCS$UST ;User specified terminators
|
||||
LXI D,RCS$ST ;Standard terminators
|
||||
RCS$UST:
|
||||
CALL RCS$CT ;Check for terminator
|
||||
JZ RCS$NOTT ;Not terminator
|
||||
MOV B,A ;Preserve terminating char
|
||||
RCS$MCI: ;(Max. char input shares this code)
|
||||
MVI C,0 ;Terminate buffer
|
||||
CALL RCS$SC ;Save character
|
||||
MOV A,B ;Recover terminating char
|
||||
ORA A ;Set flags
|
||||
RET
|
||||
RCS$NOTT: ;Not a terminator
|
||||
MVI A,BS ;Check for backspace
|
||||
CMP C
|
||||
JZ RCS$BS ;Backspace entered
|
||||
CALL RCS$SC ;Save character in buffer
|
||||
CALL RCS$UC ;Update count
|
||||
JNZ RCS$L ;Not max. so get another char
|
||||
MVI B,0 ;Fake terminating char
|
||||
JMP RCS$MCI ;A = 0 for max. chars input
|
||||
;
|
||||
RCS$BS: ;Backspace entered
|
||||
PUSH H ;Save buffer pointer
|
||||
INX H ;HL -> actual count
|
||||
DCR M ;Backup one
|
||||
JM RCS$NBS ;Check if count now -ve
|
||||
LXI H,RCS$BSS ;HL -> Backspacing sequence
|
||||
MVI A,RCS$ECHO ;No, check if echoing
|
||||
ANA B ;(BS will have been echoed if so)
|
||||
JZ RCS$BSNE ;No, input BS not echoed
|
||||
INX H ;Bypass initial Backspace
|
||||
RCS$BSNE:
|
||||
PUSH B ;Save Options and character
|
||||
PUSH D ;Save terminator table pointer
|
||||
CALL WCS ;Write console string
|
||||
POP D ;Recover terminator table pointer
|
||||
POP B ;Recover options and character
|
||||
JMP RCS$BSX ;Exit from Backspace logic
|
||||
RCS$NBS:
|
||||
INR M ;Reset count to 0
|
||||
RCS$BSX:
|
||||
POP H ;Recover buffer pointer
|
||||
JMP RCS$L ;Get next character
|
||||
|
||||
RCS$SC: ;Save character in C in buffer
|
||||
;HL -> buffer pointer
|
||||
PUSH D ;Save terminator table pointer
|
||||
PUSH H ;Save buffer pointer
|
||||
INX H ;HL -> actual count in buffer
|
||||
MOV E,M ;Get actual count
|
||||
INR E ;Count of 0 points to first data byte
|
||||
MVI D,0 ;Make word value of actual count
|
||||
DAD D ;HL -> next free data byte
|
||||
MOV M,C ;Save data byte away
|
||||
POP H ;Recover buffer pointer
|
||||
POP D ;Recover terminator table pointer
|
||||
RET
|
||||
;
|
||||
RCS$UC: ;Update buffer count and check for max.
|
||||
;Return Z set if = to max, NZ if not
|
||||
;HL -> buffer on entry
|
||||
PUSH H ;Save buffer pointer
|
||||
MOV A,M ;Get max. count
|
||||
INX H ;HL -> actual count
|
||||
INR M ;Increase actual count
|
||||
CMP M ;Compare max. to actual
|
||||
POP H ;Recover buffer pointer
|
||||
RET ;Z-flag set
|
||||
;
|
||||
RCS$GC: ;Get character and execute
|
||||
; ECHO, ABORT and FOLD options
|
||||
PUSH D ;Save terminator table pointer
|
||||
PUSH H ;Save buffer pointer
|
||||
PUSH B ;Save option flags
|
||||
RCS$WT:
|
||||
MVI C,B$DIRCONIO ;Function code
|
||||
MVI E,0FFH ;Specify input
|
||||
CALL BDOS
|
||||
ORA A ;Check if data waiting
|
||||
JZ RCS$WT ;Go back and wait
|
||||
POP B ;Recover option flags
|
||||
MOV C,A ;Save data byte
|
||||
MVI A,RCS$ABORT ;Check if abort option enabled
|
||||
ANA B
|
||||
JZ RCS$NA ;No abort
|
||||
MVI A,CTL$C ;Check for control-C
|
||||
CMP C
|
||||
JZ 0 ;Warm boot
|
||||
RCS$NA:
|
||||
MVI A,RCS$FOLD ;Check if folding enabled
|
||||
ANA B
|
||||
CNZ TOUPPER ;Fold to UPPER CASE
|
||||
MVI A,RCS$ECHO ;Check if echo required
|
||||
ANA B
|
||||
JZ RCS$NE ;No echo required
|
||||
PUSH B ;Save options and character
|
||||
MOV E,C ;Move character for output
|
||||
MVI C,B$DIRCONIO ;Function code
|
||||
CALL BDOS ;Echo character
|
||||
POP B ;Recover options and character
|
||||
MVI A,CR ;Check if Carriage Return
|
||||
CMP C
|
||||
JNZ RCS$NE ;No
|
||||
PUSH B ;Save options and character
|
||||
MVI C,B$DIRCONIO ;Function code
|
||||
MVI E,LF ;Output line feed
|
||||
CALL BDOS
|
||||
POP B ;Recover options and character
|
||||
RCS$NE:
|
||||
POP H ;Recover buffer pointer
|
||||
POP D ;Recover terminator table
|
||||
RET ;Character in C
|
||||
;
|
||||
RCS$CT: ;Check for terminator
|
||||
;C = character just input
|
||||
;DE -> 00-byte character string of Term. Chars
|
||||
;Returns Z status if no match found, NZ if found
|
||||
; (with A = C = Terminating character)
|
||||
PUSH D ;Save table pointer
|
||||
RCS$CTL:
|
||||
LDAX D ;Get next terminator character
|
||||
ORA A ;Check for end of table
|
||||
JZ RCS$CTX ;No terminator matched
|
||||
CMP C ;Compare to input character
|
||||
JZ RCS$CTX ;Terminator matched
|
||||
INX D ;Move to next terminator
|
||||
JMP RCS$CTL ;loop to try next character in table
|
||||
RCS$CTX: ;Check terminator exit
|
||||
ORA A ;At this point, A will either be 0
|
||||
; if the end of the table has been
|
||||
; reached, or NZ if a match has been
|
||||
; found. The Z-flag will be set.
|
||||
POP D ;Recover table pointer
|
||||
RET
|
||||
|
||||
;
|
||||
; TOUPPER - Fold lower case letters to upper
|
||||
;
|
||||
; C = Character on entry and exit
|
||||
;
|
||||
TOUPPER:
|
||||
MVI A,'a'-1 ;Check if folding needed
|
||||
CMP C ;Compare to input char
|
||||
JNC TOUPX ;No, char is < or = 'a'-1
|
||||
MVI A,'z' ;Maybe, char is = or > 'a'
|
||||
CMP C
|
||||
JC TOUPX ;No, char is > 'z'
|
||||
MVI A,0DFH ;Fold character
|
||||
ANA C
|
||||
MOV C,A ;Return folded character
|
||||
TOUPX:
|
||||
RET
|
||||
;
|
||||
;
|
||||
; WCS - Write Console String (Using Raw I/O)
|
||||
;
|
||||
; Output terminates when a 00H byte is encountered.
|
||||
; A Carriage Return is output when a Line Feed is
|
||||
; encountered.
|
||||
;
|
||||
; Calling sequence
|
||||
;
|
||||
; LXI H,BUFFER
|
||||
; CALL WCS
|
||||
;
|
||||
; Exit Parameters
|
||||
;
|
||||
; HL -> 00H byte terminator
|
||||
;
|
||||
;
|
||||
WCS:
|
||||
PUSH H ;Save buffer pointer
|
||||
MOV A,M ;Get next character
|
||||
ORA A ;Check if 00H
|
||||
JZ WCSX ;Yes, exit
|
||||
CPI LF ;Check if Line Feed
|
||||
CZ WCSLF ;Yes, O/P CR
|
||||
MOV E,A ;Character to be output
|
||||
MVI C,B$DIRCONIO ;Function Code
|
||||
CALL BDOS ;Output character
|
||||
POP H ;Recover Buffer pointer
|
||||
INX H ;Update to next char.
|
||||
JMP WCS ;Output next char
|
||||
WCSLF: ;Line Feed encountered
|
||||
MVI C,B$DIRCONIO ;Function Code
|
||||
MVI E,CR ;Output a CR
|
||||
CALL BDOS
|
||||
MVI A,LF ;Recreate Line Feed
|
||||
RET ;Output LF
|
||||
WCSX: ;Exit
|
||||
POP H ;Balance the stack
|
||||
RET
|
||||
|
||||
252
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG7-5.ASM
Normal file
252
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG7-5.ASM
Normal file
@@ -0,0 +1,252 @@
|
||||
; Figure 7-5.
|
||||
;
|
||||
; Example Put CP/M
|
||||
;
|
||||
; This program writes out the CP/M Cold Boot Loader, the
|
||||
; CCP, BDOS and BIOS to a floppy diskette. It runs under
|
||||
; CP/M as a normal Transient Program.
|
||||
;
|
||||
Version EQU '01' ;Equates used in the sign on message
|
||||
Month EQU '07'
|
||||
Day EQU '24'
|
||||
Year EQU '82'
|
||||
;
|
||||
;
|
||||
; The actual PUTCPMF5.COM program consists of this code,
|
||||
; plus the BOOTF5.HEX, the CCP, BDOS and BIOS.
|
||||
;
|
||||
; When this program executes, the memory image should
|
||||
; look like this :
|
||||
;
|
||||
; Component Base Address
|
||||
; BIOS 1F80H
|
||||
; BDOS 1180H
|
||||
; CCP 0980H
|
||||
; BOOTF5 0780H
|
||||
;
|
||||
; The components are produced as follows :
|
||||
;
|
||||
; BIOS.HEX By assembling source code
|
||||
; BDOS ) From a CPMnn.COM file output
|
||||
; CCP ) by MOVCPM and SAVEd on disk
|
||||
; BOOTF5.HEX By assembling source code
|
||||
;
|
||||
; The components are pieced together using DDT with the
|
||||
; following commands :
|
||||
;
|
||||
; DDT CPMnn.COM
|
||||
; IPUTCPMF5.HEX
|
||||
; R (Reads in this program)
|
||||
; IBOOTF5.HEX
|
||||
; R680 (Reads in Boot at 0780H)
|
||||
; IBIOS.HEX
|
||||
; R2980 (Reads in BIOS at 1F80H)
|
||||
; G0 (Exit from DDT)
|
||||
; SAVE 40 PUTCPMF5.COM (Create final .COM file)
|
||||
;
|
||||
; The actual layout of the diskette is as follows :
|
||||
;
|
||||
; Track 0 Sector
|
||||
; 1 2 3 4 5 6 7 8 9
|
||||
; Head +-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
; 0 |Boot |<======== CCP ========>|<======= BDOS ========|
|
||||
; +-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
; 1 |====== BDOS ====>|<============= BIOS ============>|
|
||||
; +-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
; 10 11 12 13 14 15 16 17 18
|
||||
; Sector
|
||||
;
|
||||
; Equates for defining memory size and the base address and
|
||||
; length of the system components.
|
||||
;
|
||||
Memory$Size EQU 64 ;Number of Kbytes of RAM
|
||||
;
|
||||
; The BIOS Length must match that declared in the BIOS.
|
||||
;
|
||||
BIOS$Length EQU 0900H
|
||||
;
|
||||
Boot$Length EQU 512
|
||||
CCP$Length EQU 0800H ;Constant
|
||||
BDOS$Length EQU 0E00H ;Constant
|
||||
;
|
||||
Length$In$Bytes EQU CCP$Length + BDOS$Length + BIOS$Length
|
||||
;
|
||||
Start$Image EQU 980H - Boot$Length ;Address of CP/M Image
|
||||
Length$Image EQU Length$In$Bytes + Boot$Length
|
||||
;
|
||||
;
|
||||
; Disk Characteristics
|
||||
;
|
||||
; These equates describe the physical characteristics of
|
||||
; the floppy diskette so that the program can move from
|
||||
; one sector to the next, updating the track and resetting
|
||||
; the sector when necessary.
|
||||
;
|
||||
First$Sector$on$Track EQU 1
|
||||
Last$Sector$on$Track EQU 18
|
||||
Last$Sector$on$Head$0 EQU 9
|
||||
Sector$Size EQU 512
|
||||
;
|
||||
;
|
||||
; Controller Characteristics
|
||||
;
|
||||
; On this computer system, the floppy disk controller can write
|
||||
; multiple sectors in a single command. However, in order to
|
||||
; to produce a more general example it is shown only reading one
|
||||
; sector at a time.
|
||||
;
|
||||
Sectors$Per$Write EQU 1
|
||||
;
|
||||
;
|
||||
; Cold Boot Characteristics
|
||||
;
|
||||
Start$Track EQU 0 ;Initial values for CP/M image
|
||||
Start$Sector EQU 1 ;= " =
|
||||
Sectors$To$Write EQU (Length$Image + Sector$Size - 1) / Sector$Size
|
||||
;
|
||||
|
||||
;
|
||||
B$PRINTS EQU 9 ;Print string terminated by $
|
||||
BDOS EQU 5 ;BDOS Entry Point
|
||||
;
|
||||
;
|
||||
|
||||
ORG 100H
|
||||
Put$CPM:
|
||||
JMP Main$Code ;Enter main code body
|
||||
;For reasons of clarity, the main
|
||||
; data structures are shown before the
|
||||
; executable code.
|
||||
CR EQU 0DH ;Carriage Return
|
||||
LF EQU 0AH ;Line Feed
|
||||
;
|
||||
Signon$Message:
|
||||
DB CR,LF,'Put CP/M on Diskette'
|
||||
DB CR,LF
|
||||
DB 'Version '
|
||||
DW Version
|
||||
DB ' '
|
||||
DW Month
|
||||
DB '/'
|
||||
DW Day
|
||||
DB '/'
|
||||
DW Year
|
||||
DB CR,LF,'$'
|
||||
|
||||
;
|
||||
; Disk Control Tables
|
||||
;
|
||||
Disk$Control$5 EQU 45H ;5 1/4" Control Byte
|
||||
Command$Block$5 EQU 46H ;Control Table Pointer
|
||||
Disk$Status EQU 43H ;Completion status
|
||||
;
|
||||
;
|
||||
; The command table Track and DMA$Address can also be used
|
||||
; as working storage and updated as the load process
|
||||
; continues. The Sector in the command table cannot be
|
||||
; used directly as the disk controller requires it to be
|
||||
; the sector number on the specified head (1 - 9) rather
|
||||
; than the sector number on track. Hence a separate variable
|
||||
; must be used.
|
||||
;
|
||||
Sector: DB Start$Sector
|
||||
;
|
||||
Command$Table: DB 02H ;Command - Write
|
||||
Unit: DB 0 ;Unit (Drive) number = 0 or 1
|
||||
Head: DB 0 ;Head number = 0 or 1
|
||||
Track: DB Start$Track ;Used as working variable
|
||||
Sector$on$head: DB 0 ;Converted by low-level driver
|
||||
Byte$Count: DW Sector$Size * Sectors$Per$Write
|
||||
DMA$Address: DW Start$Image
|
||||
Next$Status: DW Disk$Status ;Pointer to next Status Block
|
||||
; if commands are chained.
|
||||
Next$Control: DW Disk$Control$5 ;Pointer to next Control Byte
|
||||
; if commands are chained.
|
||||
|
||||
Main$Code:
|
||||
LXI SP,Put$CPM ;Stack grows down below code
|
||||
|
||||
LXI D,Signon$Message ;Sign on
|
||||
MVI C,B$PRINTS ;Print string until $
|
||||
CALL BDOS
|
||||
|
||||
LXI H,Command$Table ;Point the disk controller at
|
||||
SHLD Command$Block$5 ; the command block
|
||||
|
||||
MVI C,Sectors$To$Write ;Set sector count
|
||||
Write$Loop:
|
||||
CALL Put$CPM$Write ;Write data onto diskette
|
||||
DCR C ;Downdate sector count
|
||||
JZ 0 ;Warm Boot
|
||||
|
||||
LXI H,Sector ;Update sector number
|
||||
MVI A,Sectors$Per$Write ; by adding on number of sectors
|
||||
ADD M ; by controller
|
||||
MOV M,A ;Save result
|
||||
MVI A,Last$Sector$On$Track + 1 ;Check if at end of track
|
||||
CMP M
|
||||
JNZ Not$End$Track
|
||||
|
||||
MVI M,First$Sector$On$Track ;Yes, reset to beginning
|
||||
LHLD Track ;Update Track number
|
||||
INX H
|
||||
SHLD Track
|
||||
|
||||
Not$End$Track:
|
||||
LHLD DMA$Address ;Update DMA Address
|
||||
LXI D,Sector$Size * Sectors$Per$Write
|
||||
DAD D
|
||||
SHLD DMA$Address
|
||||
JMP Write$Loop ;Write next block
|
||||
;
|
||||
Put$CPM$Write: ;At this point, the description of the
|
||||
; operation required is in the variables
|
||||
; contained in the Command Table, along
|
||||
; with the Sector variable.
|
||||
|
||||
PUSH B ;Save sector count in C
|
||||
|
||||
;------ Change this routine to match the disk controller in use ------
|
||||
|
||||
MVI B,0 ;Assume head 0
|
||||
LDA Sector ;Get requested sector
|
||||
MOV C,A ;Take a copy of it
|
||||
CPI Last$Sector$on$Head$0+1 ;Check if on head 1
|
||||
JC Head$0 ;No
|
||||
SUI Last$Sector$on$Head$0 ;Bias down for head 1
|
||||
MOV C,A ;Save copy
|
||||
INR B ;Set head 1
|
||||
Head$0:
|
||||
MOV A,B ;Get head
|
||||
STA Head
|
||||
MOV A,C ;Get sector
|
||||
STA Sector$On$Head
|
||||
|
||||
LXI H,Disk$Control$5 ;Activate controller
|
||||
MVI M,80H
|
||||
|
||||
Wait$For$Boot$Complete:
|
||||
MOV A,M ;Get status byte
|
||||
ORA A ;Check if complete
|
||||
JNZ Wait$For$Boot$Complete ;No
|
||||
;Yes, check for errors
|
||||
LDA Disk$Status
|
||||
CPI 80H
|
||||
JC Put$CPM$Error ;Yes, an error occurred
|
||||
|
||||
;------ End of Physical Write routine ------
|
||||
|
||||
POP B ;Recover sector count in C
|
||||
RET
|
||||
;
|
||||
Put$CPM$Error:
|
||||
LXI D,Put$CPM$Error$Message
|
||||
MVI C,B$PRINTS ;Print string until $
|
||||
CALL BDOS ;Output error message
|
||||
JMP Main$Code ;Restart the loader
|
||||
;
|
||||
|
||||
Put$CPM$Error$Message:
|
||||
DB CR,LF,'Error in writing CP/M - retrying...',CR,LF,'$'
|
||||
END Put$CPM
|
||||
|
||||
272
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG7-7.ASM
Normal file
272
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG7-7.ASM
Normal file
@@ -0,0 +1,272 @@
|
||||
; Figure 7-7.
|
||||
;
|
||||
; Example CP/M Cold Bootstrap Loader
|
||||
;
|
||||
; This program is written out to Track 0, Head 0, Sector 1
|
||||
; by the PUTCPMF5 program.
|
||||
; It is loaded into memory at location 100H on up by the
|
||||
; PROM-based bootstrap mechanism that gets control of the
|
||||
; CPU on power-up or system reset.
|
||||
;
|
||||
Version EQU '01' ;Equates used in the sign on message
|
||||
Month EQU '07'
|
||||
Day EQU '24'
|
||||
Year EQU '82'
|
||||
;
|
||||
Debug EQU 0 ;Set Non-zero to debug as normal
|
||||
; transient program
|
||||
;
|
||||
; The actual layout of the diskette is as follows :
|
||||
;
|
||||
; Track 0 Sector
|
||||
; 1 2 3 4 5 6 7 8 9
|
||||
; Head +-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
; 0 |Boot |<======== CCP ========>|<======= BDOS ========|
|
||||
; +-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
; 1 |====== BDOS ====>|<============= BIOS ============>|
|
||||
; +-----+-----+-----+-----+-----+-----+-----+-----+-----+
|
||||
; 10 11 12 13 14 15 16 17 18
|
||||
; Sector
|
||||
;
|
||||
; Equates for defining memory size and the base address and
|
||||
; length of the system components.
|
||||
;
|
||||
Memory$Size EQU 64 ;Number of Kbytes of RAM
|
||||
;
|
||||
; The BIOS Length must match that declared in the BIOS.
|
||||
;
|
||||
BIOS$Length EQU 0900H
|
||||
;
|
||||
CCP$Length EQU 0800H ;Constant
|
||||
BDOS$Length EQU 0E00H ;Constant
|
||||
;
|
||||
Length$In$K EQU ((CCP$Length + BDOS$Length + BIOS$Length) / 1024) + 1
|
||||
Length$In$Bytes EQU CCP$Length + BDOS$Length + BIOS$Length
|
||||
;
|
||||
IF NOT Debug
|
||||
CCP$Entry EQU (Memory$Size - Length$In$K) * 1024
|
||||
ENDIF
|
||||
IF Debug
|
||||
CCP$Entry EQU 3980H ;Read into a lower address.
|
||||
;This address is chosen to be above
|
||||
; the area into which DDT initially loads
|
||||
; and the 980H makes the addresses similar
|
||||
; to the SYSGEN values so that the memory
|
||||
; image can be checked with DDT.
|
||||
ENDIF
|
||||
|
||||
BDOS$Entry EQU CCP$Entry + CCP$Length + 6
|
||||
BIOS$Entry EQU CCP$Entry + CCP$Length + BDOS$Length
|
||||
;
|
||||
;
|
||||
; Disk Characteristics
|
||||
;
|
||||
; These equates describe the physical characteristics of
|
||||
; the floppy diskette so that the program can move from
|
||||
; one sector to the next, updating the track and resetting
|
||||
; the sector when necessary.
|
||||
;
|
||||
First$Sector$on$Track EQU 1
|
||||
Last$Sector$on$Track EQU 18
|
||||
Last$Sector$on$Head$0 EQU 9
|
||||
Sector$Size EQU 512
|
||||
;
|
||||
;
|
||||
; Controller Characteristics
|
||||
;
|
||||
; On this computer system, the floppy disk controller can read
|
||||
; multiple sectors in a single command. However, in order to
|
||||
; to produce a more general example it is shown only reading one
|
||||
; sector at a time.
|
||||
;
|
||||
Sectors$Per$Read EQU 1
|
||||
;
|
||||
;
|
||||
; Cold Boot Characteristics
|
||||
;
|
||||
Start$Track EQU 0 ;Initial values for CP/M image
|
||||
Start$Sector EQU 2 ;= " =
|
||||
Sectors$To$Read EQU (Length$In$Bytes + Sector$Size - 1) / Sector$Size
|
||||
;
|
||||
;
|
||||
;
|
||||
|
||||
ORG 100H
|
||||
Cold$Boot$Loader:
|
||||
JMP Main$Code ;Enter main code body
|
||||
;For reasons of clarity, the main
|
||||
; data structures are shown before the
|
||||
; executable code.
|
||||
CR EQU 0DH ;Carriage Return
|
||||
LF EQU 0AH ;Line Feed
|
||||
;
|
||||
Signon$Message:
|
||||
DB CR,LF,'CP/M Bootstrap Loader'
|
||||
IF Debug
|
||||
DB ' (Debug)'
|
||||
ENDIF
|
||||
DB CR,LF
|
||||
DB 'Version '
|
||||
DW Version
|
||||
DB ' '
|
||||
DW Month
|
||||
DB '/'
|
||||
DW Day
|
||||
DB '/'
|
||||
DW Year
|
||||
DB CR,LF,0
|
||||
|
||||
;
|
||||
; Disk Control Tables
|
||||
;
|
||||
Disk$Control$5 EQU 45H ;5 1/4" Control Byte
|
||||
Command$Block$5 EQU 46H ;Control Table Pointer
|
||||
Disk$Status EQU 43H ;Completion status
|
||||
;
|
||||
;
|
||||
; The command table Track and DMA$Address can also be used
|
||||
; as working storage and updated as the load process
|
||||
; continues. The Sector in the command table cannot be
|
||||
; used directly as the disk controller requires it to be
|
||||
; the sector number on the specified head (1 - 9) rather
|
||||
; than the sector number on track. Hence a separate variable
|
||||
; must be used.
|
||||
;
|
||||
Sector: DB Start$Sector
|
||||
;
|
||||
Command$Table: DB 01H ;Command - Read
|
||||
Unit: DB 0 ;Unit (Drive) number = 0 or 1
|
||||
Head: DB 0 ;Head number = 0 or 1
|
||||
Track: DB Start$Track ;Used as working variable
|
||||
Sector$on$head: DB 0 ;Converted by low-level driver
|
||||
Byte$Count: DW Sector$Size * Sectors$Per$Read
|
||||
DMA$Address: DW CCP$Entry
|
||||
Next$Status: DW Disk$Status ;Pointer to next Status Block
|
||||
; if commands are chained.
|
||||
Next$Control: DW Disk$Control$5 ;Pointer to next Control Byte
|
||||
; if commands are chained.
|
||||
|
||||
Main$Code:
|
||||
LXI SP,Cold$Boot$Loader ;Stack grows down below code
|
||||
|
||||
LXI H,Signon$Message ;Sign on
|
||||
CALL Display$Message
|
||||
|
||||
LXI H,Command$Table ;Point the disk controller at
|
||||
SHLD Command$Block$5 ; the command block
|
||||
|
||||
MVI C,Sectors$To$Read ;Set sector count
|
||||
Load$Loop:
|
||||
CALL Cold$Boot$Read ;Read data into memory
|
||||
DCR C ;Downdate sector count
|
||||
|
||||
IF NOT Debug
|
||||
JZ BIOS$Entry ;Enter BIOS when load done
|
||||
ENDIF
|
||||
IF Debug
|
||||
JZ 0 ;Warm Boot
|
||||
ENDIF
|
||||
|
||||
LXI H,Sector ;Update sector number
|
||||
MVI A,Sectors$Per$Read ; by adding on number of sectors
|
||||
ADD M ; by controller
|
||||
MOV M,A ;Save result
|
||||
MVI A,Last$Sector$On$Track + 1 ;Check if at end of track
|
||||
CMP M
|
||||
JNZ Not$End$Track
|
||||
|
||||
MVI M,First$Sector$On$Track ;Yes, reset to beginning
|
||||
LHLD Track ;Update Track number
|
||||
INX H
|
||||
SHLD Track
|
||||
|
||||
Not$End$Track:
|
||||
LHLD DMA$Address ;Update DMA Address
|
||||
LXI D,Sector$Size * Sectors$Per$Read
|
||||
DAD D
|
||||
SHLD DMA$Address
|
||||
JMP Load$Loop ;Read next block
|
||||
;
|
||||
Cold$Boot$Read: ;At this point, the description of the
|
||||
; operation required is in the variables
|
||||
; contained in the Command Table, along
|
||||
; with the Sector variable.
|
||||
|
||||
PUSH B ;Save sector count in C
|
||||
|
||||
;------ Change this routine to match the disk controller in use ------
|
||||
|
||||
MVI B,0 ;Assume head 0
|
||||
LDA Sector ;Get requested sector
|
||||
MOV C,A ;Take a copy of it
|
||||
CPI Last$Sector$on$Head$0+1 ;Check if on head 1
|
||||
JC Head$0 ;No
|
||||
SUI Last$Sector$on$Head$0 ;Bias down for head 1
|
||||
MOV C,A ;Save copy
|
||||
INR B ;Set head 1
|
||||
Head$0:
|
||||
MOV A,B ;Get head
|
||||
STA Head
|
||||
MOV A,C ;Get sector
|
||||
STA Sector$On$Head
|
||||
|
||||
LXI H,Disk$Control$5 ;Activate controller
|
||||
MVI M,80H
|
||||
|
||||
Wait$For$Boot$Complete:
|
||||
MOV A,M ;Get status byte
|
||||
ORA A ;Check if complete
|
||||
JNZ Wait$For$Boot$Complete ;No
|
||||
;Yes, check for errors
|
||||
LDA Disk$Status
|
||||
CPI 80H
|
||||
JC Cold$Boot$Error ;Yes, an error occurred
|
||||
|
||||
;------ End of Physical Read routine ------
|
||||
|
||||
POP B ;Recover sector count in C
|
||||
RET
|
||||
;
|
||||
Cold$Boot$Error:
|
||||
LXI H,Cold$Boot$Error$Message
|
||||
CALL Display$Message ;Output error message
|
||||
JMP Main$Code ;Restart the loader
|
||||
;
|
||||
|
||||
Cold$Boot$Error$Message:
|
||||
DB CR,LF,'Bootstrap Loader Error - retrying...',CR,LF,0
|
||||
;
|
||||
; Equates for Terminal Output
|
||||
;
|
||||
Terminal$Status$Port EQU 01H
|
||||
Terminal$Data$Port EQU 02H
|
||||
;
|
||||
Terminal$Output$Ready EQU 0000$0001B
|
||||
;
|
||||
;
|
||||
Display$Message: ;Displays the specified message on the console.
|
||||
;On entry, HL points to a stream of bytes to be
|
||||
;output. A 00H-byte terminates the message.
|
||||
MOV A,M ;Get next message byte
|
||||
ORA A ;Check if terminator
|
||||
RZ ;Yes, return to caller
|
||||
MOV C,A ;Prepare for output
|
||||
|
||||
Output$Not$Ready:
|
||||
IN Terminal$Status$Port ;Check if ready for output
|
||||
ANI Terminal$Output$Ready
|
||||
JZ Output$Not$Ready ;No, wait
|
||||
MOV A,C ;Get Data character
|
||||
OUT Terminal$Data$Port ;Output to Screen
|
||||
|
||||
INX H ;Move to next byte of message
|
||||
JMP Display$Message ;Loop until complete message output
|
||||
|
||||
;The PROM-based bootstrap loader checks
|
||||
; to see that the characters "CP/M"
|
||||
; are on the diskette bootstrap sector
|
||||
; before it transfers control to it.
|
||||
ORG 2E0H
|
||||
DB 'CP/M'
|
||||
END Cold$Boot$Loader
|
||||
|
||||
4448
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG8-10.ASM
Normal file
4448
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG8-10.ASM
Normal file
File diff suppressed because it is too large
Load Diff
102
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG8-6.ASM
Normal file
102
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG8-6.ASM
Normal file
@@ -0,0 +1,102 @@
|
||||
; Figure 8-6
|
||||
;
|
||||
; Device Table Equates
|
||||
; The drivers use an Device Table for each
|
||||
; Physical Device they service. The EQUates that follow
|
||||
; are used to access the various fields within the
|
||||
; Device Table.
|
||||
;
|
||||
; Port Numbers and Status Bits
|
||||
DT$Status$Port EQU 0 ;Device Status Port number
|
||||
DT$Data$Port EQU DT$Status$Port+1
|
||||
;Device Data Port number
|
||||
DT$Output$Ready EQU DT$DataPort+1
|
||||
;Output ready status mask
|
||||
DT$Input$Ready EQU DT$Output$Ready+1
|
||||
;Input ready status mask
|
||||
DT$DTR$Ready EQU DT$Input$Ready+1
|
||||
;DTR ready to send mask
|
||||
DT$Reset$Int$Port EQU DT$DTR$Ready+1
|
||||
;Port number used to reset an
|
||||
; interrupt
|
||||
DT$Reset$Int$Value EQU DT$Reset$Int$Port+1
|
||||
;Value output to reset interrupt
|
||||
DT$Detect$Error$Port EQU DT$Reset$Int$Value+1
|
||||
;Port number for error detect
|
||||
DT$Detect$Error$Value EQU DT$Detect$Error$Port+1
|
||||
;Mask for detecting error (parity etc.)
|
||||
DT$Reset$Error$Port EQU DT$Detect$Error$Value+1
|
||||
;Output to port to reset error
|
||||
DT$Reset$Error$Value EQU DT$Reset$Error$Port+1
|
||||
;Value to output to reset error
|
||||
DT$RTS$Control$Port EQU DT$Reset$Error$Value+1
|
||||
;Control port for lowering RTS
|
||||
DT$Drop$RTS$Value EQU DT$RTS$Control$Port+1
|
||||
;Value, when output, to drop RTS
|
||||
DT$Raise$RTS$Value EQU DT$Drop$RTS$Value+1
|
||||
;Value, when output, to raise RTS
|
||||
;
|
||||
; Device Logical Status (incl. Protocols)
|
||||
DT$Status EQU DT$Raise$RTS$Value+1
|
||||
;Status Bits
|
||||
DT$Output$Suspend EQU 0000$0001B ;Output suspended pending
|
||||
; Protocol action
|
||||
DT$Input$Suspend EQU 0000$0010B ;Input suspended until
|
||||
; buffer empties
|
||||
DT$Output$DTR EQU 0000$0100B ;Output uses DTR high to send
|
||||
DT$Output$Xon EQU 0000$1000B ;Output uses Xon/Xoff
|
||||
DT$Output$Etx EQU 0001$0000B ;Output uses Etx/Ack
|
||||
DT$Output$Timeout EQU 0010$0000B ;Output uses Timeout
|
||||
DT$Input$RTS EQU 0100$0000B ;Input uses RTS high to receive
|
||||
DT$Input$Xon EQU 1000$0000B ;Input uses Xon/Xoff
|
||||
;
|
||||
DT$Status$2 EQU DT$Status+1 ;Secondary Status Byte
|
||||
DT$Fake$Typeahead EQU 0000$0001B ;Requests Input$Status to
|
||||
;return "Data Ready" when
|
||||
;Control Characters are in
|
||||
;input buffer
|
||||
;
|
||||
DT$Etx$Count EQU DT$Status$2+1
|
||||
;No. of chars sent in Etx protocol
|
||||
DT$Etx$Message$Length EQU DT$Etx$Count+2
|
||||
;Specified message length
|
||||
;
|
||||
; Input Buffer values
|
||||
DT$Buffer$Base EQU DT$Etx$Message$Length+2
|
||||
;Address of Input Buffer
|
||||
DT$Put$Offset EQU DT$Buffer$Base+2
|
||||
;Offset for Putting chars into buffer
|
||||
DT$Get$Offset EQU DT$Put$Offset+1
|
||||
;Offset for Getting chars from buffer
|
||||
DT$Buffer$Length$Mask EQU DT$Get$Offset+1
|
||||
;Length of buffer - 1
|
||||
;Note : Buffer length must always be
|
||||
; a binary number; e.g. 32, 64 or 128
|
||||
;This mask then becomes :
|
||||
; 32 -> 31 (0001$1111B)
|
||||
; 64 -> 63 (0011$1111B)
|
||||
; 128 -> 127 (0111$1111B)
|
||||
;After the Get/Put offset has been
|
||||
;incremented it is ANDed with the mask
|
||||
;to reset it to zero when the end of
|
||||
;the buffer has been reached.
|
||||
DT$Character$Count EQU DT$Buffer$Length$Mask+1
|
||||
;Count of the number of characters
|
||||
; currently in the buffer
|
||||
DT$Stop$Input$Count EQU DT$Character$Count+1
|
||||
;Stop input when the count reaches
|
||||
; this value
|
||||
DT$Resume$Input$Count EQU DT$Stop$Input$Count+1
|
||||
;Resume input when the count reaches
|
||||
; this value
|
||||
DT$Control$Count EQU DT$Resume$Input$Count+1
|
||||
;Count of the number of control
|
||||
; characters in the buffer
|
||||
DT$Function$Delay EQU DT$Control$Count+1
|
||||
;Number of clock ticks to delay to
|
||||
; allow all characters after function
|
||||
; key lead-in to arrive
|
||||
DT$Initialize$Stream EQU DT$Function$Delay+1
|
||||
;Address of byte stream necessary to
|
||||
; initialize this device
|
||||
|
||||
342
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG9-5.ASM
Normal file
342
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG9-5.ASM
Normal file
@@ -0,0 +1,342 @@
|
||||
; Figure 9-5
|
||||
;
|
||||
; This shows slightly more user-friendly error processor
|
||||
; for disk errors than that shown in the enhanced BIOS
|
||||
; in Figure 8-10.
|
||||
; This version outputs a recommended course of action
|
||||
; depending on the nature of the error detected.
|
||||
; Code that remains unchanged from Figure 8-10 has been
|
||||
; abbreviated.
|
||||
;
|
||||
; Dummy Equates and data declarations needed to get
|
||||
; an error free assembly of this example.
|
||||
;
|
||||
Floppy$Read$Code EQU 01H ;Read Command for controller
|
||||
Floppy$Write$Code EQU 02H ;Write Command for controller
|
||||
;
|
||||
Disk$Hung$Flag: DB 0 ;Set NZ when Watchdog timer times
|
||||
; out.
|
||||
Disk$Timer EQU 600 ;10 seconds delay (16.66ms tick)
|
||||
;
|
||||
Disk$Status$Block EQU 43H ;Address in memory where controller
|
||||
; returns status
|
||||
;Values from controller command table
|
||||
Floppy$Command: DB 0
|
||||
Floppy$Head: DB 0
|
||||
Floppy$Track: DB 0
|
||||
Floppy$Sector: DB 0
|
||||
|
||||
Deblocking$Required: DB 0 ;Flag set by SELDSK according
|
||||
; to selected disk type
|
||||
|
||||
Disk$Error$Flag: DB 0 ;Error flag returned to BDOS
|
||||
;
|
||||
In$Buffer$Disk: DB 0 ;Logical disk Id. relating to current
|
||||
; disk sector in deblocking buffer
|
||||
;
|
||||
; Equates for Messages
|
||||
;
|
||||
BELL EQU 07H ;Sound terminal Bell
|
||||
CR EQU 0DH ;Carriage Return
|
||||
LF EQU 0AH ;Line Feed
|
||||
;
|
||||
BDOS EQU 5 ;BDOS Entry Point (for system reset)
|
||||
;
|
||||
;
|
||||
;
|
||||
;
|
||||
No$Deblock$Retry:
|
||||
;----------------------------------------------------
|
||||
; Omitted code to setup disk controller command table
|
||||
; and initiate the disk operation
|
||||
;----------------------------------------------------
|
||||
JMP Wait$For$Disk$Complete
|
||||
;
|
||||
;
|
||||
Write$Physical: ;Write contents of Disk Buffer to
|
||||
; correct sector.
|
||||
MVI A,Floppy$Write$Code ;Get Write Function code
|
||||
JMP Common$Physical ;Go to common code
|
||||
Read$Physical: ;Read previously selected Sector
|
||||
; into Disk Buffer.
|
||||
MVI A,Floppy$Read$Code ;Get Read Function code
|
||||
Common$Physical:
|
||||
STA Floppy$Command ;Set command table
|
||||
|
||||
;
|
||||
Deblock$Retry: ;Re-entry point to re-try after error
|
||||
;---------------------------------------------------
|
||||
; Omitted code sets up disk controller command block
|
||||
; and initiates the disk operation
|
||||
;---------------------------------------------------
|
||||
;
|
||||
Wait$For$Disk$Complete: ;Wait until Disk Status Block indicates
|
||||
; operation has completed, then check
|
||||
; if any errors occurred.
|
||||
;On entry HL -> Disk Control byte
|
||||
XRA A ;Ensure hung flag clear
|
||||
STA Disk$Hung$Flag
|
||||
|
||||
LXI H,Disk$Timed$Out ;Setup Watchdog timer
|
||||
LXI B,Disk$Timer ;Time delay
|
||||
CALL Set$Watchdog
|
||||
Disk$Wait$Loop:
|
||||
MOV A,M ;Get control byte
|
||||
ORA A
|
||||
JZ Disk$Complete ;Operation done
|
||||
|
||||
LDA Disk$Hung$Flag ;Also check if timed out
|
||||
ORA A
|
||||
JNZ Disk$Error ;Will be set to 40H
|
||||
|
||||
JMP Disk$Wait$Loop
|
||||
|
||||
Disk$Timed$Out: ;Control arrives here from Watchdog
|
||||
; routine itself - so this is effectively
|
||||
; part of the interrupt service routine.
|
||||
MVI A,40H ;Set Disk Hung error code
|
||||
STA Disk$Hung$Flag ; into error flag to pull
|
||||
; control out of loop
|
||||
RET ;Return to Watchdog routine
|
||||
|
||||
Disk$Complete:
|
||||
LXI B,0 ;Reset Watchdog timer
|
||||
;HL is irrelevant here
|
||||
CALL Set$Watchdog
|
||||
|
||||
LDA Disk$Status$Block ;Complete - now check status
|
||||
CPI 80H ;Check if any errors occurred
|
||||
JC Disk$Error ;Yes
|
||||
;
|
||||
Disk$Error$Ignore:
|
||||
XRA A ;No
|
||||
STA Disk$Error$Flag ;Clear error flag
|
||||
RET
|
||||
|
||||
;
|
||||
; Disk Error Message handling
|
||||
;
|
||||
;
|
||||
Disk$Error$Messages: ;This table is scanned, comparing the
|
||||
; Disk Error Status with those in the
|
||||
; table. Given a match, or even when
|
||||
; then end of the table is reached, the
|
||||
; address following the status value
|
||||
; points to the correct advisory message text.
|
||||
; Following this is the address of an
|
||||
; error description message.
|
||||
DB 40H
|
||||
DW Disk$Advice1,Disk$Msg$40
|
||||
DB 41H
|
||||
DW Disk$Advice2,Disk$Msg$41
|
||||
DB 42H
|
||||
DW Disk$Advice3,Disk$Msg$42
|
||||
DB 21H
|
||||
DW Disk$Advice4,Disk$Msg$21
|
||||
DB 22H
|
||||
DW Disk$Advice5,Disk$Msg$22
|
||||
DB 23H
|
||||
DW Disk$Advice5,Disk$Msg$23
|
||||
DB 24H
|
||||
DW Disk$Advice6,Disk$Msg$24
|
||||
DB 25H
|
||||
DW Disk$Advice6,Disk$Msg$25
|
||||
DB 11H
|
||||
DW Disk$Advice7,Disk$Msg$11
|
||||
DB 12H
|
||||
DW Disk$Advice7,Disk$Msg$12
|
||||
DB 13H
|
||||
DW Disk$Advice7,Disk$Msg$13
|
||||
DB 14H
|
||||
DW Disk$Advice7,Disk$Msg$14
|
||||
DB 15H
|
||||
DW Disk$Advice7,Disk$Msg$15
|
||||
DB 16H
|
||||
DW Disk$Advice7,Disk$Msg$16
|
||||
DB 0 ;<== Terminator
|
||||
DW Disk$Advice7,Disk$Msg$Unknown ;Unmatched code
|
||||
;
|
||||
DEM$Entry$Size EQU 5 ;Entry size in Error Message Table
|
||||
;
|
||||
;
|
||||
; Message Texts
|
||||
;
|
||||
Disk$Msg$40: DB 'Hung',0 ;Timeout message
|
||||
Disk$Msg$41: DB 'Not Ready',0
|
||||
Disk$Msg$42: DB 'Write Protected',0
|
||||
Disk$Msg$21: DB 'Data',0
|
||||
Disk$Msg$22: DB 'Format',0
|
||||
Disk$Msg$23: DB 'Missing Data Mark',0
|
||||
Disk$Msg$24: DB 'Bus Timeout',0
|
||||
Disk$Msg$25: DB 'Controller Timeout',0
|
||||
Disk$Msg$11: DB 'Drive Address',0
|
||||
Disk$Msg$12: DB 'Head Address',0
|
||||
Disk$Msg$13: DB 'Track Address',0
|
||||
Disk$Msg$14: DB 'Sector Address',0
|
||||
Disk$Msg$15: DB 'Bus Address',0
|
||||
Disk$Msg$16: DB 'Illegal Command',0
|
||||
Disk$Msg$Unknown: DB 'Unknown',0
|
||||
;
|
||||
Disk$EM$1: ;Main disk error message - part 1
|
||||
DB BELL,CR,LF
|
||||
DB 'Disk ',0
|
||||
;
|
||||
;Error Text output next
|
||||
;
|
||||
Disk$EM$2: ;Main disk error message - part 2
|
||||
DB ' Error ('
|
||||
Disk$EM$Status: DB 0,0 ;Status code in Hex
|
||||
DB ')',CR,LF,' Drive '
|
||||
Disk$EM$Drive: DB 0 ;Disk Drive code, A,B...
|
||||
DB ', Head '
|
||||
Disk$EM$Head: DB 0 ;Head number
|
||||
DB ', Track '
|
||||
Disk$EM$Track: DB 0,0 ;Track number
|
||||
DB ', Sector '
|
||||
Disk$EM$Sector: DB 0,0 ;Sector number
|
||||
DB ', Operation - '
|
||||
DB 0 ;Terminator
|
||||
;
|
||||
Disk$EM$Read: DB 'Read.',0 ;Operation names
|
||||
Disk$EM$Write: DB 'Write.',0
|
||||
;
|
||||
Disk$Advice0: DB CR,LF,' ',0
|
||||
Disk$Advice1: DB 'Check disk loaded, Retry',0
|
||||
Disk$Advice2: DB 'Possible hardware problem',0
|
||||
Disk$Advice3: DB 'Write enable if correct disk, Retry',0
|
||||
Disk$Advice4: DB 'Retry several times',0
|
||||
Disk$Advice5: DB 'Reformat disk or use another disk',0
|
||||
Disk$Advice6: DB 'Hardware error, Retry',0
|
||||
Disk$Advice7: DB 'Hardware or Software error, Retry',0
|
||||
;
|
||||
Disk$Advice9: DB ', or call for help if error persists',CR,LF
|
||||
;
|
||||
Disk$Action$Confirm:
|
||||
DB 0 ;Set to character entered by user
|
||||
DB CR,LF,0
|
||||
;
|
||||
; Disk Error Processor
|
||||
;
|
||||
; This routine builds and outputs an error message.
|
||||
; The user is then given the opportunity to :
|
||||
;
|
||||
; R - Retry the operation that caused the error.
|
||||
; I - Ignore the error and attempt to continue.
|
||||
; A - Abort the program and return to CP/M.
|
||||
;
|
||||
Disk$Error:
|
||||
PUSH PSW ;Preserve error code from controller
|
||||
LXI H,Disk$EM$Status ;Convert code for message
|
||||
CALL CAH ;Converts A to hex
|
||||
|
||||
LDA In$Buffer$Disk ;Convert disk id. for message
|
||||
ADI 'A' ;Make into letter
|
||||
STA Disk$EM$Drive
|
||||
|
||||
LDA Floppy$Head ;Convert head number
|
||||
ADI '0'
|
||||
STA Disk$EM$Head
|
||||
|
||||
LDA Floppy$Track ;Convert track number
|
||||
LXI H,Disk$EM$Track
|
||||
CALL CAH
|
||||
|
||||
LDA Floppy$Sector ;Convert sector number
|
||||
LXI H,Disk$EM$Sector
|
||||
CALL CAH
|
||||
|
||||
LXI H,Disk$EM$1 ;Output first part of message
|
||||
CALL Output$Error$Message
|
||||
|
||||
POP PSW ;Recover error status code
|
||||
MOV B,A ;For comparisons
|
||||
LXI H,Disk$Error$Messages - DEM$Entry$Size
|
||||
;HL -> Table - one entry
|
||||
LXI D,DEM$Entry$Size ;For loop below
|
||||
Disk$Error$Next$Code:
|
||||
DAD D ;Move to next (or first) entry
|
||||
|
||||
MOV A,M ;Get code number from table
|
||||
ORA A ;Check if end of table
|
||||
JZ Disk$Error$Matched ;Yes - pretend a match occurred
|
||||
CMP B ;Compare to actual code
|
||||
JZ Disk$Error$Matched ;Yes - exit from loop
|
||||
JMP Disk$Error$Next$Code ;Check next code
|
||||
;
|
||||
Disk$Error$Matched:
|
||||
INX H ;HL -> Advisory text address
|
||||
MOV E,M
|
||||
INX H
|
||||
MOV D,M ;DE -> Advisory test
|
||||
PUSH D ;Save for later
|
||||
|
||||
INX H ;HL -> Message text address
|
||||
MOV E,M ;Get Address into DE
|
||||
INX H
|
||||
MOV D,M
|
||||
|
||||
XCHG ;HL -> Text
|
||||
CALL Output$Error$Message ;Display explanatory text
|
||||
|
||||
LXI H,Disk$EM$2 ;Display second part of message
|
||||
CALL Output$Error$Message
|
||||
|
||||
LXI H,Disk$EM$Read ;Choose operation text
|
||||
; (assume a read)
|
||||
LDA Floppy$Command ;Get controller command
|
||||
CPI Floppy$Read$Code
|
||||
JZ Disk$Error$Read ;Yes
|
||||
LXI H,Disk$EM$Write ;No - change address in HL
|
||||
Disk$Error$Read:
|
||||
CALL Output$Error$Message ;Display operation type
|
||||
|
||||
LXI H,Disk$Advice0 ;Display leading blanks
|
||||
CALL Output$Error$Message
|
||||
|
||||
POP H ;Recover Advisory text pointer
|
||||
CALL Output$Error$Message
|
||||
|
||||
LXI H,Disk$Advice9 ;Display trailing component
|
||||
CALL Output$Error$Message
|
||||
;
|
||||
Disk$Error$Request$Action: ;Ask the user what to do next
|
||||
CALL Request$User$Choice ;Display prompt and get single
|
||||
; character response (folded to
|
||||
; upper case)
|
||||
CPI 'R' ;Retry?
|
||||
JZ Disk$Error$Retry
|
||||
CPI 'A' ;Abort
|
||||
JZ System$Reset
|
||||
CPI 'I' ;Ignore
|
||||
JZ Disk$Error$Ignore
|
||||
JMP Disk$Error$Request$Action
|
||||
;
|
||||
Disk$Error$Retry: ;The decision on where to return to
|
||||
; depends on whether the operation
|
||||
; failed on a deblocked or
|
||||
; non-deblocked drive
|
||||
LDA Deblocking$Required
|
||||
ORA A
|
||||
JNZ Deblock$Retry
|
||||
JMP No$Deblock$Retry
|
||||
;
|
||||
System$Reset: ;This is a radical approach, but
|
||||
; it does cause CP/M to restart
|
||||
MVI C,0 ;System Reset
|
||||
CALL BDOS
|
||||
|
||||
;
|
||||
; Omitted subroutines (listed in full in Figure 8-10)
|
||||
;
|
||||
Set$Watchdog: ;Set Watchdog timer (to number of "ticks" in BC, and
|
||||
; to transfer control to (HL) if timer hits zero).
|
||||
CAH: ;Convert A to two ASCII hex characters, storing
|
||||
; the output in (HL) and (HL+1)
|
||||
Output$Error$Message: ;Display the 00-byte terminated error message
|
||||
; pointed to by HL. Output is directed only to
|
||||
; those console devices not being used for list
|
||||
; output as well.
|
||||
Request$User$Choice: ;Display prompt "Enter R, A, I..." and return
|
||||
; single keyboard character (upper case) in A
|
||||
RET ;Dummy
|
||||
|
||||
167
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIND.C
Normal file
167
CONTRIBUTIONS/cpm-handbook/cpmsrc/FIND.C
Normal file
@@ -0,0 +1,167 @@
|
||||
#define VN "1.0 02/11/83"
|
||||
/* FIND - This utility displays either a map showing on which disks and
|
||||
in which user numbers files matching the specified ambiguous
|
||||
file name are found, or it can display the actual names matched. */
|
||||
|
||||
#include <LIBRARY.H>
|
||||
|
||||
struct _dirpb dir_pb; /* directory management parameter block */
|
||||
struct _dir *dir_entry; /* pointer to directory entry (somewhere in
|
||||
dir_pb) */
|
||||
struct _scb scb; /* search control block */
|
||||
|
||||
char file_name[20]; /* formatted for display : un/d:FILENAME.TYP */
|
||||
|
||||
short cur_disk; /* current logical disk at start of program */
|
||||
int mcount; /* match count (no. of file names matched) */
|
||||
int dmcount; /* per disk match count */
|
||||
int lcount; /* line count (for lines displayed) */
|
||||
|
||||
int map_flag; /* 0 = show file names of matched files,
|
||||
NZ = show map of number of files */
|
||||
|
||||
/* The array below is used to tabulate the results for each
|
||||
disk drive, and for each user number on the drive.
|
||||
In addition, two extra "users" have been added for "Free"
|
||||
and "Used" values. */
|
||||
|
||||
unsigned disk_map[16][18]; /* Disk A -> P, Users 0 -> 15, Free, Used */
|
||||
#define USED_COUNT 16 /* "user" number for Used entities */
|
||||
#define FREE_COUNT 17 /* "user" number for Free entities */
|
||||
|
||||
|
||||
main(argc,argv)
|
||||
short argc; /* argument count */
|
||||
char *argv[]; /* argument vector (pointer to an array of chars) */
|
||||
{
|
||||
|
||||
printf("\nFIND Version %s (Library %s)",VN,LIBVN);
|
||||
chk_use(argc); /* check usage */
|
||||
cur_disk = bdos(GETDISK); /* get current default disk */
|
||||
|
||||
dm_clr(disk_map); /* reset disk map */
|
||||
|
||||
/* set search control block
|
||||
disks, name, type, user number, extent number,
|
||||
and number of bytes to compare - in this case, match all users,
|
||||
but only extent 0 */
|
||||
setscb(scb,argv[1],'?',0,13); /* set disks, name, type */
|
||||
|
||||
map_flag = usstrcmp("NAMES",argv[2]); /* set flag for map option */
|
||||
|
||||
lcount = dmcount = mcount = 0; /* initialize counts */
|
||||
|
||||
for (scb.scb_disk = 0; /* starting with logical disk A: */
|
||||
scb.scb_disk < 16; /* until logical disk P: */
|
||||
scb.scb_disk++) /* move to next logical disk */
|
||||
{
|
||||
|
||||
/* check if current disk has been selected for search */
|
||||
if (!(scb.scb_adisks & (1 << scb.scb_disk)))
|
||||
continue; /* no - so bypass this disk */
|
||||
|
||||
printf("\nSearching disk : %c",(scb.scb_disk + 'A'));
|
||||
lcount++; /* update line count */
|
||||
|
||||
dir_pb.dp_disk = scb.scb_disk; /* set to disk to be searched*/
|
||||
dmcount = 0; /* reset disk matched count */
|
||||
|
||||
if (!map_flag) /* if file names are to be displayed */
|
||||
putchar('\n'); /* move to column 1 */
|
||||
|
||||
/* set the directory to "closed", and force the get_nde
|
||||
function to open it. */
|
||||
dir_pb.dp_open = 0;
|
||||
|
||||
/* while not at the end of the directory, set a pointer to the
|
||||
next directory entry. */
|
||||
while(dir_entry = get_nde(dir_pb))
|
||||
{
|
||||
/* check if entry in use in order to update
|
||||
the free/used counts */
|
||||
|
||||
if (dir_entry -> de_userno == 0xE5) /* unused */
|
||||
disk_map[scb.scb_disk][FREE_COUNT]++;
|
||||
else /* in use */
|
||||
disk_map[scb.scb_disk][USED_COUNT]++;
|
||||
|
||||
/* select only those active entries that are the
|
||||
first extent (numbered 0) of a file that matches
|
||||
the name supplied by the user. */
|
||||
if (
|
||||
(dir_entry -> de_userno != 0xE5) &&
|
||||
(dir_entry -> de_extent == 0) &&
|
||||
(comp_fname(scb,dir_entry) == NAME_EQ)
|
||||
)
|
||||
{
|
||||
|
||||
mcount++; /* update matched counts */
|
||||
dmcount++; /* per disk count */
|
||||
|
||||
if (map_flag) /* check map option */
|
||||
{
|
||||
/* update disk map */
|
||||
disk_map[scb.scb_disk][dir_entry -> de_userno]++;
|
||||
}
|
||||
else /* display names */
|
||||
{
|
||||
conv_dfname(scb.scb_disk,dir_entry,file_name);
|
||||
printf("%s ",file_name);
|
||||
|
||||
/* check if need to start new line */
|
||||
if (!(dmcount % 4))
|
||||
{
|
||||
putchar('\n');
|
||||
if (++lcount > 18)
|
||||
{
|
||||
lcount = 0;
|
||||
printf("\nPress Space Bar to continue....");
|
||||
getchar();
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} /* end of directory */
|
||||
} /* all disks searched */
|
||||
|
||||
if (map_flag)
|
||||
{
|
||||
printf("\n Numbers show files in each User Number.");
|
||||
printf("\n --- User Numbers --- Dir. Entries");
|
||||
|
||||
dm_disp(disk_map,scb.scb_adisks); /* display disk map */
|
||||
}
|
||||
|
||||
if (mcount == 0)
|
||||
printf("\n --- File Not Found --- ");
|
||||
|
||||
bdos(SETDISK,cur_disk); /* reset to current disk */
|
||||
}
|
||||
|
||||
|
||||
chk_use(argc) /* check usage */
|
||||
/* This function checks that the correct number of
|
||||
parameters has been specified, outputting instructions
|
||||
if not.
|
||||
*/
|
||||
/* Entry Parameter */
|
||||
int argc; /* Count of the number of arguments on the command line */
|
||||
{
|
||||
|
||||
/* The minimum value of argc is 1 (for the program name itself),
|
||||
so argc is always one greater than the number of parameters
|
||||
on the command line */
|
||||
|
||||
if (argc == 1 || argc > 3)
|
||||
{
|
||||
printf("\nUsage :");
|
||||
printf("\n\tFIND d:filename.typ {NAMES}");
|
||||
printf("\n\t *:filename.typ (All disks)");
|
||||
printf("\n\t ABCD..OP:filename.typ (Selected Disks)");
|
||||
printf("\n\tNAMES option shows actual names rather than map.");
|
||||
exit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
149
CONTRIBUTIONS/cpm-handbook/cpmsrc/FUNKEY.C
Normal file
149
CONTRIBUTIONS/cpm-handbook/cpmsrc/FUNKEY.C
Normal file
@@ -0,0 +1,149 @@
|
||||
#define VN "\nFUNKEY Vn 1.0 02/18/83"
|
||||
|
||||
#include <LIBRARY.H>
|
||||
|
||||
|
||||
int fnum; /* function key number to be programmed */
|
||||
char fstring[20]; /* string for function key */
|
||||
struct _fkt *pfk; /* pointer to function key table */
|
||||
|
||||
main(argc,argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
|
||||
if (argc == 1 || argc > 3)
|
||||
show_use();
|
||||
|
||||
pfk = get_cba(CB_FKT); /* set pointer to function key table */
|
||||
|
||||
if (usstrcmp("SHOW",argv[1]))
|
||||
{
|
||||
if (!isdigit(argv[1][0]))
|
||||
{
|
||||
printf("\n\007'%s' is an illegal function key.",
|
||||
argv[1]);
|
||||
show_use();
|
||||
}
|
||||
|
||||
fnum = atoi(argv[1]); /* convert function key number */
|
||||
|
||||
if (fnum > FK_ENTRIES)
|
||||
{
|
||||
printf("\n\007Function key number %d too large.",fnum);
|
||||
show_use();
|
||||
}
|
||||
|
||||
if (get_fs(fstring) > FK_LENGTH)
|
||||
{
|
||||
printf("\n\007Function key string is too long.");
|
||||
show_use();
|
||||
}
|
||||
|
||||
|
||||
pfk += fnum; /* update pointer to string */
|
||||
/* copy string into function key table */
|
||||
|
||||
/* check if function key input present */
|
||||
if (!(pfk -> fk_input[0]))
|
||||
{
|
||||
printf("\n\007Error : Function Key #%d is not set up to be programmed.",fnum);
|
||||
show_use();
|
||||
}
|
||||
strcpy(pfk -> fk_output,fstring);
|
||||
}
|
||||
else /* SHOW function specified */
|
||||
{
|
||||
printf(VN); /* display signon message */
|
||||
show_fun();
|
||||
}
|
||||
}
|
||||
|
||||
get_fs(string) /* get function string from command tail */
|
||||
char string[]; /* pointer to character string */
|
||||
{
|
||||
char *tail; /* pointer to command tail */
|
||||
short tcount; /* count of TOTAL characters in command tail */
|
||||
int slen; /* string length */
|
||||
|
||||
tail = 0x80; /* command line is in memory at 0080H */
|
||||
tcount = *tail++; /* set TOTAL count of characters in command tail */
|
||||
slen = 0; /* initialize string length */
|
||||
|
||||
while(tcount--) /* for all characters in the command tail */
|
||||
{
|
||||
if (*tail++ == '"') /* scan for first quotes */
|
||||
break;
|
||||
}
|
||||
if (!tcount) /* no quotes found */
|
||||
{
|
||||
printf("\n\007No leading quotes found.");
|
||||
show_use();
|
||||
}
|
||||
|
||||
++tcount; /* adjust tail count */
|
||||
while(tcount--) /* for all remaining characters in tail */
|
||||
{
|
||||
if (*tail == '"')
|
||||
{
|
||||
string[slen] = '\0'; /* add terminator */
|
||||
break; /* exit from loop */
|
||||
}
|
||||
string[slen] = *tail++; /* move char. from tail into string */
|
||||
|
||||
if (string[slen] == '<')
|
||||
string[slen] = 0x0A;
|
||||
++slen;
|
||||
}
|
||||
if (!tcount) /* no terminating quotes found */
|
||||
{
|
||||
printf("\n\007No trailing quotes found.");
|
||||
show_use();
|
||||
}
|
||||
return slen; /* return string length */
|
||||
}
|
||||
|
||||
|
||||
show_fun() /* display settings for all function keys */
|
||||
{
|
||||
struct _fkt *pfkt; /* local pointer to function keys */
|
||||
int count; /* count to access function keys */
|
||||
char *lf; /* pointer to '<' character (line feed) */
|
||||
|
||||
pfkt = get_cba(CB_FKT); /* set pointer to function key table */
|
||||
|
||||
for (count = 0; count <= FK_ENTRIES; count++)
|
||||
{
|
||||
if (pfkt -> fk_input[0]) /* key is programmed */
|
||||
{
|
||||
/* check if at physical end of table */
|
||||
if (pfkt -> fk_input == 0xFF)
|
||||
break; /* yes - break out of for-loop */
|
||||
strcpy(fstring,pfkt -> fk_output);
|
||||
/* convert all 0x0A chars to '<' */
|
||||
while (lf = strscn(fstring,"\012"))
|
||||
{
|
||||
*lf = '<';
|
||||
}
|
||||
|
||||
printf("\n\tKey #%d = '%s'",count,fstring);
|
||||
}
|
||||
++pfkt; /* move to next entry */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
show_use()
|
||||
{
|
||||
printf("\nFUNKEY sets a specific function key string.");
|
||||
printf("\n\tFUNKEY key-number \042string to be programmed<\042 ");
|
||||
printf("\n\t (Note : '<' is changed to line feed.)");
|
||||
printf("\n\t ( key-number is from 0 to %d.)",
|
||||
FK_ENTRIES-1);
|
||||
printf("\n\t ( string can be up to %d chars.)",
|
||||
FK_LENGTH);
|
||||
printf("\n\tFUNKEY SHOW (displays settings for all keys)");
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
1363
CONTRIBUTIONS/cpm-handbook/cpmsrc/LIBRARY.C
Normal file
1363
CONTRIBUTIONS/cpm-handbook/cpmsrc/LIBRARY.C
Normal file
File diff suppressed because it is too large
Load Diff
322
CONTRIBUTIONS/cpm-handbook/cpmsrc/LIBRARY.H
Normal file
322
CONTRIBUTIONS/cpm-handbook/cpmsrc/LIBRARY.H
Normal file
@@ -0,0 +1,322 @@
|
||||
#define LIBVN "1.0" /* Library Version Number */
|
||||
/* 12:36 02/21/83 */
|
||||
|
||||
/* This file contains groups of useful definitions.
|
||||
It should be included at the front of any programs
|
||||
that use the functions in BLIB. */
|
||||
|
||||
|
||||
/* Definitions to make minor language modifications to C. */
|
||||
#define short char /* Short is not supported directly */
|
||||
|
||||
/* one of the functions (bv_make) in the library uses the BDS C
|
||||
function, alloc, to allocate memory. The following definitions
|
||||
are provided for alloc. */
|
||||
|
||||
struct _header /* header for block of memory allocated */
|
||||
{
|
||||
struct _header *_ptr; /* pointer to the next header in the chain */
|
||||
unsigned _size; /* number of bytes in the allocated block */
|
||||
};
|
||||
struct _header _base; /* declare the first header of the chain */
|
||||
struct _header *_allocp; /* used by alloc() and free() functions */
|
||||
|
||||
|
||||
/* BDOS Function Call Numbers. */
|
||||
|
||||
#define SETDISK 14 /* Set (Select) Disk */
|
||||
#define SEARCHF 17 /* Search First */
|
||||
#define SEARCHN 18 /* Search Next */
|
||||
#define DELETEF 19 /* Delete File */
|
||||
#define GETDISK 25 /* Get Default Disk (Currently Logged In) */
|
||||
#define SETDMA 26 /* Set DMA (Read/Write) address */
|
||||
#define GETDPARM 31 /* Get Disk Parameter Block address */
|
||||
#define GETUSER 32 /* Get Current User Number */
|
||||
#define SETUSER 32 /* Set Current User Number */
|
||||
|
||||
|
||||
/* Direct BIOS Calls.
|
||||
These definitions are for direct calls into the BIOS.
|
||||
WARNING - Using these makes program less transportable.
|
||||
Each symbol is related to its corresponding Jump in the
|
||||
BIOS Jump Vector.
|
||||
Only the more useful entries are defined. */
|
||||
|
||||
#define CONST 2 /* Console Status */
|
||||
#define CONIN 3 /* Console Input */
|
||||
#define CONOUT 4 /* Console Output */
|
||||
#define LIST 5 /* List Output */
|
||||
#define AUXOUT 6 /* Auxiliary Output */
|
||||
#define AUXIN 7 /* Auxiliary Input */
|
||||
|
||||
#define HOME 8 /* Home disk */
|
||||
#define SELDSK 9 /* Select logical disk */
|
||||
#define SETTRK 10 /* Set track */
|
||||
#define SETSEC 11 /* Set sector */
|
||||
#define SETDMA 12 /* Set DMA Address */
|
||||
#define DREAD 13 /* Disk read */
|
||||
#define DWRITE 14 /* Disk write */
|
||||
#define LISTST 15 /* List status */
|
||||
#define SECTRN 16 /* Sector translate */
|
||||
#define AUXIST 17 /* Auxiliary Input Status */
|
||||
#define AUXOST 18 /* Auxiliary Output Status */
|
||||
|
||||
/* "Private" entries in Jump Vector */
|
||||
#define CIOINIT 19 /* Specific Character I/O Initialization */
|
||||
#define SETDOG 20 /* Set Watchdog timer */
|
||||
#define CBGADDR 21 /* Configuration Block, Get Address */
|
||||
|
||||
|
||||
/* defines for accessing the Configuration Block */
|
||||
|
||||
#define CB_GET 21 /* bios jump number to access routine */
|
||||
#define DEV_INIT 19 /* bios jump to do device initialization */
|
||||
|
||||
#define CB_DATE 0 /* date in ASCII */
|
||||
#define CB_TIMEA 1 /* time in ASCII */
|
||||
#define CB_DTFLAGS 2 /* date, time flags */
|
||||
#define TIME_SET 0x01 /* this bit NZ means date has been set */
|
||||
#define DATE_SET 0x02 /* this bit NZ means time has been set */
|
||||
|
||||
#define CB_FIP 3 /* forced input pointer */
|
||||
#define CB_SUM 4 /* system startup message */
|
||||
|
||||
#define CB_CI 5 /* console input */
|
||||
#define CB_CO 6 /* console output */
|
||||
#define CB_AI 7 /* auxiliary input */
|
||||
#define CB_AO 8 /* auxiliary output */
|
||||
#define CB_LI 9 /* list input */
|
||||
#define CB_LO 10 /* list output */
|
||||
|
||||
#define CB_DTA 11 /* device table addresses */
|
||||
#define CB_C1224 12 /* clock 12/24 format flag */
|
||||
#define CB_RTCTR 13 /* real time clock tick rate (per second) */
|
||||
|
||||
#define CB_WDC 14 /* watchdog count */
|
||||
#define CB_WDA 15 /* watchdog address */
|
||||
|
||||
#define CB_FKT 16 /* function key table */
|
||||
#define CB_COET 17 /* console output escape table */
|
||||
|
||||
#define CB_D0_IS 18 /* device 0 initialization stream */
|
||||
#define CB_D0_BRC 19 /* device 0 baud rate constant */
|
||||
|
||||
#define CB_D1_IS 20 /* device 1 initialization stream */
|
||||
#define CB_D1_BRC 21 /* device 1 baud rate constant */
|
||||
|
||||
#define CB_D2_IS 22 /* device 2 initialization stream */
|
||||
#define CB_D2_BRC 23 /* device 2 baud rate constant */
|
||||
|
||||
#define CB_IV 24 /* interrupt vector */
|
||||
#define CB_LTCBO 25 /* long term config. block offset */
|
||||
#define CB_LTCBL 26 /* long term config. block length */
|
||||
|
||||
#define CB_PUBF 27 /* public files flag */
|
||||
#define CB_MCBUF 28 /* multi-command buffer */
|
||||
#define CB_POLLC 29 /* polled console flag */
|
||||
|
||||
|
||||
/* device numbers and names for physical devices */
|
||||
/* NOTE : Change these definitions for your computer system */
|
||||
|
||||
#define T_DEVN 0 /* Terminal */
|
||||
#define M_DEVN 1 /* Modem */
|
||||
#define P_DEVN 2 /* Printer */
|
||||
|
||||
#define MAXPDEV 2 /* maximum physical device number */
|
||||
|
||||
/* names for the physical devices */
|
||||
#define PN_T "TERMINAL"
|
||||
#define PN_M "MODEM"
|
||||
#define PN_P "PRINTER"
|
||||
|
||||
/* structure and definitions for function keys */
|
||||
|
||||
#define FK_ILENGTH 2 /* number of chars. input when f-key pressed
|
||||
NOTE : This does NOT include the ESCAPE */
|
||||
#define FK_LENGTH 16 /* length of string (not including fk_term) */
|
||||
#define FK_ENTRIES 18 /* number of function key entries in table */
|
||||
|
||||
struct _fkt /* function key table */
|
||||
{
|
||||
char fk_input[FK_ILENGTH]; /* lead-in character is not in table */
|
||||
char fk_output[FK_LENGTH]; /* output character string */
|
||||
char fk_term; /* safety terminating character */
|
||||
};
|
||||
|
||||
|
||||
/* definitions and structure for device tables */
|
||||
|
||||
/* protocol bits */
|
||||
/* Note : if the most significant bit is
|
||||
set = 1, then the set_proto function
|
||||
will logically OR in the value. This
|
||||
permits Input DTR to co-exist with
|
||||
Xon or Etx protocol. */
|
||||
|
||||
#define DT_ODTR 0x8004 /* output dtr high to send (OR'ed in) */
|
||||
#define DT_OXON 0x0008 /* output xon */
|
||||
#define DT_OETX 0x0010 /* output etx/ack */
|
||||
|
||||
#define DT_IRTS 0x8040 /* input RTS (OR'ed in) */
|
||||
#define DT_IXON 0x0080 /* input xon */
|
||||
|
||||
#define ALLPROTO 0xDC /* all protocols combined */
|
||||
|
||||
struct _dt /* device table */
|
||||
{
|
||||
char dt_f1[14]; /* filler */
|
||||
char dt_st1; /* status byte 1 - has protocol flags */
|
||||
char dt_st2; /* status byte 2 */
|
||||
unsigned dt_f2; /* filler */
|
||||
unsigned dt_etxml; /* etx/ack message length */
|
||||
char dt_f3[12]; /* filler */
|
||||
} ;
|
||||
|
||||
|
||||
/* Values returned by the comp_fname (compare file name) */
|
||||
|
||||
#define NAME_EQ 0 /* names equal */
|
||||
#define NAME_LT 1 /* name less than mask */
|
||||
#define NAME_GT 2 /* name greater than mask */
|
||||
#define NAME_NE 3 /* name not equal (and comparison ambiguous) */
|
||||
|
||||
|
||||
/* Structure for Standard CP/M File Control Block */
|
||||
|
||||
#define FCBSIZE 36 /* define the overall length of an FCB */
|
||||
|
||||
struct _fcb
|
||||
{
|
||||
short fcb_disk; /* logical disk (0 = default) */
|
||||
char fcb_fname[11]; /* file name, type (with attributes) */
|
||||
short fcb_extent; /* current extent */
|
||||
unsigned fcb_s12; /* reserved for CP/M */
|
||||
short fcb_reccnt; /* record count used in current extent */
|
||||
union /* allocation blocks can be either */
|
||||
{ /* single or double bytes */
|
||||
short fcbab_short[16];
|
||||
unsigned fcbab_long[8];
|
||||
} _fcbab;
|
||||
short fcb_currec; /* current record within extent */
|
||||
char fcb_ranrec[3]; /* record for random read/write */
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/* parameter block used for calls to the directory management routines */
|
||||
|
||||
#define DIR_BSZ 128 /* directory buffer size */
|
||||
|
||||
struct _dirpb
|
||||
{
|
||||
short dp_open; /* 0 to request directory to be opened */
|
||||
short dp_end; /* NZ when at end of directory */
|
||||
short dp_write; /* NZ to write current sector to disk */
|
||||
struct _dir *dp_entry; /* pointer to directory entry in buffer */
|
||||
char dp_buffer [DIR_BSZ]; /* directory sector buffer */
|
||||
char dp_disk; /* current logical disk */
|
||||
int dp_track; /* start track */
|
||||
int dp_sector; /* start sector */
|
||||
int dp_nument; /* number of directory entries */
|
||||
int dp_entrem; /* entries remaining to process */
|
||||
int dp_sptrk; /* number of sectors per track */
|
||||
int dp_nabpde; /* number of allocation blocks per dir. entry */
|
||||
unsigned dp_nab; /* number of allocation blocks */
|
||||
int dp_absize; /* allocation block size (in Kbytes) */
|
||||
};
|
||||
|
||||
/* The err_dir function is used to report errors found by the
|
||||
directory management routines, open_dir and rw_dir.
|
||||
Err_dir needs an parameter to define the operation being
|
||||
performed when the error occurred. The following #defines
|
||||
represent the operations possible. */
|
||||
|
||||
#define W_DIR 0 /* Writing Directory */
|
||||
#define R_DIR 1 /* Reading Directory */
|
||||
#define O_DIR 2 /* Opening Directory */
|
||||
|
||||
|
||||
/* disk parameter block maintained by CPM */
|
||||
|
||||
struct _dpb
|
||||
{
|
||||
unsigned dpb_sptrk; /* sectors per track */
|
||||
short dpb_bshift; /* block shift */
|
||||
short dpb_bmask; /* block mask */
|
||||
short dpb_emask; /* extent mask */
|
||||
unsigned dpb_maxabn; /* maximum allocation block number */
|
||||
unsigned dpb_maxden; /* maximum directory entry number */
|
||||
short dpb_rab0; /* allocation blocks reserved for */
|
||||
short dpb_rab1; /* directory blocks */
|
||||
unsigned dpb_diskca; /* disk changed workarea */
|
||||
unsigned dpb_trkoff; /* track offset */
|
||||
};
|
||||
|
||||
|
||||
/* disk directory entry format */
|
||||
|
||||
struct _dir {
|
||||
char de_userno; /* user number or 0xE5 if free entry */
|
||||
char de_fname[11]; /* file name [8] and type [3] */
|
||||
int de_extent; /* extent number of this entry */
|
||||
int de_reccnt; /* number of 128-byte records used in last
|
||||
allocation block */
|
||||
union /* allocation blocks can be either */
|
||||
{ /* single or double bytes */
|
||||
short de_short[16];
|
||||
unsigned de_long[8];
|
||||
} _dirab;
|
||||
};
|
||||
|
||||
/* disk request parameters for BIOS-level Read/Writes */
|
||||
|
||||
struct _drb
|
||||
{
|
||||
short dr_disk; /* logical disk A = 0, B = 1... */
|
||||
unsigned dr_track; /* track (for SETTRK) */
|
||||
unsigned dr_sector; /* sector (for SETSEC) */
|
||||
char *dr_buffer; /* buffer address (for SETDMA) */
|
||||
} ;
|
||||
|
||||
|
||||
/* search control block used by directory scanning functions */
|
||||
struct _scb
|
||||
{
|
||||
short scb_userno; /* user number(s) to match */
|
||||
char scb_fname[11]; /* file name and type */
|
||||
short scb_extent; /* extent number */
|
||||
char unused[19]; /* dummy bytes to make this appear like
|
||||
a file control block */
|
||||
short scb_length; /* number of bytes to compare */
|
||||
short scb_disk; /* current disk to be searched */
|
||||
unsigned scb_adisks; /* bit map of disks to be searched.
|
||||
The right-most bit is for disk A:. */
|
||||
} ;
|
||||
|
||||
|
||||
/* code table related definitions */
|
||||
|
||||
#define CT_SNF 0xFFFF /* String Not Found */
|
||||
|
||||
struct _ct /* define structure of code table */
|
||||
{
|
||||
unsigned _ct_code; /* code value */
|
||||
char *_ct_sp; /* string pointer */
|
||||
};
|
||||
|
||||
|
||||
/* Structure for bit-vectors */
|
||||
|
||||
struct _bv
|
||||
{
|
||||
unsigned bv_bytes; /* number of bytes in the vector */
|
||||
char *bv_bits; /* pointer to the first byte in the vector */
|
||||
char *bv_end; /* pointer to byte following bit vector */
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
/* End of LIBRARY.H */
|
||||
|
||||
187
CONTRIBUTIONS/cpm-handbook/cpmsrc/MAKE.C
Normal file
187
CONTRIBUTIONS/cpm-handbook/cpmsrc/MAKE.C
Normal file
@@ -0,0 +1,187 @@
|
||||
#define VN "1.0 02/12/83"
|
||||
/* MAKE - This utility is really two very similar programs
|
||||
according to the parameter specified on the command line.
|
||||
|
||||
INVISIBLE finds all of the specified files and moves them
|
||||
to user number 0 and sets them to be System and Read Only
|
||||
status. These files can then be accessed from user numbers
|
||||
other than 0 when the public files feature is enabled in the
|
||||
BIOS.
|
||||
|
||||
VISIBLE is the "opposite" in that the specified files are
|
||||
moved to the current user number and changed to Directory
|
||||
and Read Write status. */
|
||||
|
||||
#include <LIBRARY.H>
|
||||
|
||||
struct _dirpb dir_pb; /* directory management parameter block */
|
||||
struct _dir *dir_entry; /* pointer to directory entry */
|
||||
struct _scb scb; /* search control block */
|
||||
short to_user; /* user number to which files will be set */
|
||||
short from_user; /* user number from which files will be moved */
|
||||
|
||||
char file_name[20]; /* formatted for display : un/d:FILENAME.TYP */
|
||||
short name_flag; /* NZ to display names of files moved */
|
||||
|
||||
short cur_disk; /* current logical disk at start of program */
|
||||
|
||||
int mcount; /* match count (no. of file names matched) */
|
||||
|
||||
short invisible; /* NZ when parameter specifies Invisible */
|
||||
char *operation; /* pointer to either "Invisible" or "Visible" */
|
||||
|
||||
main(argc,argv)
|
||||
short argc; /* argument count */
|
||||
char *argv[]; /* argument vector (pointer to an array of chars) */
|
||||
{
|
||||
|
||||
printf("\nMAKE Version %s (Library %s)",VN,LIBVN);
|
||||
chk_use(argc); /* check usage */
|
||||
cur_disk = bdos(GETDISK); /* get current default disk */
|
||||
mcount = 0; /* initialize count */
|
||||
|
||||
|
||||
/* set the invisible flag according to the parameter */
|
||||
if (usstrcmp("VISIBLE",argv[2]) == 0)
|
||||
invisible = 0;
|
||||
else if (usstrcmp("INVISIBLE",argv[2]) == 0)
|
||||
invisible = 1;
|
||||
else
|
||||
{
|
||||
printf("\n\007Error : '%s' can only be INVISIBLE/VISIBLE.",argv[2]);
|
||||
exit();
|
||||
}
|
||||
|
||||
/* set the from_user and to_user numbers depending on which
|
||||
program is to be built, and the parameters specified. */
|
||||
|
||||
if (invisible)
|
||||
{
|
||||
from_user = bdos(GETUSER,0xFF); /* get current user number */
|
||||
to_user = 0; /* always move files to user 0 */
|
||||
operation = "Invisible"; /* set pointer to string */
|
||||
}
|
||||
else /* visible */
|
||||
{
|
||||
from_user = 0; /* always move from user 0 */
|
||||
to_user = bdos(GETUSER,0xFF); /* get current user */
|
||||
operation = "Visible"; /* set pointer to string */
|
||||
}
|
||||
|
||||
/* set search control block disks, name, type, user number,
|
||||
extent number and number of bytes to compare - in this
|
||||
case, match the 'from' user, all extents. */
|
||||
setscb(scb,argv[1],from_user,'?',13); /* set disks, name, type */
|
||||
|
||||
name_flag = usstrcmp("NAMES",argv[3]); /* set name suppress flag from param. 3 */
|
||||
|
||||
/* to simplify the logic below, name_flag must be made
|
||||
NZ if it is equal to NAME_EQ, 0 if it is any other value */
|
||||
name_flag = (name_flag == NAME_EQ ? 1 : 0);
|
||||
|
||||
|
||||
/* convert search user number and name for output */
|
||||
conv_dfname(scb.scb_disk,scb,file_name);
|
||||
printf("\n\nMoving files from User %d to %d and making them %s.",
|
||||
from_user,to_user,operation);
|
||||
|
||||
for (scb.scb_disk = 0; /* starting with logical disk A: */
|
||||
scb.scb_disk < 16; /* until logical disk P: */
|
||||
scb.scb_disk++) /* move to next logical disk */
|
||||
{
|
||||
|
||||
/* check if current disk has been selected for search */
|
||||
if (!(scb.scb_adisks & (1 << scb.scb_disk)))
|
||||
continue; /* no - so bypass this disk */
|
||||
|
||||
printf("\nSearching disk : %c",(scb.scb_disk + 'A'));
|
||||
|
||||
dir_pb.dp_disk = scb.scb_disk; /* set to disk to be searched*/
|
||||
|
||||
if (name_flag) /* if file names are to be displayed */
|
||||
putchar('\n'); /* move to column 1 */
|
||||
|
||||
|
||||
/* set the directory to "closed", and force the get_nde
|
||||
function to open it. */
|
||||
dir_pb.dp_open = 0;
|
||||
|
||||
/* while not at the end of the directory,
|
||||
set a pointer to the next directory entry. */
|
||||
while(dir_entry = get_nde(dir_pb))
|
||||
{
|
||||
|
||||
/* match those entries that have the correct
|
||||
user number, file name, type, and any
|
||||
extent number. */
|
||||
if (
|
||||
(dir_entry -> de_userno != 0xE5) &&
|
||||
(comp_fname(scb,dir_entry) == NAME_EQ)
|
||||
)
|
||||
{
|
||||
mcount++; /* update matched counts */
|
||||
|
||||
if (invisible)
|
||||
{ /* set ms bits */
|
||||
dir_entry -> de_fname[8] |= 0x80;
|
||||
dir_entry -> de_fname[9] |= 0x80;
|
||||
}
|
||||
else /* visible */
|
||||
{ /* clear ms bits */
|
||||
dir_entry -> de_fname[8] &= 0x7F;
|
||||
dir_entry -> de_fname[9] &= 0x7F;
|
||||
}
|
||||
|
||||
/* move to correct user number */
|
||||
dir_entry -> de_userno = to_user;
|
||||
|
||||
/* indicate sector to be written back */
|
||||
dir_pb.dp_write = 1;
|
||||
|
||||
/* check if name to be displayed */
|
||||
if (name_flag)
|
||||
{
|
||||
conv_dfname(scb.scb_disk,dir_entry,file_name);
|
||||
printf("\n\t%s made %s in User %d.",
|
||||
file_name,operation,to_user);
|
||||
}
|
||||
}
|
||||
} /* all directory entries processed */
|
||||
} /* all disks processed */
|
||||
|
||||
if (mcount == 0)
|
||||
printf("\n --- No Files Processed --- ");
|
||||
|
||||
bdos(SETDISK,cur_disk); /* reset to current disk */
|
||||
}
|
||||
|
||||
|
||||
chk_use(argc) /* check usage */
|
||||
/* This function checks that the correct number of
|
||||
parameters has been specified, outputting instructions
|
||||
if not.
|
||||
*/
|
||||
/* Entry Parameter */
|
||||
int argc; /* Count of the number of arguments on the command line */
|
||||
{
|
||||
|
||||
/* The minimum value of argc is 1 (for the program name itself),
|
||||
so argc is always one greater than the number of parameters
|
||||
on the command line */
|
||||
|
||||
if (argc == 3 || argc == 4)
|
||||
return;
|
||||
else
|
||||
{
|
||||
printf("\nUsage :");
|
||||
printf("\n\tMAKE d:filename.typ INVISIBLE {NAMES}");
|
||||
printf("\n\t VISIBLE");
|
||||
printf("\n\t *:filename.typ (All disks)");
|
||||
printf("\n\t ABCD..OP:filename.typ (Selected Disks)");
|
||||
printf("\n\tNAMES option shows names of files processed.");
|
||||
exit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
182
CONTRIBUTIONS/cpm-handbook/cpmsrc/MOVE.C
Normal file
182
CONTRIBUTIONS/cpm-handbook/cpmsrc/MOVE.C
Normal file
@@ -0,0 +1,182 @@
|
||||
#define VN "1.0 02/10/83"
|
||||
/* MOVE - This utility moves file(s) from one user number to another,
|
||||
but on the SAME logical disk. Files are not actually copied -
|
||||
rather their directory entries are changed. */
|
||||
|
||||
#include <LIBRARY.H>
|
||||
|
||||
struct _dirpb dir_pb; /* directory management parameter block */
|
||||
struct _dir *dir_entry; /* pointer to directory entry */
|
||||
struct _scb scb; /* search control block */
|
||||
|
||||
|
||||
#define DIR_BSZ 128 /* directory buffer size */
|
||||
char dir_buffer[DIR_BSZ]; /* directory buffer */
|
||||
|
||||
char file_name[20]; /* formatted for display : un/d:FILENAME.TYP */
|
||||
short name_flag; /* NZ to display names of files moved */
|
||||
|
||||
short cur_disk; /* current logical disk at start of program */
|
||||
int from_user; /* user number from which to move files */
|
||||
int to_user; /* user number to which files will be moved */
|
||||
|
||||
int mcount; /* match count (no. of file names matched) */
|
||||
int dmcount; /* per disk match count */
|
||||
int lcount; /* line count (for lines displayed) */
|
||||
|
||||
|
||||
main(argc,argv)
|
||||
short argc; /* argument count */
|
||||
char *argv[]; /* argument vector (pointer to an array of chars) */
|
||||
{
|
||||
|
||||
printf("\nMOVE Version %s (Library %s)",VN,LIBVN);
|
||||
|
||||
chk_use(argc); /* check usage */
|
||||
|
||||
to_user = atoi(argv[2]); /* convert user no. to integer */
|
||||
/* set and chk destination user number */
|
||||
if(to_user > 15)
|
||||
{
|
||||
printf("\nError - the destination user number can not be greater than 15.");
|
||||
}
|
||||
|
||||
/* set the current user number */
|
||||
from_user = bdos(GETUSER,0xFF);
|
||||
|
||||
/* check if source user number specified */
|
||||
if (isdigit(argv[3][0]))
|
||||
{
|
||||
/* set and check source user number */
|
||||
if((from_user = atoi(argv[3])) > 15)
|
||||
{
|
||||
printf("\nError - the source user number can not be greater than 15.");
|
||||
exit();
|
||||
}
|
||||
|
||||
/* set name suppress flag from parameter #4 */
|
||||
name_flag = usstrcmp("NAMES",argv[4]);
|
||||
}
|
||||
else /* no source user specified */
|
||||
{
|
||||
/* set name suppress flag from parameter #3 */
|
||||
name_flag = usstrcmp("NAMES",argv[3]);
|
||||
}
|
||||
|
||||
/* to simplify the logic below, name_flag must be made
|
||||
NZ if it is equal to NAME_EQ, 0 if it is any other value */
|
||||
name_flag = (name_flag == NAME_EQ ? 1 : 0);
|
||||
|
||||
if (to_user == from_user) /* to = from */
|
||||
{
|
||||
printf("\nError - 'to' user number is the same as the 'from' user number.");
|
||||
exit();
|
||||
}
|
||||
|
||||
/* set the search control block file name, type, user number
|
||||
extent number and length - length matches user number, file
|
||||
name and type. As the extent number does not enter into the
|
||||
comparison, all extents of a given file will be found. */
|
||||
setscb(scb,argv[1],from_user,'?',13);
|
||||
|
||||
cur_disk = bdos(GETDISK); /* get current default disk */
|
||||
lcount = dmcount = mcount = 0; /* initialize counts */
|
||||
|
||||
for (scb.scb_disk = 0; /* starting with logical disk A: */
|
||||
scb.scb_disk < 16; /* until logical disk P: */
|
||||
scb.scb_disk++) /* move to next logical disk */
|
||||
{
|
||||
/* check if current disk has been selected for search */
|
||||
if (!(scb.scb_adisks & (1 << scb.scb_disk)))
|
||||
continue; /* no - so bypass this disk */
|
||||
|
||||
/* convert search user number and name for output */
|
||||
conv_dfname(scb.scb_disk,scb,file_name);
|
||||
printf("\n\nMoving file(s) %s -> User %d.",file_name,to_user);
|
||||
|
||||
lcount++; /* update line count */
|
||||
|
||||
dir_pb.dp_disk = scb.scb_disk; /* set to disk to be searched*/
|
||||
dmcount = 0; /* reset disk matched count */
|
||||
|
||||
if (name_flag) /* if file names are to be displayed */
|
||||
putchar('\n'); /* move to column 1 */
|
||||
|
||||
/* set the directory to "closed" to force the get_nde
|
||||
function to open it. */
|
||||
dir_pb.dp_open = 0;
|
||||
|
||||
/* while not at the end of the directory, set a pointer
|
||||
to the next directory entry */
|
||||
while(dir_entry = get_nde(dir_pb))
|
||||
{
|
||||
/* match those entries that have the correct
|
||||
user number, file name, type, and any
|
||||
extent number. */
|
||||
if (
|
||||
(dir_entry -> de_userno != 0xE5) &&
|
||||
(comp_fname(scb,dir_entry) == NAME_EQ)
|
||||
)
|
||||
{
|
||||
|
||||
dir_entry -> de_userno = to_user; /* move to new user */
|
||||
/* request sector to be written back */
|
||||
dir_pb.dp_write = 1;
|
||||
|
||||
mcount++; /* update matched counts */
|
||||
dmcount++; /* per disk count */
|
||||
|
||||
if (name_flag) /* check map option */
|
||||
{
|
||||
conv_dfname(scb.scb_disk,dir_entry,file_name);
|
||||
printf("%s ",file_name);
|
||||
|
||||
/* check if need to start new line */
|
||||
if (!(dmcount % 4))
|
||||
{
|
||||
putchar('\n');
|
||||
if (++lcount > 18)
|
||||
{
|
||||
lcount = 0;
|
||||
printf("\nPress Space Bar to continue....");
|
||||
getchar();
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mcount == 0)
|
||||
printf("\n --- No Files Moved --- ");
|
||||
|
||||
bdos(SETDISK,cur_disk); /* reset to current disk */
|
||||
}
|
||||
|
||||
|
||||
chk_use(argc) /* check usage */
|
||||
/* This function checks that the correct number of
|
||||
parameters has been specified, outputting instructions
|
||||
if not. */
|
||||
/* Entry Parameter */
|
||||
int argc; /* Count of the number of arguments on the command line */
|
||||
{
|
||||
|
||||
/* The minimum value of argc is 1 (for the program name itself),
|
||||
so argc is always one greater than the number of parameters
|
||||
on the command line */
|
||||
|
||||
if (argc == 1 || argc > 5)
|
||||
{
|
||||
printf("\nUsage :");
|
||||
printf("\n\tMOVE d:filename.typ to_user {from_user} {NAMES}");
|
||||
printf("\n\t *:filename.typ (All disks)");
|
||||
printf("\n\t ABCD..OP:filename.typ (Selected Disks)");
|
||||
printf("\n\tNAMES option shows names of files moved.");
|
||||
exit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
5
CONTRIBUTIONS/cpm-handbook/cpmsrc/PRODUNIT.SUB
Normal file
5
CONTRIBUTIONS/cpm-handbook/cpmsrc/PRODUNIT.SUB
Normal file
@@ -0,0 +1,5 @@
|
||||
PIP $1:=$2:FIG*.*[V
|
||||
PIP $1:=$2:*.C[V
|
||||
PIP $1:=$2:READ.ME[V
|
||||
MOVEF $1:*.* 0
|
||||
|
||||
264
CONTRIBUTIONS/cpm-handbook/cpmsrc/PROTOCOL.C
Normal file
264
CONTRIBUTIONS/cpm-handbook/cpmsrc/PROTOCOL.C
Normal file
@@ -0,0 +1,264 @@
|
||||
#define VN "\nPROTOCOL Vn 1.0 02/17/83"
|
||||
/* PROTOCOL - This utility sets the serial port protocol for the
|
||||
specified physical device. Alternatively, it displays the
|
||||
current protocols for all of the serial devices. */
|
||||
|
||||
#include <LIBRARY.H>
|
||||
|
||||
/* code tables used to relate ASCII strings to code values */
|
||||
struct _ct ct_iproto[3]; /* code table for input protocols */
|
||||
struct _ct ct_oproto[4]; /* code table for output protocols */
|
||||
struct _ct ct_dproto[7]; /* code table for displaying protocols */
|
||||
struct _ct ct_pdev[MAXPDEV + 2]; /* physical device table */
|
||||
struct _ct ct_io[3]; /* input, output */
|
||||
|
||||
|
||||
/* parameters on the command line */
|
||||
#define PDEV argv[1] /* physical device */
|
||||
#define IO argv[2] /* input/output */
|
||||
#define PROTO argv[3] /* protocol */
|
||||
#define PROTOL argv[4] /* protocol message length */
|
||||
|
||||
|
||||
main(argc,argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
printf(VN); /* display signon message */
|
||||
setup(); /* setup code tables */
|
||||
chk_use(argc); /* check correct usage */
|
||||
|
||||
/* check if request to show current settings */
|
||||
if (usstrcmp("SHOW",argv[1]))
|
||||
{ /* no, assume a set is required */
|
||||
set_proto(get_pdev(PDEV), /* physical device */
|
||||
/* Input/Output and Protocol */
|
||||
get_proto(get_io(IO),PROTO),
|
||||
PROTOL); /* Protocol message length */
|
||||
}
|
||||
show_proto();
|
||||
|
||||
} /* end of program */
|
||||
|
||||
setup() /* setup the code tables for this program */
|
||||
{
|
||||
/* initialize the physical device table */
|
||||
ct_init(ct_pdev[0],0,PN_T); /* terminal */
|
||||
ct_init(ct_pdev[1],1,PN_P); /* printer */
|
||||
ct_init(ct_pdev[2],2,PN_M); /* modem */
|
||||
ct_init(ct_pdev[3],CT_SNF,"*"); /* terminator */
|
||||
|
||||
/* initialize the input/output table */
|
||||
ct_init(ct_io[0],0,"INPUT");
|
||||
ct_init(ct_io[1],1,"OUTPUT");
|
||||
ct_init(ct_io[2],CT_SNF,"*"); /* terminator */
|
||||
|
||||
/* initialize the output protocol table */
|
||||
ct_init(ct_oproto[0],DT_ODTR,"DTR");
|
||||
ct_init(ct_oproto[1],DT_OXON,"XON");
|
||||
ct_init(ct_oproto[2],DT_OETX,"ETX");
|
||||
ct_init(ct_oproto[3],CT_SNF,"*"); /* terminator */
|
||||
|
||||
/* initialize the input protocol table */
|
||||
ct_init(ct_iproto[0],DT_IRTS,"RTS");
|
||||
ct_init(ct_iproto[1],DT_IXON,"XON");
|
||||
ct_init(ct_iproto[2],CT_SNF,"*"); /* terminator */
|
||||
|
||||
/* initialize the display protocol */
|
||||
ct_init(ct_dproto[0],DT_ODTR,"Output DTR");
|
||||
ct_init(ct_dproto[1],DT_OXON,"Output XON");
|
||||
ct_init(ct_dproto[2],DT_OETX,"Output ETX");
|
||||
ct_init(ct_dproto[3],DT_IRTS,"Input RTS");
|
||||
ct_init(ct_dproto[4],DT_IXON,"Input XON");
|
||||
ct_init(ct_dproto[5],CT_SNF,"*");
|
||||
}
|
||||
|
||||
unsigned
|
||||
get_pdev(ppdev) /* get physical device */
|
||||
/* This function returns the physical device code
|
||||
specified by the user in the command line. */
|
||||
char *ppdev; /* pointer to character string */
|
||||
{
|
||||
unsigned retval; /* return value */
|
||||
|
||||
retval = ct_parc(ct_pdev,ppdev); /* get code for ASCII string */
|
||||
if (retval == CT_SNF) /* if string not found */
|
||||
{
|
||||
printf("\n\007Physical Device '%s' is invalid or ambiguous.",
|
||||
ppdev);
|
||||
printf("\nLegal Physical Devices are : ");
|
||||
ct_disps(ct_pdev); /* display all values */
|
||||
exit();
|
||||
}
|
||||
return retval; /* return code */
|
||||
}
|
||||
|
||||
unsigned
|
||||
get_io(pio) /* get input/output parameter */
|
||||
char *pio; /* pointer to character string */
|
||||
{
|
||||
unsigned retval; /* return value */
|
||||
|
||||
retval = ct_parc(ct_io,pio); /* get code for ASCII string */
|
||||
if (retval == CT_SNF) /* if string not found */
|
||||
{
|
||||
printf("\n\007Input/Output direction '%s' is invalid or ambiguous.",
|
||||
pio);
|
||||
printf("\nLegal values are : ");
|
||||
ct_disps(ct_io); /* display all values */
|
||||
exit();
|
||||
}
|
||||
return retval; /* return code */
|
||||
}
|
||||
|
||||
unsigned
|
||||
get_proto(output,pproto)
|
||||
/* This function returns the protocol code for the
|
||||
protocol specified by the user in the command line. */
|
||||
int output; /* =1 for output, =0 for input */
|
||||
char *pproto; /* pointer to character string */
|
||||
|
||||
{
|
||||
unsigned retval; /* return value */
|
||||
|
||||
if (output) /* OUTPUT specified */
|
||||
{
|
||||
/* get code for ASCII string */
|
||||
retval = ct_parc(ct_oproto,pproto);
|
||||
if (retval == CT_SNF) /* if string not found */
|
||||
{
|
||||
printf("\n\007Output Protocol '%s' is invalid or ambiguous.",
|
||||
pproto);
|
||||
printf("\nLegal Output Protocols are : ");
|
||||
ct_disps(ct_oproto); /* display valid protocols */
|
||||
exit();
|
||||
}
|
||||
}
|
||||
else /* INPUT specified */
|
||||
{
|
||||
/* get code for ASCII string */
|
||||
retval = ct_parc(ct_iproto,pproto);
|
||||
if (retval == CT_SNF) /* if string not found */
|
||||
{
|
||||
printf("\n\007Input Protocol '%s' is invalid or ambiguous.",
|
||||
pproto);
|
||||
printf("\nLegal Input Protocols are : ");
|
||||
ct_disps(ct_iproto); /* display valid protocols */
|
||||
exit();
|
||||
}
|
||||
}
|
||||
return retval; /* return code */
|
||||
}
|
||||
|
||||
|
||||
set_proto(pdevc,protoc,pplength) /* set the protocol for physical device */
|
||||
int pdevc; /* physical device code */
|
||||
unsigned protoc; /* protocol byte */
|
||||
char *pplength; /* pointer to protocol length */
|
||||
{
|
||||
struct _ppdt
|
||||
{
|
||||
char *pdt[16]; /* array of 16 pointers to the device tables */
|
||||
} ;
|
||||
struct _ppdt *ppdt; /* pointer to the device table array */
|
||||
struct _dt *dt; /* pointer to a device table */
|
||||
|
||||
ppdt = get_cba(CB_DTA); /* set pointer to array of pointers */
|
||||
dt = ppdt -> pdt[pdevc];
|
||||
|
||||
if (!dt) /* chk if pointer in array is valid */
|
||||
{
|
||||
printf("\nError - Array of Device Table Addresses is not set for device #%d.",
|
||||
pdevc);
|
||||
exit();
|
||||
}
|
||||
|
||||
if (protoc & 0x8000) /* check if protocol byte to be set
|
||||
directly or to be OR'ed in */
|
||||
{ /* OR'ed */
|
||||
dt -> dt_st1 |= (protoc & 0x7F);
|
||||
}
|
||||
else
|
||||
{ /* set directly */
|
||||
dt -> dt_st1 = (protoc & 0x7F);
|
||||
}
|
||||
|
||||
if ((protoc & 0x7F) == DT_OETX) /* if etx/ack, check for message
|
||||
length */
|
||||
{
|
||||
if (isdigit(*pplength)) /* check if length present */
|
||||
{
|
||||
/* convert length to binary and set device
|
||||
table field. */
|
||||
dt -> dt_etxml = atoi(pplength);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
show_proto() /* show the current protocol settings */
|
||||
{
|
||||
struct _ppdt
|
||||
{
|
||||
char *pdt[16]; /* array of 16 pointers to the device tables */
|
||||
} ;
|
||||
struct _ppdt *ppdt; /* pointer to the device table array */
|
||||
struct _dt *dt; /* pointer to a device table */
|
||||
int pdevc; /* physical device code */
|
||||
struct _ct *dproto; /* pointer to display protocols */
|
||||
|
||||
ppdt = get_cba(CB_DTA); /* set pointer to array of pointers */
|
||||
|
||||
/* for all physical devices */
|
||||
for (pdevc = 0; pdevc <= MAXPDEV; pdevc++)
|
||||
{
|
||||
/* set pointer to device table */
|
||||
dt = ppdt -> pdt[pdevc];
|
||||
|
||||
if (dt) /* chk if pointer in array is valid */
|
||||
{
|
||||
printf("\n\tProtocol for %s - ",ct_strc(ct_pdev,pdevc));
|
||||
/* check if any protocols set */
|
||||
if (!(dt -> dt_st1 & ALLPROTO))
|
||||
{
|
||||
printf("None.");
|
||||
continue;
|
||||
}
|
||||
|
||||
/* set pointer to display protocol table */
|
||||
dproto = ct_dproto;
|
||||
while (dproto -> _ct_code != CT_SNF)
|
||||
{
|
||||
/* check if protocol bit set */
|
||||
if (dproto -> _ct_code & dt -> dt_st1)
|
||||
{ /* display protocol */
|
||||
printf("%s ",dproto -> _ct_sp);
|
||||
}
|
||||
++dproto; /* move to next entry */
|
||||
}
|
||||
/* chk if etx/ack protocol and
|
||||
message length to be displayed */
|
||||
if (dt -> dt_st1 & DT_OETX)
|
||||
printf(" Message Length %d bytes.",
|
||||
dt -> dt_etxml);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
chk_use(argc) /* check for correct usage */
|
||||
int argc; /* argument count on commmand line */
|
||||
{
|
||||
if (argc == 1)
|
||||
{
|
||||
printf("\nPROTOCOL sets the physical device's serial protocols.");
|
||||
printf("\n\tPROTOCOL physical-device direction protocol {message-length}");
|
||||
printf("\n\nLegal physical devices are :");
|
||||
ct_disps(ct_pdev);
|
||||
printf("\nLegal direction/protocols are :");
|
||||
ct_disps(ct_dproto);
|
||||
printf("\n\tMessage length can be specifed with Output Etx.\n");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
88
CONTRIBUTIONS/cpm-handbook/cpmsrc/READ.ME
Normal file
88
CONTRIBUTIONS/cpm-handbook/cpmsrc/READ.ME
Normal file
@@ -0,0 +1,88 @@
|
||||
[READ.ME]
|
||||
The Programmer's CP/M Handbook Source Code Examples
|
||||
Version 1.0 August 18, 1983
|
||||
(c) 1983 Johnson-Laird Inc.
|
||||
|
||||
W<EFBFBD> hav<61> trie<69> t<> includ<75> a<> man<61> o<> th<74> example<6C> fro<72> th<74> CP/<2F> <20>
|
||||
Programmer'<27> Handboo<6F> a<> wa<77> possibl<62> t<> fi<66> ont<6E> 2 single-sided
|
||||
single-density diskettes (or alternately, <20> single-densit<69> <20>
|
||||
double-side<64> "flippy" diskette)<29> Ou<4F> origina<6E> intentio<69> wa<77> t<> <20>
|
||||
includ<EFBFBD> th<74> sourc<72> cod<6F> fo<66> ever<65> figur<75> i<> th<74> boo<6F> a<> wel<65> a<> al<61> <20>
|
||||
th<EFBFBD> exampl<70> listing<6E> i<> Chapte<74> 5<> Sinc<6E> thi<68> prove<76> t<> b<> impossibl<62> <20>
|
||||
t<EFBFBD> fi<66> ont<6E> tw<74> diskettes<65> w<> hav<61> chose<73> thos<6F> figure<72> an<61> example<6C> <20>
|
||||
whic<EFBFBD> w<> fel<65> woul<75> b<> th<74> mos<6F> useful<75> Th<54> onl<6E> majo<6A> omissio<69> i<> <20>
|
||||
Figur<EFBFBD> 9-<2D> whic<69> deal<61> wit<69> ba<62> secto<74> management<6E>
|
||||
|
||||
W<EFBFBD> hav<61> fixe<78> on<6F> bu<62> an<61> adde<64> on<6F> lin<69> t<> Figur<75> 8-10<31> Thi<68> i<> <20>
|
||||
note<EFBFBD> i<> th<74> sourc<72> code<64> Th<54> line<6E> whic<69> wer<65> lef<65> ou<6F> i<> th<74> boo<6F> <20>
|
||||
afte<EFBFBD> pag<61> 26<32> ar<61> als<6C> included<65> Yo<59> ma<6D> notic<69> som<6F> mino<6E> <20>
|
||||
difference<EFBFBD> i<> capitalization<6F> indentation<6F> etc<74> i<> th<74> comment<6E> wher<65> <20>
|
||||
w<EFBFBD> hav<61> no<6E> include<64> change<67> mad<61> i<> copyediting.
|
||||
|
||||
W<EFBFBD> woul<75> appreciat<61> hearin<69> fro<72> you<6F> especiall<6C> abou<6F> an<61> bugs<67> <20>
|
||||
typos<EFBFBD> othe<68> horribl<62> goofs<66> suggestion<6F> fo<66> improvements.
|
||||
|
||||
Contents of Diskette No 1
|
||||
|
||||
Filename Page No. Figure Title
|
||||
|
||||
FIG5-2.AS<41> 70 Equate<74> fo<66> BDO<44> functio<69> cod<6F> numbers
|
||||
FIG5-3.ASM 74 Write console byte example, output null-byte
|
||||
terminated message from specified address
|
||||
FIG5-4.AS͠ 7<><37> Writ<69><74>consol<6F><6C>byt<79><74>example<6C><65>outpu<70><75>null-byt<79>
|
||||
terminate<74> messag<61> followin<69> cal<61> t<> subroutine
|
||||
FIG5-5.ASM 76 Read line from reader device
|
||||
FIG5-6.ASM 78 Write line to punch device
|
||||
FIG5-7.ASM 79 Write line to list device
|
||||
FIG5-8.AS<41> 8<> Read/Writ<69> strin<69> from/t<> consol<6F> usin<69> ra<72> I/O
|
||||
FIG5-10.ASM 86 IOBYTE equates
|
||||
FIG5-11.ASM 87 Simple terminal emulator
|
||||
FIG5-12.ASM 89 Display $-terminated message on console
|
||||
FIG5-13.ASM 92 Read console string for keyboard options
|
||||
FIG5-14.ASM 95 Determine the CP/M version number
|
||||
FIG5-15.ASM 96 Reset requested disk drive
|
||||
FIG5-16.ASM 100 Open file request
|
||||
FIG5-17.AS<41> 10<31> Searc<72> first/nex<65> call<6C> fo<66> ambiguou<6F> filenam<61>
|
||||
FIG5-18.ASM 110 Read next character from sequential disk file
|
||||
FIG5-19.ASM 113 Write next character to sequential disk file
|
||||
FIG5-20.ASM 115 Create file request
|
||||
FIG5-21.ASM 117 Rename file request
|
||||
FIG5-22.ASM 122 Set file attributes
|
||||
FIG5-23.ASM 123 Get file attributes
|
||||
FIG5-24.ASM 126 Accessing disk parameter block data
|
||||
FUNCTN33.ASM 131 Example for function 33, read random
|
||||
FIG5-25.ASM 135 Create random file
|
||||
FIG5-26.ASM 136 Read/write variable length records randomly
|
||||
FIG6-4.ASM 159 Simple BIOS listing
|
||||
<EFBFBD>FIG7-5.ASM 191 Example PUTCP/M
|
||||
FIG7-7.ASM 198 Example CP/M cold bootstrap loader
|
||||
FIG8-6.ASM 226 Device table equates
|
||||
FIG10-5.ASM 363 Testbed for real time clock driver in the BIOS
|
||||
FIG10-6.ASM 365 Testbed for disk I/O drivers in the BIOS
|
||||
ERASE.<2E> 41<34> Figur<75> 11-3<> request<73> confirmation before erasing
|
||||
UNERASE.C 412 Figure 11-4, "revives" erased files
|
||||
FIND.C 417 Figure 11-5, locates specific files or groups of files
|
||||
SPACE.<2E> 42<34> Figur<75> 11-6<> display<61> ho<68> muc<75> dis<69> storage is used
|
||||
or available
|
||||
MOVE.C 424 Figure 11-7, "moves" files from one user to another
|
||||
MAKE.C 428 Figure 11-8, makes files "invisible" and protected
|
||||
or makes them "visible," accessible and unprotected
|
||||
SPEED.C 431 Figure 11-9, sets the baud rate for a specific device
|
||||
PROTOCOL.C 435 Figure 11-10, sets the protocol governing input and output
|
||||
of a specified serial device
|
||||
ASSIGN.C 439 Figure 11-11, assigns a logical devices input and output
|
||||
to two or more physical devices
|
||||
DATE.C 443 Figure 11-12, makes the current date part of the system
|
||||
TIME.C 444 Figure 11-13, makes the current time part of the system
|
||||
FUNKEY.C 446 Figure 11-14, sets the character strings associated with
|
||||
specific function keys
|
||||
|
||||
Contents of Diskette 2
|
||||
|
||||
FIG8-10.ASM 237 Enhanced BIOS listing
|
||||
FIG9-5.ASM 312 User-friendly disk error processor
|
||||
FIG10-2.ASM 331 Debug subroutines
|
||||
FIG10-4.ASM 355 Testbed for character I/O drivers
|
||||
LIBRARY.C 372 Figure 11-1, commonly used functions in C
|
||||
LIBRARY.H 390 Figure 11-2, code to be included at the beginning of
|
||||
any program that calls LIBRARY functions in Figure 11-1
|
||||
229
CONTRIBUTIONS/cpm-handbook/cpmsrc/SPACE.C
Normal file
229
CONTRIBUTIONS/cpm-handbook/cpmsrc/SPACE.C
Normal file
@@ -0,0 +1,229 @@
|
||||
#define VN "1.0 02/11/83"
|
||||
/* SPACE - This utility displays a map showing on the amount of space
|
||||
(expressed as relative percentages) occupied in each user number
|
||||
for each logical disk). It also shows the relative amount of space
|
||||
free. */
|
||||
|
||||
#include <LIBRARY.H>
|
||||
|
||||
struct _dirpb dir_pb; /* directory management parameter block */
|
||||
struct _dir *dir_entry; /* pointer to directory entry */
|
||||
struct _scb scb; /* search control block */
|
||||
struct _dpb dpb; /* CP/M's disk parameter block */
|
||||
|
||||
char file_name[20]; /* formatted for display : un/d:FILENAME.TYP */
|
||||
|
||||
short cur_disk; /* current logical disk at start of program
|
||||
NZ = show map of number of files */
|
||||
int count; /* used to access the allocation block numbers
|
||||
in each directory entry */
|
||||
int user; /* used to access the disk map when calculating */
|
||||
|
||||
/* The array below is used to tabulate the results for each
|
||||
disk drive, and for each user number on the drive.
|
||||
In addition, two extra "users" have been added for "Free"
|
||||
and "Used" values.
|
||||
*/
|
||||
unsigned disk_map[16][18]; /* Disk A -> P, Users 0 -> 15, Free, Used */
|
||||
#define USED_COUNT 16 /* "user" number for Used entities */
|
||||
#define FREE_COUNT 17 /* "user" number for Free entities */
|
||||
|
||||
|
||||
main(argc,argv)
|
||||
short argc; /* argument count */
|
||||
char *argv[]; /* argument vector (pointer to an array of chars) */
|
||||
{
|
||||
|
||||
printf("\nSPACE Version %s (Library %s)",VN,LIBVN);
|
||||
chk_use(argc); /* check usage */
|
||||
cur_disk = bdos(GETDISK); /* get current default disk */
|
||||
|
||||
dm_clr(disk_map); /* reset disk map */
|
||||
|
||||
ssetscb(scb,argv[1]); /* special version : set disks,
|
||||
name, type */
|
||||
|
||||
for (scb.scb_disk = 0; /* starting with logical disk A: */
|
||||
scb.scb_disk < 16; /* until logical disk P: */
|
||||
scb.scb_disk++) /* move to next logical disk */
|
||||
{
|
||||
|
||||
/* check if current disk has been selected for search */
|
||||
if (!(scb.scb_adisks & (1 << scb.scb_disk)))
|
||||
continue; /* no - so bypass this disk */
|
||||
|
||||
printf("\nSearching disk : %c",(scb.scb_disk + 'A'));
|
||||
|
||||
dir_pb.dp_disk = scb.scb_disk; /* set to disk to be searched*/
|
||||
|
||||
/* set the directory to "closed", and force the get_nde
|
||||
function to open it. */
|
||||
dir_pb.dp_open = 0;
|
||||
|
||||
/* while not at the end of the directory, set a pointer
|
||||
to the next entry in the directory */
|
||||
while (dir_entry = get_nde(dir_pb))
|
||||
{
|
||||
if (dir_entry -> de_userno == 0xE5)
|
||||
continue; /* bypass inactive entries */
|
||||
|
||||
for (count = 0; /* start with the first alloc. block */
|
||||
count < dir_pb.dp_nabpde; /* for number of alloc. blks per dir. entry */
|
||||
count++)
|
||||
{
|
||||
if (dir_pb.dp_nabpde == 8) /* assume 8 2-byte numbers */
|
||||
{
|
||||
disk_map[scb.scb_disk][dir_entry -> de_userno]
|
||||
+= (dir_entry -> _dirab.de_long[count] > 0 ? 1 : 0);
|
||||
}
|
||||
else /* assume 16 1-byte numbers */
|
||||
{
|
||||
disk_map[scb.scb_disk][dir_entry -> de_userno]
|
||||
+= (dir_entry -> _dirab.de_short[count] > 0 ? 1 : 0);
|
||||
}
|
||||
} /* all allocation blocks processed */
|
||||
} /* end of directory for this disk */
|
||||
|
||||
|
||||
/* Compute the storage used by multiplying the number of
|
||||
allocation blocks counted by the number of Kbytes in
|
||||
each allocation block. */
|
||||
|
||||
for (user = 0; /* start with user 0 */
|
||||
user < 16; /* end with user 15 */
|
||||
user ++) /* move to next user number */
|
||||
{
|
||||
/* compute size occupied in Kbytes */
|
||||
disk_map[scb.scb_disk][user] *= dir_pb.dp_absize;
|
||||
/* build up sum for this disk */
|
||||
disk_map[scb.scb_disk][USED_COUNT] += disk_map[scb.scb_disk][user];
|
||||
}
|
||||
|
||||
/* free space = (# of alloc. blks * # of kbyte per blk)
|
||||
- used Kbytes
|
||||
- (directory entries * 32) / 1024 ... or divide by 32 */
|
||||
disk_map[scb.scb_disk][FREE_COUNT] = (dir_pb.dp_nab * dir_pb.dp_absize)
|
||||
- disk_map[scb.scb_disk][USED_COUNT]
|
||||
- (dir_pb.dp_nument >> 5); /* same as / 32 */
|
||||
} /* all disks processed */
|
||||
|
||||
|
||||
printf("\n Numbers show space used in kilobytes.");
|
||||
printf("\n --- User Numbers --- Space (Kb)");
|
||||
|
||||
dm_disp(disk_map,scb.scb_adisks); /* display disk map */
|
||||
|
||||
|
||||
bdos(SETDISK,cur_disk); /* reset to current disk */
|
||||
}
|
||||
|
||||
ssetscb(scb,ldisks) /* special version of - set search control block */
|
||||
/* This function sets up a search control block according
|
||||
to just the logical disks specified. The disk are specified as
|
||||
a single string of characters without any separators. An
|
||||
asterisk means "all disks". For example -
|
||||
|
||||
ABGH (disks A:, B:, G: and H: )
|
||||
* (all disks for which SELDSK has tables)
|
||||
|
||||
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).
|
||||
The file name, type and extent number are all set to '?' to match
|
||||
all possible entries in the directory. */
|
||||
|
||||
/* Entry Parameters */
|
||||
struct _scb *scb; /* pointer to search control block */
|
||||
char *ldisks; /* pointer to the logical disks */
|
||||
|
||||
/* 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 (*ldisks) /* some values specified */
|
||||
{
|
||||
if (*ldisks == '*') /* check if "all disks" */
|
||||
{
|
||||
adisks = 0xFFFF; /* set all bits */
|
||||
}
|
||||
else /* set specific disks */
|
||||
{
|
||||
while(*ldisks) /* until end of disks 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(*ldisks) - 'A');
|
||||
++ldisks; /* move to next character */
|
||||
}
|
||||
}
|
||||
}
|
||||
else /* use only current default disk */
|
||||
{
|
||||
/* set just the bit corresponding to the current disk */
|
||||
adisks = 1 << bdos(GETDISK);
|
||||
}
|
||||
|
||||
/* set the user number, file name, type and extent to '?'
|
||||
so that all active directory entries will match */
|
||||
/* 0123456789012 */
|
||||
strcpy(&scb -> scb_userno,"?????????????");
|
||||
|
||||
/* 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 */
|
||||
|
||||
} /* end ssetscb */
|
||||
|
||||
|
||||
|
||||
chk_use(argc) /* check usage */
|
||||
/* This function checks that the correct number of
|
||||
parameters has been specified, outputting instructions
|
||||
if not. */
|
||||
|
||||
/* Entry Parameter */
|
||||
int argc; /* Count of the number of arguments on the command line */
|
||||
{
|
||||
|
||||
/* The minimum value of argc is 1 (for the program name itself),
|
||||
so argc is always one greater than the number of parameters
|
||||
on the command line */
|
||||
|
||||
if (argc != 2)
|
||||
{
|
||||
printf("\nUsage :");
|
||||
printf("\n\tSPACE * (All disks)");
|
||||
printf("\n\tSPACE ABCD..OP (Selected Disks)");
|
||||
exit();
|
||||
}
|
||||
} /* end chk_use */
|
||||
|
||||
179
CONTRIBUTIONS/cpm-handbook/cpmsrc/SPEED.C
Normal file
179
CONTRIBUTIONS/cpm-handbook/cpmsrc/SPEED.C
Normal file
@@ -0,0 +1,179 @@
|
||||
#define VN "\nSPEED 1.0 02/17/83"
|
||||
/* This utility sets the baud rate speed for each of the physical
|
||||
devices. */
|
||||
|
||||
#include <LIBRARY.H>
|
||||
|
||||
struct _ct ct_pdev[MAXPDEV + 2]; /* physical device table */
|
||||
|
||||
/* hardware specific items */
|
||||
/* baud rates for serial ports */
|
||||
#define B300 0x35 /* 300 baud */
|
||||
#define B600 0x36 /* 600 baud */
|
||||
#define B1200 0x37 /* 1200 baud */
|
||||
#define B2400 0x3A /* 2400 baud */
|
||||
#define B4800 0x3C /* 4800 baud */
|
||||
#define B9600 0x3E /* 9600 baud */
|
||||
#define B19200 0x3F /* 19200 baud */
|
||||
struct _ct ct_br[10]; /* code table for baud rates (+ spare entries) */
|
||||
|
||||
|
||||
/* parameters on the command line */
|
||||
#define PDEV argv[1] /* physical device */
|
||||
#define BAUD argv[2] /* baud rate */
|
||||
|
||||
|
||||
main(argc,argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
printf(VN); /* display sign on message */
|
||||
setup(); /* setup code tables */
|
||||
chk_use(argc); /* check correct usage */
|
||||
|
||||
/* check if request to show current settings */
|
||||
if (usstrcmp("SHOW",argv[1]))
|
||||
{ /* No, assume setting is required */
|
||||
set_baud(get_pdev(PDEV),get_baud(BAUD)); /* set baud rate */
|
||||
}
|
||||
|
||||
show_baud(); /* display current settings */
|
||||
|
||||
} /* end of program */
|
||||
|
||||
setup() /* setup the code tables for this program */
|
||||
{
|
||||
/* initialize the physical device table */
|
||||
ct_init(ct_pdev[0],T_DEVN,PN_T); /* terminal */
|
||||
ct_init(ct_pdev[1],P_DEVN,PN_P); /* printer */
|
||||
ct_init(ct_pdev[2],M_DEVN,PN_M); /* modem */
|
||||
ct_init(ct_pdev[3],CT_SNF,"*"); /* terminator */
|
||||
|
||||
/* initialize the baud rate table */
|
||||
ct_init(ct_br[0],B300,"300");
|
||||
ct_init(ct_br[1],B600,"600");
|
||||
ct_init(ct_br[2],B1200,"1200");
|
||||
ct_init(ct_br[3],B2400,"2400");
|
||||
ct_init(ct_br[4],B4800,"4800");
|
||||
ct_init(ct_br[5],B9600,"9600");
|
||||
ct_init(ct_br[6],B19200,"19200");
|
||||
ct_init(ct_br[7],CT_SNF,"*"); /* terminator */
|
||||
}
|
||||
|
||||
unsigned
|
||||
get_pdev(ppdev) /* get physical device */
|
||||
/* This function returns the physical device code
|
||||
specified by the user in the command line. */
|
||||
char *ppdev; /* pointer to character string */
|
||||
{
|
||||
unsigned retval; /* return value */
|
||||
|
||||
retval = ct_parc(ct_pdev,ppdev); /* get code for ASCII string */
|
||||
if (retval == CT_SNF) /* if string not found */
|
||||
{
|
||||
printf("\n\007Physical Device '%s' is invalid or ambiguous.",
|
||||
ppdev);
|
||||
printf("\nLegal Physical Devices are : ");
|
||||
ct_disps(ct_pdev); /* display all values */
|
||||
exit();
|
||||
}
|
||||
return retval; /* return code */
|
||||
}
|
||||
|
||||
|
||||
unsigned
|
||||
get_baud(pbaud)
|
||||
/* This function returns the baud rate time constant
|
||||
for baud rate specified by the user in the command line. */
|
||||
char *pbaud; /* pointer to character string */
|
||||
{
|
||||
unsigned retval; /* return value */
|
||||
retval = ct_parc(ct_br,pbaud); /* get code for ASCII string */
|
||||
if (retval == CT_SNF) /* if string not found */
|
||||
{
|
||||
printf("\n\007Baud Rate '%s' is invalid or ambiguous.",
|
||||
pbaud);
|
||||
printf("\nLegal Baud Rates are : ");
|
||||
ct_disps(ct_br); /* display all values */
|
||||
exit();
|
||||
}
|
||||
return retval; /* return code */
|
||||
}
|
||||
|
||||
set_baud(pdevc,baudc) /* set the baud rate of the specified device */
|
||||
int pdevc; /* physical device code */
|
||||
short baudc; /* baud rate code */
|
||||
/* on some systems this may have to be a
|
||||
two-byte (unsigned) value. */
|
||||
{
|
||||
short *baud_rc; /* pointer to the baud rate constant */
|
||||
/* on some systems this may have to be a
|
||||
two-byte (unsigned) value. */
|
||||
/* Note : the respective codes for accessing the baud rate constants
|
||||
via the get_cba (get configuration block address) function are:
|
||||
Device #0 = 19, #1 = 21, #2 = 23. This function uses this
|
||||
mathematical relationship. */
|
||||
|
||||
/* setup pointer to the baud rate constant */
|
||||
baud_rc = get_cba(CB_D0_BRC + (pdevc << 1));
|
||||
|
||||
/* then set the baud rate constant */
|
||||
*baud_rc = baudc;
|
||||
|
||||
/* then call the BIOS initialization routine */
|
||||
bios(CIOINIT,pdevc);
|
||||
}
|
||||
|
||||
|
||||
show_baud() /* show current baud rate */
|
||||
{
|
||||
|
||||
int pdevn; /* physical device number */
|
||||
short baudc; /* baud rate code */
|
||||
/* on some systems this may have to be a
|
||||
two-byte (unsigned) value. */
|
||||
short *baud_rc; /* pointer to the baud rate constant */
|
||||
/* on some systems this may have to be a
|
||||
two-byte (unsigned) value. */
|
||||
/* Note : the respective codes for accessing the baud rate constants
|
||||
via the get_cba (get configuration block address) function are:
|
||||
Device #0 = 19, #1 = 21, #2 = 23. This function uses this
|
||||
mathematical relationship. */
|
||||
|
||||
printf("\nCurrent Baud Rate settings are :");
|
||||
|
||||
|
||||
for (pdevn = 0; pdevn <= MAXPDEV; pdevn ++) /* all physical devices */
|
||||
{
|
||||
/* setup pointer to the baud rate constant -
|
||||
the code for the get_cba function is computed
|
||||
by adding the physical device number *2 to
|
||||
the Baud Rate code for device #0 */
|
||||
|
||||
baud_rc = get_cba(CB_D0_BRC + (pdevn << 1));
|
||||
|
||||
/* then set the baud rate constant */
|
||||
baudc = *baud_rc;
|
||||
|
||||
printf("\n\t%s set to %s baud.",
|
||||
ct_strc(ct_pdev,pdevn), /* get ptr to device name */
|
||||
ct_strc(ct_br,baudc) ); /* get ptr to baud rate */
|
||||
}
|
||||
}
|
||||
|
||||
chk_use(argc) /* check correct usage */
|
||||
int argc; /* argument count */
|
||||
{
|
||||
if (argc == 1)
|
||||
{
|
||||
printf("\nThe SPEED utility sets the baud rate speed for each physical device.");
|
||||
printf("\nUsage is : SPEED physical-device baud-rate, or");
|
||||
printf("\n SPEED SHOW (to show current settings)");
|
||||
printf("\n\nValid physical devices are: ");
|
||||
ct_disps(ct_pdev);
|
||||
printf("\nValid baud rates are: ");
|
||||
ct_disps(ct_br);
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
77
CONTRIBUTIONS/cpm-handbook/cpmsrc/TIME.C
Normal file
77
CONTRIBUTIONS/cpm-handbook/cpmsrc/TIME.C
Normal file
@@ -0,0 +1,77 @@
|
||||
#define VN "\nTIME Vn 1.0 02/18/83"
|
||||
/* This utility accepts the current time from the command tail
|
||||
validates it, and set the internal system time in the BIOS.
|
||||
Alternatively, it can be requested just to display the current
|
||||
system time. */
|
||||
|
||||
#include <LIBRARY.H>
|
||||
|
||||
char *time; /* pointer to the time in the config. block */
|
||||
char *time_set; /* pointer to the time set flag */
|
||||
int hh,mm,ss; /* variables to hold hours, minutes, seconds */
|
||||
int mcount; /* match count of numeric values entered */
|
||||
int count; /* count used to add leading 0's to time */
|
||||
|
||||
main(argc,argv)
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
printf(VN); /* display signon message */
|
||||
time = get_cba(CB_TIMEA); /* set pointer to time */
|
||||
time_flag = get_cba(CB_DTFLAGS); /* set pointer to the time set flag */
|
||||
hh = mm = ss = 0; /* initialize the time in case seconds or
|
||||
minutes are not specified */
|
||||
|
||||
if (argc != 2) /* check if help requested (or needed) */
|
||||
show_use(); /* display correct usage and exit */
|
||||
|
||||
if (usstrcmp("SHOW",argv[1])) /* check if not SHOW option */
|
||||
{
|
||||
/* convert time into hours, minutes, seconds */
|
||||
mcount = sscanf(argv[1],"%d:%d:%d",&hh,&mm,&ss);
|
||||
if (!mcount) /* input not numeric */
|
||||
show_use(); /* display correct usage and exit */
|
||||
|
||||
if (hh > 12) /* check valid hours, minutes, seconds */
|
||||
{
|
||||
printf("\n\007Hours = %d is illegal.",hh);
|
||||
show_use(); /* display correct usage and exit */
|
||||
}
|
||||
if (mm > 59)
|
||||
{
|
||||
printf("\n\007Minutes = %d is illegal.",mm);
|
||||
show_use(); /* display correct usage and exit */
|
||||
}
|
||||
if (ss > 59)
|
||||
{
|
||||
show_use(); /* display correct usage and exit */
|
||||
printf("\n\007Seconds = %d is illegal.",ss);
|
||||
}
|
||||
|
||||
/* convert integers back into formatted string */
|
||||
sprintf(time,"%2d:%2d:%2d",hh,mm,ss);
|
||||
time[8] = 0x0A; /* terminate with line feed */
|
||||
time[9] = '\0'; /* new string terminator */
|
||||
|
||||
/* convert " 1: 2: 3" into "01:02:03" */
|
||||
for (count = 0; count < 7; count+=3)
|
||||
{
|
||||
if (time[count] == ' ')
|
||||
time[count] = '0';
|
||||
}
|
||||
/* turn bit on to indicate that the time has been set */
|
||||
*time_flag |= TIME_SET;
|
||||
}
|
||||
|
||||
printf("\n\tCurrent Time is %s",time);
|
||||
}
|
||||
|
||||
show_use() /* display correct usage and exit */
|
||||
{
|
||||
printf("\nTIME sets the system time. Usage is :");
|
||||
printf("\n\tTIME hh{:mm{:ss}}");
|
||||
printf("\n\tTIME SHOW (to display current time)\n");
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
282
CONTRIBUTIONS/cpm-handbook/cpmsrc/UNERASE.C
Normal file
282
CONTRIBUTIONS/cpm-handbook/cpmsrc/UNERASE.C
Normal file
@@ -0,0 +1,282 @@
|
||||
#define VN "1.0 02/12/83"
|
||||
/* UNERASE - This does the inverse of ERASE. It revives the specified files,
|
||||
by changing the first byte of their directory entries from 0xE5
|
||||
back to the specified user number. */
|
||||
|
||||
#include <LIBRARY.H>
|
||||
|
||||
struct _dirpb dir_pb; /* directory management parameter block */
|
||||
struct _dir *dir_entry; /* pointer to directory entry */
|
||||
struct _scb scb; /* search control block */
|
||||
struct _scb scba; /* scb setup to match all files */
|
||||
struct _dpb dpb; /* CP/M's disk parameter block */
|
||||
struct _bv inuse_bv; /* bit vector for blocks in use */
|
||||
struct _bv file_bv; /* bit vector for file to be unerased */
|
||||
struct _bv extents; /* bit vector for those extents unerased */
|
||||
|
||||
char file_name[20]; /* formatted for display : un/d:FILENAME.TYP */
|
||||
|
||||
short cur_disk; /* current logical disk at start of program
|
||||
NZ = show map of number of files */
|
||||
int count; /* used to access the allocation block numbers
|
||||
in each directory entry */
|
||||
int user; /* user in which the file is to be revived */
|
||||
|
||||
|
||||
main(argc,argv)
|
||||
short argc; /* argument count */
|
||||
char *argv[]; /* argument vector (pointer to an array of chars) */
|
||||
{
|
||||
|
||||
printf("\nUNERASE Version %s (Library %s)",VN,LIBVN);
|
||||
chk_use(argc); /* check usage */
|
||||
cur_disk = bdos(GETDISK); /* get current default disk */
|
||||
|
||||
|
||||
/* using a special version of set search control block,
|
||||
set the disk, name, type (no ambiguous names), the user number
|
||||
to match only erased entries, and the length to compare
|
||||
the user, name and type.
|
||||
This special version also returns the disk_id taken from
|
||||
the file name on the command line. */
|
||||
if ((dir_pb.dp_disk = ssetscb(scb,argv[1],0xE5,12)) == 0)
|
||||
{ /* use default disk */
|
||||
dir_pb.dp_disk = cur_disk;
|
||||
}
|
||||
else
|
||||
{ /* make disk A = 0, B = 1 (for SELDSK) */
|
||||
dir_pb.dp_disk--;
|
||||
}
|
||||
printf("\nSearching disk %d.",dir_pb.dp_disk);
|
||||
|
||||
if(strscn(scb,"?")) /* check if ambiguous name */
|
||||
{
|
||||
printf("\nError - UNERASE can only revive a single file at a time.");
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
/* set up a special search control block that will match with
|
||||
all existing files. */
|
||||
ssetscb(scba,"*.*",'?',12); /* set file name and initialize scb */
|
||||
|
||||
|
||||
if (argc == 2) /* no user number specified */
|
||||
user = bdos(GETUSER,0xFF); /* get current user number */
|
||||
else
|
||||
{
|
||||
user = atoi(argv[2]); /* get specified number */
|
||||
if (user > 15)
|
||||
{
|
||||
printf("\nUser number can only be between 0 - 15.");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
/* build a bit vector that shows the allocation blocks
|
||||
currently in use. scba has been set up to match all
|
||||
active directory entries on the disk. */
|
||||
build_bv(inuse_bv,scba);
|
||||
|
||||
/* build a bit vector for the file to be restored showing
|
||||
which allocation blocks will be needed for the file. */
|
||||
if (!build_bv(file_bv,scb))
|
||||
{
|
||||
printf("\nNo directory entries found for file %s.",
|
||||
argv[1]);
|
||||
exit();
|
||||
}
|
||||
/* perform a boolean AND of the two bit vectors. */
|
||||
bv_and(file_bv,inuse_bv,file_bv);
|
||||
|
||||
/* check if the result is non-zero - if so, then one or more
|
||||
of the allocation blocks required by the erased file is
|
||||
already in use for an existing file and the file cannot
|
||||
be restored. */
|
||||
if (bv_nz(file_bv))
|
||||
{
|
||||
printf("\n--- This file cannot be restored as some parts of it");
|
||||
printf("\n have been re-used for other files! ---");
|
||||
exit();
|
||||
}
|
||||
|
||||
/* continue on to restore the file by changing all the entries
|
||||
in the directory to have the specified user number.
|
||||
Note : the problem is complicated by the fact that there
|
||||
may be several entries in the directory that are for the
|
||||
same file name and type, and even have the same extent
|
||||
number. For this reason, a bit map is kept of the extent
|
||||
numbers unerased - duplicate extent numbers will not be
|
||||
unerased. */
|
||||
|
||||
/* set up the bit vector for up to 127 unerased extents */
|
||||
bv_make(extents,16); /* 16 * 8 bits */
|
||||
|
||||
|
||||
/* set the directory to "closed", and force the get_nde
|
||||
function to open it. */
|
||||
dir_pb.dp_open = 0;
|
||||
|
||||
/* while not at the end of the directory, return a pointer to
|
||||
the next entry in the directory. */
|
||||
while(dir_entry = get_nde(dir_pb))
|
||||
{
|
||||
|
||||
/* check if user = 0xE5 and name, type match */
|
||||
if (comp_fname(scb,dir_entry) == NAME_EQ)
|
||||
{
|
||||
/* test if this extent has already been
|
||||
unerased */
|
||||
if (bv_test(extents,dir_entry -> de_extent))
|
||||
{ /* yes it has */
|
||||
printf("\n\t\tExtent #%d of %s ignored.",
|
||||
dir_entry -> de_extent,argv[1]);
|
||||
continue; /* do not unerase this one */
|
||||
}
|
||||
else /* indicate this extent unerased */
|
||||
{
|
||||
bv_set(extents,dir_entry -> de_extent);
|
||||
dir_entry -> de_userno = user; /* unerase entry */
|
||||
dir_pb.dp_write = 1; /* need to write sector back */
|
||||
printf("\n\tExtent #%d of %s unerased.",
|
||||
dir_entry -> de_extent,argv[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
printf("\n\nFile %s unerased in User Number %d.",
|
||||
argv[1],user);
|
||||
|
||||
bdos(SETDISK,cur_disk); /* reset to current disk */
|
||||
}
|
||||
|
||||
|
||||
build_bv(bv,scb) /* build bit vector (from directory) */
|
||||
/* This function scans the directory of the disk specified in
|
||||
the directory parameter block (declared as a global variable),
|
||||
and builds the specified bit vector showing all the allocation
|
||||
blocks used by files matching the name in the search control
|
||||
block. */
|
||||
|
||||
/* Entry Parameters */
|
||||
struct _bv *bv; /* pointer to the bit vector */
|
||||
struct _scb *scb; /* pointer to search control block */
|
||||
/* also uses : directory parameter block (dir_pb) */
|
||||
|
||||
/* Exit Parameters
|
||||
The specified bit vector will be created, and will have 1-bits
|
||||
set wherever an allocation block is found in a directory
|
||||
entry that matches the search control block.
|
||||
It also returns the number of directory entries matched. */
|
||||
{
|
||||
unsigned abno; /* allocation block number */
|
||||
struct _dpb *dpb; /* pointer to the disk parameter block in the BIOS */
|
||||
int mcount; /* match count of dir. entries matched */
|
||||
|
||||
mcount = 0; /* initialize match count */
|
||||
dpb = get_dpb(dir_pb.dp_disk); /* get disk parameter block address */
|
||||
|
||||
/* make the bit vector with one byte for each 8 allocation
|
||||
blocks + 1 */
|
||||
if (!(bv_make(bv,(dpb -> dpb_maxabn >>3)+1)))
|
||||
{
|
||||
printf("\nError - Insufficient memory to make a bit vector.");
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
/* set directory to "closed" to force the get_nde
|
||||
function to open it. */
|
||||
dir_pb.dp_open = 0;
|
||||
|
||||
/* now scan the directory building the bit vector */
|
||||
while(dir_entry = get_nde(dir_pb))
|
||||
{
|
||||
/* compare user number (which can legitimately be
|
||||
0xE5), the file name and the type). */
|
||||
if (comp_fname(scb,dir_entry) == NAME_EQ)
|
||||
{
|
||||
++mcount; /* update match count */
|
||||
for (count = 0; /* start with the first alloc. block */
|
||||
count < dir_pb.dp_nabpde; /* for number of alloc. blks per dir. entry */
|
||||
count++)
|
||||
{
|
||||
/* set the appropriate bit number for
|
||||
each non-zero allocation block number */
|
||||
if (dir_pb.dp_nabpde == 8) /* assume 8 2-byte numbers */
|
||||
{
|
||||
abno = dir_entry -> _dirab.de_long[count];
|
||||
}
|
||||
else /* assume 16 1-byte numbers */
|
||||
{
|
||||
abno = dir_entry -> _dirab.de_short[count];
|
||||
}
|
||||
if (abno) bv_set(bv,abno); /* set the bit */
|
||||
}
|
||||
}
|
||||
}
|
||||
return mcount; /* return number of dir entries matched */
|
||||
}
|
||||
|
||||
|
||||
|
||||
chk_use(argc) /* check usage */
|
||||
/* This function checks that the correct number of
|
||||
parameters has been specified, outputting instructions
|
||||
if not. */
|
||||
|
||||
/* Entry Parameter */
|
||||
int argc; /* Count of the number of arguments on the command line */
|
||||
{
|
||||
|
||||
/* The minimum value of argc is 1 (for the program name itself),
|
||||
so argc is always one greater than the number of parameters
|
||||
on the command line */
|
||||
|
||||
if (argc == 1 || argc > 3)
|
||||
{
|
||||
printf("\nUsage :");
|
||||
printf("\n\tUNERASE {d:}filename.typ {user}");
|
||||
printf("\n\tOnly a single unambiguous file name can be used.)");
|
||||
exit();
|
||||
}
|
||||
} /* end chk_use */
|
||||
|
||||
|
||||
ssetscb(scb,fname,user,length) /* special version of set search control block */
|
||||
/* This function sets up a search control block according
|
||||
to the file name, type, user number and number of bytes to
|
||||
compare.
|
||||
The file name can take the following forms :
|
||||
|
||||
filename
|
||||
filename.typ
|
||||
d:filename.typ
|
||||
|
||||
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 be matched */
|
||||
int length; /* number of bytes to compare */
|
||||
|
||||
/* Exit Parameters
|
||||
Disk number to be searched. (A = 1, B = 2...)
|
||||
*/
|
||||
{
|
||||
short disk_id; /* disk number to search */
|
||||
|
||||
setfcb(scb,fname); /* set search control block as though it
|
||||
is a file control block. */
|
||||
disk_id = scb -> scb_userno; /* set disk_id before it gets overwritten
|
||||
by the user number */
|
||||
scb -> scb_userno = user; /* set user number */
|
||||
scb -> scb_length = length; /* set number of bytes to compare */
|
||||
return disk_id;
|
||||
} /* end setscb */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user