Category : Files from Magazines
Archive   : DDJ0691.ZIP
Filename : CLOCK_C.ASC

 
Output of file : CLOCK_C.ASC contained in archive : DDJ0691.ZIP

_USING THE REAL-TIME CLOCK_
by Kenneth Roach

[TURBO C VERSION]


[TIME_C.C]

/*
** TIME_C
** (C) Copyright 1990 by Kenneth Roach
** Version date: 3 November, 1990
**
** This program uses the time and date functions provided by the Turbo-C
** compiler, as well as similar functions contained in the module TIMELIB.C.
** TIME_C calls each function for five seconds, counting the number of
** times the function in question was called. It then compares the number
** of times each function was called and displays the results. Following
** this, it displays the current date and time obtained from the
** get_rtc_time function, and as reported and converted by the rtc_time
** and ctime2 functions.
*/

#include
#include
#include
#include "timelib.h"

long grt_count = 0L; /* counter for get_rtc_time() calls */
long grd_count = 0L; /* counter for get_rtc_date() calls */
long rt_count = 0L; /* counter for rtc_time() calls */
long gt_count = 0L; /* counter for gettime() calls */
long gd_count = 0L; /* counter for getdate() calls */
long t_count = 0L; /* counter for time() calls */
long t2_count = 0L; /* counter for time2() calls */
long ct2_count = 0L; /* counter for ctime2() calls */
long ct_count = 0L; /* counter for ctime() calls */
struct time t; /* used in testing of gettime, get_rtc_time */
struct date d; /* used in testing of getdate, get_rtc_date */
char *str; /* used in testing ctime, ctime2 */
time_t timer; /* used in testing time, time2, rtc_time */
long temp;

#define TEST_TIME 5120L /* 5 seconds * 1024 interrupts per */

/*
** test performance of real time clock based time functions
*/

void test_rtc()
{
printf("\nTesting get_rtc_time...");
temp = rtc_clock();
do {
get_rtc_time(&t);
++grt_count;
} while(rtc_clock() - temp < TEST_TIME); /* count for 5 seconds */

printf("\nTesting get_rtc_date...");
temp = rtc_clock();
do {
get_rtc_date(&d);
++grd_count;
} while(rtc_clock() - temp < TEST_TIME); /* count for 5 seconds */

printf("\nTesting rtc_time...");
temp = rtc_clock();
do {
rtc_time(&timer);
++rt_count;
} while(rtc_clock() - temp < TEST_TIME); /* count for 5 seconds */

printf("\nTesting ctime2...");
temp = rtc_clock();
do {
str = ctime2(&timer);
++ct2_count;
} while(rtc_clock() - temp < TEST_TIME); /* count for 5 seconds */
}

/*
** test performance of C's DOS based time functions
*/

void test_c()
{

printf("\nTesting gettime...");
temp = rtc_clock();
do {
gettime(&t);
++gt_count;
} while(rtc_clock() - temp < TEST_TIME); /* count for 5 seconds */

printf("\nTesting getdate...");
temp = rtc_clock();
do {
getdate(&d);
++gd_count;
} while(rtc_clock() - temp < TEST_TIME); /* count for 5 seconds */

printf("\nTesting time...");
temp = rtc_clock();
do {
time(&timer);
++t_count;
} while(rtc_clock() - temp < TEST_TIME); /* count for 5 seconds */

printf("\nTesting time2...");
temp = rtc_clock();
do {
time2(&timer);
++t2_count;
} while(rtc_clock() - temp < TEST_TIME); /* count for 5 seconds */

printf("\nTesting ctime...");
temp = rtc_clock();
do {
str = ctime(&timer);
++ct_count;
} while(rtc_clock() - temp < TEST_TIME); /* count for 5 seconds */
}

/*
** determine percentage one value represents of another
*/

long percent(long count1,long count2)
{
long temp;
temp = (count1 * 100L) / count2;
if(((count1 * 100L) % count2) >= 50L)
++temp;
return(temp);
}

/*
** show results of timing tests
*/

void display_results()
{
printf("\nTest Summary:\n");
printf("\ngettime() called %6ld times\n",gt_count);
printf("get_rtc_time() called %6ld times\n",grt_count);
if(grt_count > gt_count)
printf("get_rtc_time() was %02ld%% the speed of gettime()\n",
percent(grt_count,gt_count));
else
printf("gettime() was %02ld%% the speed of get_rtc_time()\n",
percent(gt_count,grt_count));

printf("\ngetdate() called %6ld times\n",gd_count);
printf("get_rtc_date() called %6ld times\n",grd_count);
if(grd_count > gd_count)
printf("get_rtc_date() was %02ld%% the speed of getdate()\n",
percent(grd_count,gd_count));
else
printf("getdate() was %02ld%% the speed of get_rtc_date()\n",
percent(gd_count,grd_count));

printf("\ntime() called %6ld times\n",t_count);
printf("time2() called %6ld times\n",t2_count);
printf("rtc_time() called %6ld times\n",rt_count);
if(rt_count > t_count)
printf("rtc_time() was %02ld%% the speed of time()\n",
percent(rt_count,t_count));
else
printf("time() was %02ld%% the speed of rtc_time()\n",
percent(t_count,rt_count));

printf("\nctime() called %6ld times\n",ct_count);
printf("ctime2() called %6ld times\n",ct2_count);
if(ct2_count > ct_count)
printf("ctime2() was %02ld%% the speed of ctime()\n",
percent(ct2_count,ct_count));
else
printf("ctime() was %02ld%% the speed of ctime2()\n",
percent(ct_count,ct2_count));
}

void main()
{
enable_rtc_ints();

clrscr();

test_rtc(); /* test the functions using the real time clock */
test_c(); /* test the normal C/DOS based time functions */

display_results();

printf("\nEnd of test.\nStart time display.\nDepress any key to stop\n\n");

while(!kbhit())
{
get_rtc_time(&t);
rtc_time(&timer);
printf("\r%02.2d:%02.2d:%02.2d.%02.2d %-24.24s",
t.ti_hour,t.ti_min,t.ti_sec,t.ti_hund,ctime(&timer));
}

disable_rtc_ints();
}



[TIMELIB.C}


/*
** TIMELIB.C
** (C) Copyright 1990 by Kenneth Roach
** Version date: 3 November, 1990
**
** This module contains functions similar to ANSI C's time(), gettime() and
** getdate(), and clock() functions, but which are based on use of the AT
** class of system's real time clock. Additionally, functions are provided
** to enable and disable periodic interrupts from the real time clock along
** with an intterupt handler for same. Interrupts from the real time clock
** are provided at a rate of 1024 per second, and a function is provided to
** return the number of interrupts received in the current second. Also
** provided is a replacement for the C language's ctime() function which is
** modestly faster.
*/

#pragma inline

#include
#include
#include
#include "timelib.h"

#define CMOSFLAG 0x70
#define CMOSDATA 0x71
#define SECONDS_REQ 0x00
#define MINUTES_REQ 0x02
#define HOURS_REQ 0x04
#define STATUSA 0x0a
#define STATUSB 0x0b
#define STATUSC 0x0c
#define DATE_REQ 0x07
#define MONTH_REQ 0x08
#define YEAR_REQ 0x09
#define CENTURY_REQ 0x32
#define UPDATE 0x80
#define BCD 0x04
#define MASK_24 0x02
#define HINIBBLE 0xf0
#define LONIBBLE 0x0f

#define APRIL 4
#define JUNE 6
#define SEPTEMBER 9
#define NOVEMBER 11
#define FEBRUARY 2

#define RTC_VEC 0x70
#define IMR2 0xa1
#define CMD1 0x20
#define CMD2 0xa0
#define EOI 0x20
#define RTC_MASK 0xfe
#define RTC_FLAG 0x40

#define SECS_PER_DAY 86400L
#define SECS_PER_YEAR 31536000L
#define BIAS_10_YEARS 315532800L /* difference between 1970 and 1980 */
#define BASE_YEAR 1980
#define SECS_PER_MIN 60
#define SECS_PER_HOUR 3600
#define MINS_PER_HOUR 60
#define DAYS_PER_YEAR 365
#define DAYS_PER_WEEK 7
#define TUESDAY 3 /* day of week for 1-1-1980 */

#define bcd_bin(x) (bcd) ? ((((x & HINIBBLE) >> 4)\
* 10) + (x & LONIBBLE)) : (x)

char months[12][4] = {"Jan","Feb","Mar","Apr","May","Jun",
"Jul","Aug","Sep","Oct","Nov","Dec"};
char days[7][4] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};

extern long timezone;

volatile int rtc_count = 0;
volatile long tick_count = 0L;

void interrupt (*old_rtc_vec)();

int func_init = 0;
int bcd = 0;
int dst = 0;
unsigned int old_mask;
char time_str[26];

/*
** replacement for the Turbo-C clock() function. rtc_clock returns
** a value corresponding to the number of periodic interrupts which
** have occurred since interrupts from the real time clock were
** enabled. The value will remain positive for some 24 days from
** initialization.
*/

clock_t rtc_clock()
{
return(tick_count);
}

/*
** millicount returns the real time clock periodic interrupt count for
** the current second. Range of value is 0 to 1023.
*/

int milli_count()
{
return(rtc_count);
}

/*
** real time clock interrupt handler
*/

void interrupt rtc()
{
asm cli;
outportb(CMOSFLAG,STATUSC); /* get interrupt register identification */
if((inportb(CMOSDATA) & 0x40) != 0) /* if a "periodic" interrupt */
{
if(++rtc_count == 1024) /* update nbr times ISR called this sec */
rtc_count = 0; /* if start of new second, reset rtc_count */
else
{
outportb(CMOSFLAG,STATUSA); /* check it again for accuracy */
if(inportb(CMOSDATA) & UPDATE)
rtc_count = 0;
}
++tick_count; /* update total number of times called */
outportb(CMD1,EOI); /* signal end of interrupt to primary 8259 */
outportb(CMD2,EOI); /* signal end of interrupt to chained 8259 */
}
else
(*old_rtc_vec)();
asm sti;
}

/*
** turn on interrupts from the real time clock
*/

void enable_rtc_ints()
{
rtc_count = 0;
tick_count = 0L;
old_rtc_vec = getvect(RTC_VEC);
setvect(RTC_VEC,rtc); /* point to interrupt handler */
outportb(IMR2,inportb(IMR2) & RTC_MASK); /* enable clock interrupt */
outportb(CMOSFLAG,STATUSB);
old_mask = inportb(CMOSDATA); /* get rtc mask register */
outportb(CMOSFLAG,STATUSB);
outportb(CMOSDATA,old_mask | RTC_FLAG); /* enable 1k interrupts */
}

/*
** turn off interrupts from the real time clock
*/

void disable_rtc_ints()
{
outportb(CMOSFLAG,STATUSB);
outportb(CMOSDATA,old_mask); /* turn off periodic interrupts */
outportb(IMR2,inportb(IMR2) & ~RTC_MASK); /* diable RTC interrupts */
setvect(RTC_VEC,old_rtc_vec); /* restore old interrupt vector */
}

/*
** replacement for the C language's ctime() function
*/

char *ctime2(time_t *t)
{
unsigned int hr,mn,sc;
unsigned int yr,mo,dy;
unsigned int bias,dw;
int junk,s,tp;
long temp;
time_t time;

time = *t - BIAS_10_YEARS;
if(dst)
time -= 3600L; /* compensate for daylight savings */
time -= timezone;
temp = time % SECS_PER_DAY; /* get seconds left for this day */
hr = temp / SECS_PER_HOUR; /* determine hours this day */
temp %= SECS_PER_HOUR; /* lose hours this day */
mn = temp / MINS_PER_HOUR; /* determine minutes this hour */
sc = temp % SECS_PER_MIN; /* determine seconds this minute */

asm cli;
do /* following code duplicated for speed */
outportb(CMOSFLAG,STATUSA); /* wait until not in update cycle */
while(inportb(CMOSDATA) & UPDATE);
outportb(CMOSFLAG,CENTURY_REQ); s = inportb(CMOSDATA); tp = bcd_bin(s);
outportb(CMOSFLAG,YEAR_REQ); s = inportb(CMOSDATA); bias = bcd_bin(s);
outportb(CMOSFLAG,MONTH_REQ); s = inportb(CMOSDATA); mo = bcd_bin(s);
outportb(CMOSFLAG,DATE_REQ); s = inportb(CMOSDATA); dy = bcd_bin(s);
asm sti;

bias = bias + tp * 100 - BASE_YEAR;
temp = time / SECS_PER_DAY; /* get number of days for this value */
yr = temp / DAYS_PER_YEAR; /* now convert it to years */
bias >>= 2; /* get leap year days for value */
dy = temp - yr * DAYS_PER_YEAR - bias; /* get unprocessed days */
yr += BASE_YEAR; /* now add in the 1980 start date */
dw = time / SECS_PER_DAY + TUESDAY; /* 1-1-80 was a Tuesday */
dw %= DAYS_PER_WEEK; /* determine weekday */
--dw;
s = 1; /* now determine the month's name */
mo = 0;
while(s) /* process total remaining days for year */
{
junk = 0;
switch(s)
{
case APRIL: /* first do months with 30 days */
case JUNE:
case SEPTEMBER:
case NOVEMBER: if(dy >= 30)
junk = 30; break;
case FEBRUARY: if((yr >> 2) == 0) /* special case february */
if(dy >= 29)
junk = 29; /* process leap year */
else
;
else if(dy >= 28) /* not a leap year */
junk = 28; break;
default: if(dy >= 31)
junk = 31; /* else month has 31 days */
}
if(junk)
{
++mo; /* account for month just processed */
++s; /* bump case index */
dy -= junk; /* subtract days just processed */
}
else
s = 0; /* Dy is less than 1 month, clear while var */
}

time_str[0] = days[dw][0]; /* now convert all values to a string */
time_str[1] = days[dw][1]; /* avoid call to sprintf for speed */
time_str[2] = days[dw][2];
time_str[4] = months[mo][0];
time_str[5] = months[mo][1];
time_str[6] = months[mo][2];
time_str[8] = dy / 10 + '0';
time_str[9] = dy % 10 + '0';
time_str[11] = hr / 10 + '0';
time_str[12] = hr % 10 + '0';
time_str[14] = mn / 10 + '0';
time_str[15] = mn % 10 + '0';
time_str[17] = sc / 10 + '0';
time_str[18] = sc % 10 + '0';
time_str[20] = yr / 1000 + '0'; yr %= 1000;
time_str[21] = yr / 100 + '0'; yr %= 100;
time_str[22] = yr / 10 + '0';
time_str[23] = yr % 10 + '0';
time_str[24] = '\n';
time_str[25] = 0;
time_str[3] = time_str[7] = time_str[10] = time_str[19] = ' ';
time_str[13] = time_str[16] = ':';
return(time_str);
}

/*
** replacement for Turbo-C's gettime() function
*/

void get_rtc_time(struct time *timep)
{
int h,m,s;
if(!func_init)
init_time(); /* assure we have info we need */
asm cli;
do
outportb(CMOSFLAG,STATUSA); /* wait until not in update cycle */
while(inportb(CMOSDATA) & UPDATE);

outportb(CMOSFLAG,HOURS_REQ); /* get hours */
h = inportb(CMOSDATA); timep->ti_hour = bcd_bin(h);
outportb(CMOSFLAG,MINUTES_REQ); /* get minutes */
m = inportb(CMOSDATA); timep->ti_min = bcd_bin(m);
outportb(CMOSFLAG,SECONDS_REQ); /* get seconds */
s = inportb(CMOSDATA); timep->ti_sec = bcd_bin(s);
asm sti;
s = rtc_count / 10; /* rtc_count goes to 1024 */
if(s > 75) /* correct for values to 102 each second */
s -= 3;
else if(s > 50)
s -= 2;
else if(s > 25)
--s;
timep->ti_hund = s;
}

/*
** replacement for Turbo-C's getdate() function
*/

void get_rtc_date(struct date *datep)
{
int d,m,y,t,s;
if(!func_init)
init_time(); /* assure we have info we need */
asm cli;
do
outportb(CMOSFLAG,STATUSA); /* wait until not in update cycle */
while(inportb(CMOSDATA) & UPDATE);

outportb(CMOSFLAG,CENTURY_REQ); /* get century */
s = inportb(CMOSDATA); t = bcd_bin(s);
outportb(CMOSFLAG,YEAR_REQ); /* get year */
y = inportb(CMOSDATA); datep->da_year = bcd_bin(y);
outportb(CMOSFLAG,MONTH_REQ); /* get month */
m = inportb(CMOSDATA); datep->da_mon = bcd_bin(m);
outportb(CMOSFLAG,DATE_REQ); /* get day */
d = inportb(CMOSDATA); datep->da_day = bcd_bin(d);
asm sti;
datep->da_year = datep->da_year + t * 100; /* add in century */
}

/*
** replacement for Turbo-C's time() function
*/

time_t rtc_time(time_t *result)
{
time_t hr;
unsigned s,b,yr,sc,mn,mo,dy;
if(!func_init)
init_time(); /* assure we have info we need */
asm cli; /* following code is duplicated for speed */
do
outportb(CMOSFLAG,STATUSA); /* wait until not update cycle */
while(inportb(CMOSDATA) & UPDATE);

outportb(CMOSFLAG,SECONDS_REQ); /* get seconds */
s = inportb(CMOSDATA); sc = bcd_bin(s);
outportb(CMOSFLAG,MINUTES_REQ); /* get minutes */
s = inportb(CMOSDATA); mn = bcd_bin(s);
outportb(CMOSFLAG,HOURS_REQ); /* get hours */
s = inportb(CMOSDATA); hr = bcd_bin(s);

outportb(CMOSFLAG,YEAR_REQ); /* get year */
s = inportb(CMOSDATA); yr = bcd_bin(s);
outportb(CMOSFLAG,CENTURY_REQ); /* get century */
s = inportb(CMOSDATA); b = bcd_bin(s);
outportb(CMOSFLAG,MONTH_REQ); /* get month */
s = inportb(CMOSDATA); mo = bcd_bin(s);
outportb(CMOSFLAG,DATE_REQ); /* get day */
s = inportb(CMOSDATA); dy = bcd_bin(s);
asm sti;

mn = mn * 60 + sc; /* convert minutes to seconds */
hr = hr * 3600 + mn + timezone; /* convert hours to seconds */
yr = yr + b * 100 - 1980; /* get years since 1980 */
dy = dy + (yr >> 2); /* correct days for leap years */
s = 1;
while(s < mo) /* add days for this year */
switch(s++)
{
case APRIL: /* months with 30 days */
case JUNE:
case SEPTEMBER:
case NOVEMBER: dy += 30L; break;
case FEBRUARY: dy += ((yr >> 2) == 0) ? 29L : 28L; break;
default: dy += 31L; /* else month has 31 days */
}
if(dst)
hr -= 3600L; /* compensate for daylight savings */
return(*result = (yr * SECS_PER_YEAR + /* return final value */
dy * SECS_PER_DAY +
hr + BIAS_10_YEARS)); /* 10 yr bias for difference */
/* between 1970 and 1980 (secs) */
}

/*
** replacement for Turbo-C's time() function
*/

time_t time2(time_t *result)
{
time_t hr;
unsigned s,yr,mn,mo,dy;
struct date d;
struct time t;
asm cli;

getdate(&d);
gettime(&t);
mn = t.ti_min * 60 + t.ti_sec; /* convert minutes to seconds */
hr = t.ti_hour * 3600 + mn + timezone; /* convert hours to seconds */
yr = d.da_year - 1980; /* get years since 1980 */
dy = d.da_day + (yr >> 2); /* correct days for leap years */
s = 1;
mo = d.da_mon;
while(s < mo) /* add days for this year */
switch(s++)
{
case APRIL: /* months with 30 days */
case JUNE:
case SEPTEMBER:
case NOVEMBER: dy += 30L; break;
case FEBRUARY: dy += ((yr >> 2) == 0) ? 29L : 28L; break;
default: dy += 31L; /* else month has 31 days */
}
if(dst)
hr -= 3600L; /* compensate for daylight savings */
asm sti;
return(*result = (yr * SECS_PER_YEAR + /* return final value */
dy * SECS_PER_DAY +
hr + BIAS_10_YEARS)); /* 10 yr bias for difference */
/* between 1970 and 1980 (secs) */
}
/*
** initialize variables for rtc time and date functions
*/

void init_time()
{
struct tm *cur_time;
time_t timer;
time(&timer); /* kick start TC's time code */
cur_time = localtime(&timer); /* check for daylight savings time */
dst = cur_time->tm_isdst;
outportb(CMOSFLAG,STATUSB); /* get mode the clock is in */
bcd = (inportb(CMOSDATA) & BCD) == 0; /* (binary or BCD) */
outportb(CMOSFLAG,STATUSB);
outportb(CMOSDATA,inportb(CMOSDATA) | MASK_24);/* force 24 hour mode */
func_init = 1;
}



[TIMELIB.H]


/*
** TIMELIB.H
**
** prototype declarations for TIMELIB.C
*/


clock_t rtc_clock();
int milli_count();
void enable_rtc_ints();
void disable_rtc_ints();
void get_rtc_time(struct time *timep);
void get_rtc_date(struct date *datep);
time_t rtc_time(time_t *result);
time_t time2(time_t *result);
void init_time();
char *ctime2();