_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();