📄 time.c
字号:
/*****************************************************************************
REALTIME CLOCK
EXPORTS:
unsigned long sys_time(void);
unsigned long year_month_date_to_time_t(unsigned short year,
unsigned char month, unsigned char date);
*****************************************************************************/
#include <x86.h> /* inportb(), outportb() */
/*****************************************************************************
use BCD mode, since binary mode seems to be buggy
*****************************************************************************/
static unsigned char read_cmos(unsigned char reg)
{
unsigned char high_digit, low_digit;
outportb(0x70, reg);
high_digit = low_digit = inportb(0x71);
/* convert from BCD to binary */
high_digit >>= 4;
high_digit &= 0x0F;
low_digit &= 0x0F;
return 10 * high_digit + low_digit;
}
/*****************************************************************************
Finds the number of days between two dates in the Gregorian calendar.
- it's a leap year if the year is divisible by 4,
- UNLESS the year is also divisible by 100,
- UNLESS the year is also divisible by 400
To compute Julian Day Number (JDN;
days since Nov 24, 4714 BC/BCE in Gregorian calendar):
days_between_dates(-4713, 327, curr_day_in_year, curr_year);
To compute days since Jan 1, 1970 (UNIX epoch):
days_between_dates(1970, 0, curr_day_in_year, curr_year);
or
days_between_dates(-4713, 327, curr_day_in_year, curr_year) + 2440588L;
This code divides the time between start_day/start_year and end_day/end_year
into "slices": fourcent (400-year) slices in the middle, bracketed on
either end by century slices, fouryear (4-year) slices, and year slices.
When used to compute JDN, this code produces the same results as the
code shown here:
http://serendipity.magnet.ch/hermetic/cal_stud/jdn.htm
IMHO, it's easier to see how the algorithm for this code works,
versus the code at the URL above.
*****************************************************************************/
static long days_between_dates(short start_year, unsigned short start_day,
short end_year, unsigned short end_day)
{
short fourcents, centuries, fouryears, years;
long days;
fourcents = end_year / 400 - start_year / 400;
centuries = end_year / 100 - start_year / 100 -
/* subtract from 'centuries' the centuries already accounted for by
'fourcents' */
fourcents * 4;
fouryears = end_year / 4 - start_year / 4 -
/* subtract from 'fouryears' the fouryears already accounted for by
'fourcents' and 'centuries' */
fourcents * 100 - centuries * 25;
years = end_year - start_year -
/* subtract from 'years' the years already accounted for by
'fourcents', 'centuries', and 'fouryears' */
400 * fourcents - 100 * centuries - 4 * fouryears;
/* add it up: 97 leap days every fourcent */
days = (365L * 400 + 97) * fourcents;
/* 24 leap days every residual century */
days += (365L * 100 + 24) * centuries;
/* 1 leap day every residual fouryear */
days += (365L * 4 + 1) * fouryears;
/* 0 leap days for residual years */
days += (365L * 1) * years;
/* residual days (need the cast!) */
days += ((long)end_day - start_day);
/* account for terminal leap year */
if(end_year % 4 == 0 && end_day >= 60)
{
days++;
if(end_year % 100 == 0)
days--;
if(end_year % 400 == 0)
days++;
}
/* xxx - what have I wrought? I don't know what's going on here,
but the code won't work properly without it */
if(end_year >= 0)
{
days++;
if(end_year % 4 == 0)
days--;
if(end_year % 100 == 0)
days++;
if(end_year % 400 == 0)
days--;
}
if(start_year > 0)
days--;
return days;
}
/*****************************************************************************
month and date start with 1, not with 0
*****************************************************************************/
#define EPOCH_YEAR 1970
#define EPOCH_DAY 0 /* Jan 1 */
unsigned long date_time_to_time_t(unsigned short year, unsigned char month,
unsigned char date, unsigned char hour, unsigned char min,
unsigned char sec)
{
static const unsigned short days_to_date[12] =
{
/* jan feb mar apr may jun jul aug sep oct nov dec */
0,
31,
31 + 28,
31 + 28 + 31,
31 + 28 + 31 + 30,
31 + 28 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30
};
/**/
unsigned long ret_val;
unsigned short day;
/* convert month and year to day-in-year */
if(month > 11 || date > 30)
return 0;
day = date + days_to_date[month];
/* convert to Unix JDN (UJDN) */
ret_val = days_between_dates(EPOCH_YEAR, EPOCH_DAY, year, day);
/* convert from days to seconds, adding time as you go */
ret_val *= 24;
ret_val += hour;
ret_val *= 60;
ret_val += min;
ret_val *= 60;
ret_val += sec;
return ret_val;
}
/*****************************************************************************
NOTE: this function works only with local time, stored in the CMOS clock.
It knows nothing of GMT or timezones. This is a feature, not a bug :)
*****************************************************************************/
unsigned long sys_time(void)
{
static char init;
/**/
unsigned short date, month, hour, minute, second;
short year;
if(!init)
{
/* b2=0 BCD mode, vs. binary (binary mode seems to be buggy)
b1=1 24-hour mode, vs. 12-hour mode */
outportb(0x70, 11);
outportb(0x71, (inportb(0x71) & ~6) | 2);
init = 1;
}
/* wait for stored time value to stop changing */
outportb(0x70, 10);
while(inportb(0x71) & 128)
/* nothing */;
/* get year/month/date
year = read_cmos(9) + 1900; xxx - OH NO, Y2K!
year = read_cmos(9) + 2000;
use the Microsoft method -- this should be good from 1970-2069 */
year = read_cmos(9); /* 0-99 */
if(year < 70)
year += 2000;
else
year += 1900;
month = read_cmos(8); /* 1-12 */
date = read_cmos(7); /* 1-31 */
/* get time */
hour = read_cmos(4); /* 0-23 */
minute = read_cmos(2); /* 0-59 */
second = read_cmos(0); /* 0-59 */
return date_time_to_time_t(year, month, date, hour, minute, second);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -