home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
walnut-creek-CDROM
/
CPM
/
CPM3
/
DATEP2.LBR
/
DATE+.CZ
/
DATE+.C
Wrap
Text File
|
2000-06-30
|
19KB
|
748 lines
/*=============================================================================
D A T E
-------
John Hastwell-Batten
Jonathon Richard Saxton
January 1987
A program which (approximately) emulates DATE.COM as distributed
with CP/M 3 by Digital Research.
Command formats:
---------------
date
displays current date and time.
date set
prompts for date and time and adjusts the system clock
accordingly.
date cont
loops, displaying date and time, until a key is pressed.
date read
reads hardware clock and sets system date and time.
date dd/mm/yy hh:mm:ss -or- date mm/dd/yy hh:mm:ss
sets the system clock to the specified value.
Only the first letter of the argument is significant so "date s"
is equivalent to "date set".
The format in which this program will accept dates and times is
much less strict than that allowed by DRI's DATE program:--
1. DRI's DATE wants date input in the format MM/DD/YY and no
other. 05/07/86 is acceptable, 5/7/86 is not. This program
will accept either of the above and also 5-7-86, 5 7 86 or
just about anything else. It does not care what you use as
a separator between the fields and does not insist that you
pad the numbers with leading zeros. It will also accept
1986 as a valid year.
2. Depending on a compile-time option, this program will accept
numeric dates in the month/day/year format used in the USA
or in the more usual day/month/year format used throughout
the rest of the world. It will also accept dates in universal
alphanumeric format - e.g. 7 dec 86 (again, the program is not
fussy about what delimiters are used).
3. DRI's DATE wants time input in strict HH:MM:SS format. This
program removes the unnecessary stringencies for time input
as for date input.
===============================================================================
Modification history:
?? Jan 87 Jon Saxton
Original release.
02 Mar 87 Jon Saxton
Bug fix - A spurious line of code in setClock() was causing a
one-day error when year mod 4 was 0 or 3.
=============================================================================*/
#include <stdio.h>
#include <signal.h>
#include <ctype.h>
/* ONE or NEITHER of the following should be defined */
#undef DS1216E
#undef DS1216
#ifdef DS1216E
#define SWaddr 0x6100 /* I had one of each installed while writing */
#endif /* this program and they were in different */
#ifdef DS1216 /* sockets. */
#define SWaddr 0x2100
#endif
#define TPAbank 1
#define SWbank -4
#define A2 4
unsigned char buf[8];
unsigned char key[8] = {0xC5,0x3A,0xA3,0x5C,0xC5,0x3A,0xA3,0x5C};
unsigned char b64[64];
unsigned char b1[1];
/* BDOS functions */
#define GET_VSN 12
#define GET_TIME 105
#define SET_SCB 49
#define DATE 0x58 /* Offset of date/time within SCB */
#define WORD 0xFE /* Signal to set a word value in SCB */
#define BYTE 0xFF /* Signal to set a byte value in SCB */
/* BIOS functions */
#define TIME 26
struct
{
unsigned short int days;
unsigned char hour, minute, second;
}
time_pb;
struct
{
unsigned char offset;
unsigned char worb;
unsigned short int value;
}
scb_pb;
struct
{
unsigned char year,
month,
day,
hour,
minute,
second;
}
time;
#ifndef SWaddr
struct
{
unsigned char func;
unsigned char aVal;
unsigned short int bcVal,
deVal,
hlVal;
}
bios_pb;
#endif
unsigned char sentinel[] = {'U','S','A','-','>'};
unsigned char usa = 0; /* Use wierd MM/DD/YY format if non-0 */
extern unsigned int bdos();
unsigned char monthName[] =
{"Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec"};
unsigned char dayName[] =
{"Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat"};
unsigned short int atou(p)
unsigned char *p;
{
unsigned int g = 0;
while (isspace(*p))
++p;
while (isdigit(*p))
g = g*10 + *p++ - '0';
return g;
}
/*-----------------------------------------------------------------------------
g e t D a t e and g e t T i m e
============= =============
Prompt for date or time and get input from keyboard.
-----------------------------------------------------------------------------*/
void getDate()
{
printf("Enter today's date (%s/YY): ", (usa ? "MM/DD" : "DD/MM"));
gets(b64);
}
void getTime()
{
printf("Enter the time (HH:MM:SS): ");
gets(b64);
}
/*-----------------------------------------------------------------------------
u s a g e
=========
This routine is entered if we couldn't make sense of the command-
line. Displays a very terse instruction on usage.
------------------------------------------------------------------------------*/
void usage()
{
#ifdef SWaddr
printf("date [read | set | continuous | %s/yy hh:mm:ss]",
#else
printf("date [set | continuous | %s/yy hh:mm:ss]",
#endif
(usa ? "mm/dd" : "dd/mm"));
}
/*-----------------------------------------------------------------------------
a M o n t h
===========
Checks a string to see if it is a month name. Insensitive to
upper/lower case. Only looks at first three characters. Returns
month number (1-12) or 0.
-----------------------------------------------------------------------------*/
unsigned short int aMonth(c)
unsigned char *c;
{
unsigned short int m;
unsigned char *n, *s, looking = 1;
for (m = 0; looking && m < 12; ++m)
{
n = &monthName[m*4];
s = c;
if (isalpha(*s) && (*s++ & 0x5F) == *n++ &&
isalpha(*s) && (*s++ | 0x20) == *n++ &&
isalpha(*s) && (*s | 0x20) == *n)
looking = 0;
}
return looking ? 0 : m;
}
/*-----------------------------------------------------------------------------
l e a p
=======
Returns 1 if the year is a leap year, 0 otherwise. Crude and
takes advantage of the fact that 2000 will be a leap year by
ignoring the checks for non-leap end-of-century years.
-----------------------------------------------------------------------------*/
unsigned short int leap(year)
unsigned short int year;
{
return (year & 3) ? 0 : 1;
}
/*-----------------------------------------------------------------------------
y e a r L e n g t h
===================
Determines the number of days in a year. Crude, and takes full
advantage of the fact that 2000 will be a leap year.
-----------------------------------------------------------------------------*/
unsigned int yearLength(year)
unsigned short int year;
{
return 365 + leap(year);
}
/*-----------------------------------------------------------------------------
d a y s I n
===========
Returns the number of days in a month (taking leap years into
account).
-----------------------------------------------------------------------------*/
unsigned char daysIn(month,year)
unsigned short int month, year;
{
static unsigned char monthLength[] = {31,28,31,30,31,30,31,31,30,31,30,31};
return monthLength[month] + ((month == 1) ? leap(year) : 0);
}
/*-----------------------------------------------------------------------------
e x p a n d and c o n t r a c t
=========== ===============
expand() takes an array of 64 bits and blows it out to an array
of 64 bytes with the low-order bit of each byte set according to
the corresponding bit in the bit array.
contract() does the reverse and builds a bit array from the low-
order bits of a byte array.
-----------------------------------------------------------------------------*/
void expand(narrow,wide)
unsigned char narrow[], *wide;
{
int byte, bit;
for (byte=0; byte<8; ++byte)
for (bit=0; bit<8; ++bit)
*wide++ = (narrow[byte] >> bit) & 1;
}
void contract(wide,narrow)
unsigned char *wide, *narrow;
{
int byte, bit;
unsigned char val;
for (byte=0; byte<8; ++byte)
{
val = 0;
for(bit=0; bit<8; ++bit)
val |= (*wide++ & 1) << bit;
narrow[byte] = val;
}
}
/*-----------------------------------------------------------------------------
u n b c d
=========
Converts a BCD byte to a binary number.
-----------------------------------------------------------------------------*/
unsigned short int unbcd(v)
unsigned char v;
{
return (v >> 4) * 10 + (v & 15);
}
/*-----------------------------------------------------------------------------
b c d
=====
Converts a binary number to a BCD byte.
-----------------------------------------------------------------------------*/
unsigned char bcd(v)
unsigned short int v;
{
return ((v / 10) << 4) | (v % 10);
}
/*-----------------------------------------------------------------------------
r e a d C l o c k
=================
Reads the hardware clock, converts the time to DRI format and
sets the date and time fields in the SCB. Thereafter, the CIO
interrupts keep the system clock ticking.
-----------------------------------------------------------------------------*/
void readClock()
{
#ifdef SWaddr
extern void ibmm(); /* Inter-bank memory move - parameters are:-
Source address,
Source bank number,
Destination address,
Destiation bank number,
Number of bytes to transfer.
*/
#endif
extern void ei(), di();
unsigned int y, m;
#ifdef DS1216
ibmm(SWaddr,SWbank,b64,TPAbank,64);
expand(key,b64); /* Expand key to 64 bytes */
ibmm(b64,TPAbank,SWaddr,SWbank,64); /* Write key to unlock clock */
ibmm(SWaddr,SWbank,b64,TPAbank,64); /* Read clock */
contract(b64,buf); /* Contract clock readout */
#endif
#ifdef DS1216E
for (y=64; y--;)
ibmm(SWaddr+A2,SWbank,b64,TPAbank,1);
expand(key,b64); /* Expand key to 64 bytes */
for (y=0; y<64; ++y)
ibmm(SWaddr+b64[y],SWbank,b1,TPAbank,1);
for (y=0; y<64; ++y)
ibmm(SWaddr+A2,SWbank,&b64[y],TPAbank,1); /* Read clock */
contract(b64,buf); /* Contract clock readout */
#endif
/* buf[] now contains the clock readout in BCD as follows ....
buf[0] 1/100 second
buf[1] second
buf[2] minute
buf[3] hour
buf[4] 12/24-hr flag and day of week
buf[5] day
buf[6] month
buf[7] year
We convert it to DRI format .... */
time_pb.days = 0;
for (y=78; y < unbcd(buf[7]); ++y)
time_pb.days += yearLength(y);
for (m=0; m < unbcd(buf[6])-1; ++m)
time_pb.days += daysIn(m,y);
time_pb.days += unbcd(buf[5]);
time_pb.hour = buf[3];
time_pb.minute = buf[2];
time_pb.second = buf[1];
/* Now we update the SCB fields. Note that we must do this with interrupts
disabled so we don't get a clock rollover. Also, we must directly update
the SCB fields because the standard BDOS function (104) for setting the
clock clears the seconds field!!! */
scb_pb.offset = DATE;
scb_pb.worb = WORD;
scb_pb.value = time_pb.days;
di();
bdos(SET_SCB,&scb_pb);
scb_pb.offset += 2;
scb_pb.worb = BYTE;
scb_pb.value = time_pb.hour;
bdos(SET_SCB,&scb_pb);
scb_pb.offset++;
scb_pb.value = time_pb.minute;
bdos(SET_SCB,&scb_pb);
scb_pb.offset++;
scb_pb.value = time_pb.second;
bdos(SET_SCB,&scb_pb);
ei();
/* If neither SmartWatch socket is present then we want to tell the CP/M+
BIOS that it should update its clock. */
#ifndef SWaddr
bios_pb.bcVal = 0xFF; /* Tell BIOS to set h/w clock */
bios_pb.func = TIME;
bdos(50,&bios_pb);
#endif
}
/*-----------------------------------------------------------------------------
s h o w T i m e
===============
Displays the current date and time. May loop pending a keypress.
-----------------------------------------------------------------------------*/
void showTime(looping)
unsigned char looping;
{
short int y, m, d, w;
unsigned char ss = 0xFF;
do
{
while ((time_pb.second = bdos(GET_TIME, &time_pb)) == ss)
if (looping)
if (kbhit())
{
looping = 0;
getch();
}
ss = time_pb.second;
w = ((d = time_pb.days) + 6) % 7;
for (y = 78; d > yearLength(y); ++y)
d -= yearLength(y);
for (m = 0; d > daysIn(m,y); ++m)
d -= daysIn(m,y);
printf("\r%s %d %s %d; %02x:%02x:%02x ",
&dayName[w*4],
d, &monthName[m*4], y+1900,
time_pb.hour,
time_pb.minute,
time_pb.second);
}
while (looping);
}
/*-----------------------------------------------------------------------------
p a r s e D a t e
=================
-----------------------------------------------------------------------------*/
unsigned char parseDate(p)
unsigned char *p;
{
unsigned short int v;
unsigned char a1, mf;
mf = usa;
a1 = 0;
while (isspace(*p))
++p;
if (isalpha(*p)) /* then may be (should be) a month name */
{
time.month = aMonth(p);
a1 = 1;
while (isalpha(*p))
++p;
}
else if (isdigit(*p)) /* then should be a month or day number */
{
time.month = atou(p); /* treat it as a month for now */
while (isdigit(*p))
++p;
}
while (*p && !isalpha(*p) && !isdigit(*p))
++p;
if (isalpha(*p)) /* then should be a month name */
{
v = aMonth(p);
if (a1) /* then we've already seen a month name */
return 1; /* so this is wrong */
time.day = time.month;
time.month = v;
while (isalpha(*p))
++p;
}
else if (isdigit(*p))
{
v = atou(p);
while (isdigit(*p))
++p;
if (a1) /* first thing was a month name */
time.day = v; /* this must be a day number */
else /* first thing was a number */
if (mf) /* if MM/DD/YY format */
time.day = v; /* this is a day */
else /* else first thing was a day */
{ /* and this is a month */
time.day = time.month;
time.month = v;
}
}
while (*p && !isdigit(*p))
++p;
if (isdigit(*p))
{
v = atou(p);
if (v > 1900)
v -= 1900;
if (v < 78 || v > 114)
v = 0;
time.year = v;
}
if (time.year == 0 || time.day == 0 || time.month == 0)
return 1;
if (time.month > 12)
return 1;
if (time.day > daysIn(time.month-1,time.year))
return 1;
return 0;
}
/*-----------------------------------------------------------------------------
p a r s e T i m e
=================
-----------------------------------------------------------------------------*/
unsigned char parseTime(p)
unsigned char *p;
{
unsigned short int v;
while (isspace(*p))
++p;
if (!isdigit(*p))
return 1;
if ((v = atou(p)) > 23)
return 1;
time.hour = v;
while (isdigit(*p))
++p;
while (*p && !isdigit(*p))
++p;
if ((v = atou(p)) > 59)
return 1;
time.minute = v;
while (isdigit(*p))
++p;
while (*p && !isdigit(*p))
++p;
if ((v = atou(p)) > 59)
return 1;
time.second = v;
return 0;
}
/*-----------------------------------------------------------------------------
s e t C l o c k
===============
Obtains date and time specifications and sets the system clock.
-----------------------------------------------------------------------------*/
void setClock(dateStr, timeStr)
unsigned char *dateStr, *timeStr;
{
unsigned short int y, m, days = 0;
unsigned char prompt = 0;
#ifdef SWaddr
extern void ibmm(); /* Inter-bank memory move - parameters are:-
Source address,
Source bank number,
Destination address,
Destiation bank number,
Number of bytes to transfer.
*/
#endif
if (dateStr == NULL)
{
prompt = 1;
getDate();
dateStr = b64;
}
if (parseDate(dateStr))
{
puts("Badly-formed date");
exit();
}
if (timeStr == NULL)
{
getTime();
timeStr = b64;
}
if (parseTime(timeStr))
{
puts("Badly-formed time");
exit();
}
/* If we get here then we have a valid date and time. Now program the clock */
buf[7] = bcd(time.year);
buf[6] = bcd(time.month);
buf[5] = bcd(time.day);
buf[3] = bcd(time.hour);
buf[2] = bcd(time.minute);
buf[1] = bcd(time.second);
buf[0] = 0;
days = 0;
for (y=78; y < time.year; ++y)
days += yearLength(y);
for (m=0; m < time.month-1; ++m)
days += daysIn(m,time.year);
days += time.day;
buf[4] = (days+6) % 7 + 16;
if (prompt)
{
printf("Press any key to set time: ");
if (getch() == 3) /* Allow ^C abort */
exit();
putchar('\n');
}
#ifdef DS1216
ibmm(SWaddr,SWbank,b64,TPAbank,64);
expand(key,b64);
ibmm(b64,TPAbank,SWaddr,SWbank,64);
expand(buf,b64);
ibmm(b64,TPAbank,SWaddr,SWbank,64);
#endif
#ifdef DS1216E
for (y=64; y--;)
ibmm(SWaddr+A2,SWbank,b64,TPAbank,1);
expand(key,b64);
for (y=0; y<64; ++y)
ibmm(SWaddr+b64[y],SWbank,b1,TPAbank,1);
expand(buf,b64);
for (y=0; y<64; ++y)
ibmm(SWaddr+b64[y],SWbank,b1,TPAbank,1);
#endif
/* If neither clock is present then the properly-formatted date is still
in buf[] and in the struct time */
}
/*-----------------------------------------------------------------------------
m a i n
=======
Entry point to this program. Performs primary command-line
analysis and invokes appropriate function.
-----------------------------------------------------------------------------*/
main(argc, argv)
short int argc;
unsigned char *argv[];
{
signal(SIGINT, SIG_IGN);
if (bdos(GET_VSN,0) < 0x30)
{
puts("This program requires CP/M 3");
exit();
}
switch (argc)
{
case 1:
showTime(0);
break;
case 2:
switch (*(argv[1]) & 0x5F)
{
case 'S':
setClock(NULL,NULL);
#ifdef SWaddr
case 'R':
#endif
readClock();
showTime(0);
break;
case 'C':
showTime(1);
break;
default:
usage();
}
break;
case 3:
setClock(argv[1],argv[2]);
readClock();
showTime(0);
break;
default:
usage();
}
}