home *** CD-ROM | disk | FTP | other *** search
-
- /* clockerr.c
- *
- * This program determines the number of seconds lost or gained by the
- * cmos clock on an AT computer. The program is invoked from the
- * command line using one of two possible command line parameters, like
- * this: clockerr s, or clockerr q.
- *
- * The s parameter is used the first time the program is run. The next
- * time the program is run it must use the q parameter to generate the
- * cmos clock statistics. The first time you run the program, (clockerr
- * s), you will be prompted for the correct date and time. Your entries
- * will be used to set the cmos clock. They will also be written to a
- * disk file called error.log. This file is created in the current
- * directory. If this file already exists then it means the cmos clock
- * has already been set by this program and further action is
- * inhibited. If you still want to reset the cmos clock then you have
- * to erase error.log first.
- *
- * The q parameter is used when you run this program anytime after the
- * first time. You will once again be prompted for the correct date and
- * time. These values will be used to compare the time as reported by
- * the cmos clock to the actual time. The difference between the cmos
- * reported time and the actual time is adjusted to account for the
- * length of time the cmos clock has been running since it was first
- * set by this program. The resulting value, seconds/month is reported
- * as a loss or gain, as appropriate.
- *
- * This program can be run with the q parameter anytime after the cmos
- * clock has been initially set by this program. However, the cmos
- * clock usually is not in error by more than a few minutes/month. This
- * means you will not obtain a particularly accurate value unless at
- * least several days have passed.
- */
-
- #include <time.h>
- #include <stdlib.h>
- #include <stdio.h>
- #include <dos.h>
- #include <math.h>
-
- #define RT_CLOCK 0x1A
- #define SET_RT_TIME 0x03
- #define SET_RT_DATE 0x05
- #define GET_SYS_TIME 0x2C
- #define GET_SYS_DATE 0x2A
- #define GET_RT_TIME 0x02
- #define GET_RT_DATE 0x04
- #define DOS_FUNC_21 0x21
- #define MONTH 2419200.0 /* Number of seconds in 28 days */
-
- void main(int argc, char *argv[]);
- void open_error_log(char *argv[]);
- int bcd_to_bin(int bcd);
- int bin_to_bcd(int bin);
- void set_time(void);
- void set_date(void);
- time_t cmos_time(void);
-
- FILE *fp;
-
- void main( int argc, char *argv[] )
- {
- time_t start_time, elapsed_cmos, time_now, elapsed_actual, error, cmos;
- struct tm *dst;
-
- system("cls");
- if(argc != 2)
- {
- puts("\nIncorrect usage: A command line parameter is needed.\n\n");
- puts("Example: First time program is run....clockerr s\n");
- puts(" Subsequent times.............clockerr q\n");
- exit(0);
- }
- if((*argv[1] != 's') && (*argv[1] != 'q'))
- {
- puts("\nInvalid parameter: Must be either s or q.");
- exit(0);
- }
- open_error_log(argv);
- switch(*argv[1])
- {
- case 's': /*
- * Program is started for the first time.
- *
- * Set the system date and time. Convert this time to a
- * value of type time_t. Call localtime() to determine if
- * daylight savings time is in effect. If dst->tm_isdst = 1
- * then subtract an hour to convert to standard time. Write
- * the start_time to error.log and exit.
- */
- set_date();
- set_time();
- time(&start_time);
- dst = localtime(&start_time);
- if(dst->tm_isdst) start_time -= 3600;
- fwrite(&start_time, sizeof(time_t), 1, fp);
- puts("\nThe CMOS clock has been initialized.\n");
- puts("The file, error.log has been created.\n");
- exit(0);
-
- case 'q': /*
- * Clock statistics are produced.
- *
- * Set the sysyem time and date correctly. The program
- * will get the time from the operating system later.
- */
- puts("\n\nTHE UNCORRECTED TIME AND DATE ARE DISPLAYED.");
- puts("ENTER THE CORRECT VALUES.\n");
- system("date");
- system("time");
-
- /*
- * Get the time at which the program was originally
- * started. This has been stored as a value of type
- * time_t in the disc file, error.log.
- */
- fread(&start_time, sizeof(time_t), 1, fp);
-
- /*
- * The current time as kept by the cmos clock is obtained
- * from a call to cmos_time(). The number of seconds that
- * have elapsed on the cmos clock is elapsed_cmos.
- */
- cmos = cmos_time();
- elapsed_cmos = cmos - start_time;
-
- /*
- * The correct time, time_now, is obtained from the system.
- * A call to localtime() sets dst if daylight savings time
- * is in effect. If dst = 1 then the time entered by the
- * user is converted to standard time, which the program
- * needs.
- */
- time(&time_now);
- dst = localtime(&time_now);
- if(dst->tm_isdst) time_now -= 3600;
-
- /*
- * elapsed_actual is the amount of time that has passed
- * since this program was first run, using the s parameter.
- * The number of seconds by which the cmos clock varies
- * from the actual elapsed time since then is computed
- * and normalized to a per month value.
- */
- elapsed_actual = time_now - start_time;
- error = floor((elapsed_cmos - elapsed_actual)*
- MONTH/elapsed_actual +.5);
- system("cls");
- puts("\n\n---------------------- REAL TIME CLOCK STATISTICS ----------------------\n");
- printf(" Correct time entered by user............%s\n",asctime(localtime(&time_now)));
- printf(" Time reported by CMOS clock.............%s\n",asctime(localtime(&cmos)));
- printf(" The CMOS clock was set on...............%s\n",asctime(localtime(&start_time)));
- if(elapsed_cmos <= elapsed_actual)
- printf(" The CMOS clock has lost.................%d seconds so far.\n\n", abs(elapsed_cmos - elapsed_actual));
- else
- printf(" The CMOS clock has gained...............%d seconds so far.\n\n", elapsed_cmos - elapsed_actual);
- if(error <= 0)
- printf(" The CMOS clock looses...................%d seconds/month.\n\n", abs(error));
- else
- printf(" The CMOS clock gains....................%d seconds/month.\n\n", error);
- puts("------------------------------------------------------------------------");
- exit(0);
- }
- }
-
- void set_time() /* Sets the system time then puts it in the cmos clock. */
- {
- union REGS reg;
- struct tm *dst;
- time_t now;
-
- /*
- * Set the system time. Setup for DOS call then return that time in REGS.
- * Current time is also stored in now and passed to localtime() to see if
- * daylight savings time is in effect. If so, then dst->tm_isdst = 1 and
- * structure returned by the DOS call must be corrected by subtracting
- * an hour (mod 24) from the field that represents the hour.
- */
- system("time");
- reg.h.ah = GET_SYS_TIME;
- int86(DOS_FUNC_21, ®, ®);
- time(&now);
- dst = localtime(&now);
- if(dst->tm_isdst)
- reg.h.ch = (reg.h.ch-1)<0 ? 23: reg.h.ch-1;
-
- /*
- * Convert returned values to BCD. Set daylight savings time option to 0.
- * Setup for and call BIOS with converted values to set the cmos clock.
- */
- reg.h.ch = bin_to_bcd(reg.h.ch);
- reg.h.cl = bin_to_bcd(reg.h.cl);
- reg.h.dh = bin_to_bcd(reg.h.dh);
- reg.h.dl = 0;
- reg.h.ah = SET_RT_TIME;
- int86(RT_CLOCK, ®, ®);
- }
-
- void set_date() /* Sets the system date then puts it in the cmos clock. */
- {
- union REGS reg;
- double year, century;
-
- /*
- * Set system date. Setup for DOS call then return that date in REGS.
- */
- system("date");
- reg.h.ah = GET_SYS_DATE;
- int86(DOS_FUNC_21, ®, ®);
-
- /*
- * Separate century/year into century and year, (eg. 1988 to 1900, 88)
- * then convert all returned values to BCD format, required by BIOS call.
- */
- year = reg.x.cx - 100*floor(.01*reg.x.cx);
- century = (reg.x.cx - year);
- reg.h.ch = bin_to_bcd((int)(.01*century));
- reg.h.cl = bin_to_bcd((int)year);
- reg.h.dh = bin_to_bcd(reg.h.dh);
- reg.h.dl = bin_to_bcd(reg.h.dl);
-
- /*
- * Setup for BIOS call. The date values obtained from the DOS call and
- * converted to the proper format above are used to set the cmos clock.
- */
- reg.h.ah = SET_RT_DATE;
- int86(RT_CLOCK, ®, ®);
- }
-
-
- void open_error_log( char *argv[] )
- {
- switch(*argv[1])
- {
- case 's': /*
- * Try to open a file with the r+ option. For this to be
- * successful the file must already exist. If it does we
- * probably don't want to overwrite it, hence the warning.
- * If it doesn't exist, create it if possible.
- */
- if((fp = fopen("error.log", "r+")) != NULL)
- {
- puts("\nThe CMOS clock has been set already. If you");
- puts("want to start over, erase error.log first.");
- exit(0);
- }
- else
- if((fp = fopen("error.log", "w")) == NULL)
- {
- puts("\nCan't open the required file.");
- exit(0);
- }
- break;
-
- case 'q': /*
- * The file error.log must exist to generate the clock
- * statistics.
- */
- if((fp = fopen("error.log", "r")) == NULL)
- {
- puts("\nCan't open the error file, or it doesn't exist.");
- puts("Probably, the CMOS clock was never initialized.");
- exit(0);
- }
- }
- }
-
- time_t cmos_time() /* Returns the elapsed time on the cmos clock. */
- {
- struct tm cmos;
- union REGS reg;
-
- /*
- * Setup for BIOS call. First get the time from the cmos clock, convert
- * the result from BCD to binary format and place in the structure, cmos.
- * Then do the same for the date. Finally call mktime() with a pointer to
- * the structure cmos. This converts the structure to a representation of
- * the time as a value of type time_t. This is the value returned.
- */
- reg.h.ah = GET_RT_TIME;
- int86(RT_CLOCK, ®, ®);
- cmos.tm_hour = bcd_to_bin(reg.h.ch);
- cmos.tm_min = bcd_to_bin(reg.h.cl);
- cmos.tm_sec = bcd_to_bin(reg.h.dh);
- cmos.tm_isdst =0;
- reg.h.ah = GET_RT_DATE;
- int86(RT_CLOCK, ®, ®);
- cmos.tm_year = bcd_to_bin(reg.h.ch)*100 + bcd_to_bin(reg.h.cl) - 1900;
- cmos.tm_mon = bcd_to_bin(reg.h.dh) - 1;
- cmos.tm_mday = bcd_to_bin(reg.h.dl);
- return mktime(&cmos);
- }
-
- int bcd_to_bin( int bcd ) /* Convert from BCD format to binary. */
- {
- return bcd - (bcd/16)*6;
- }
-
- int bin_to_bcd( int bin ) /* Convert from binary format to BCD. */
- {
- return bin + (bin/10)*6;
- }
-