Cleanup in elf.c with .bss section clean; adm command mounts cdrom instead of floppy...
[ZeXOS.git] / kernel / arch / i386 / rtc.c
blob7c83b283f5e0c68d19bf45d3db8f561436e01e4e
1 /*
2 * ZeX/OS
3 * Copyright (C) 2007 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
4 * Copyright (C) 2008 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
5 * Copyright (C) 2009 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include <system.h>
23 #include <arch/io.h>
24 #include <time.h>
26 static tm rtime;
28 /* use BCD mode, since binary mode seems to be buggy */
29 static unsigned read_register (unsigned reg)
31 unsigned high_digit, low_digit;
33 outb (0x70, reg);
35 high_digit = low_digit = inb (0x71);
37 /* convert from BCD to binary */
38 high_digit >>= 4;
39 high_digit &= 0x0F;
40 low_digit &= 0x0F;
42 return 10 * high_digit + low_digit;
45 /*****************************************************************************
46 Finds the number of days between two dates in the Gregorian calendar.
47 - it's a leap year if the year is divisible by 4,
48 - UNLESS the year is also divisible by 100,
49 - UNLESS the year is also divisible by 400
51 To compute Julian Day Number (JDN;
52 days since Nov 24, 4714 BC/BCE in Gregorian calendar):
53 days_between_dates(-4713, 327, curr_day_in_year, curr_year);
55 To compute days since Jan 1, 1970 (UNIX epoch):
56 days_between_dates(1970, 0, curr_day_in_year, curr_year);
58 days_between_dates(-4713, 327, curr_day_in_year, curr_year) + 2440588L;
60 This code divides the time between start_day/start_year and end_day/end_year
61 into "slices": fourcent (400-year) slices in the middle, bracketed on
62 either end by century slices, fouryear (4-year) slices, and year slices.
64 When used to compute JDN, this code produces the same results as the
65 code shown here:
66 http://serendipity.magnet.ch/hermetic/cal_stud/jdn.htm
68 IMHO, it's easier to see how the algorithm for this code works,
69 versus the code at the URL above.
70 *****************************************************************************/
71 static long days_between_dates (unsigned start_year, unsigned start_day,
72 int end_year, unsigned end_day)
74 int fourcents, centuries, fouryears, years;
75 long days;
77 fourcents = end_year / 400 - start_year / 400;
78 centuries = end_year / 100 - start_year / 100 -
79 /* subtract from 'centuries' the centuries already accounted for by
80 'fourcents' */
81 fourcents * 4;
82 fouryears = end_year / 4 - start_year / 4 -
83 /* subtract from 'fouryears' the fouryears already accounted for by
84 'fourcents' and 'centuries' */
85 fourcents * 100 - centuries * 25;
86 years = end_year - start_year -
87 /* subtract from 'years' the years already accounted for by
88 'fourcents', 'centuries', and 'fouryears' */
89 400 * fourcents - 100 * centuries - 4 * fouryears;
90 /* add it up: 97 leap days every fourcent */
91 days = (365L * 400 + 97) * fourcents;
92 /* 24 leap days every residual century */
93 days += (365L * 100 + 24) * centuries;
94 /* 1 leap day every residual fouryear */
95 days += (365L * 4 + 1) * fouryears;
96 /* 0 leap days for residual years */
97 days += (365L * 1) * years;
98 /* residual days (need the cast!) */
99 days += ((long) end_day - start_day);
101 /* account for terminal leap year */
102 if(end_year % 4 == 0 && end_day >= 60) {
103 days ++;
105 if (end_year % 100 == 0)
106 days --;
107 if (end_year % 400 == 0)
108 days ++;
111 /* xxx - what have I wrought? I don't know what's going on here,
112 but the code won't work properly without it */
113 if (end_year >= 0) {
114 days ++;
116 if (end_year % 4 == 0)
117 days --;
118 if (end_year % 100 == 0)
119 days ++;
120 if (end_year % 400 == 0)
121 days --;
124 if (start_year > 0)
125 days --;
127 return days;
130 /*****************************************************************************
131 month and date start with 1, not with 0
132 *****************************************************************************/
133 #define EPOCH_YEAR 1970
134 #define EPOCH_DAY 0 /* Jan 1 */
136 unsigned long date_time_to_time_t(unsigned year, unsigned month,
137 unsigned date, unsigned hour, unsigned min,
138 unsigned sec)
140 static const unsigned days_to_date[12] =
142 /* jan feb mar apr may jun jul aug sep oct nov dec */
145 31 + 28,
146 31 + 28 + 31,
147 31 + 28 + 31 + 30,
148 31 + 28 + 31 + 30 + 31,
149 31 + 28 + 31 + 30 + 31 + 30,
150 31 + 28 + 31 + 30 + 31 + 30 + 31,
151 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
152 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
153 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
154 31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30
157 unsigned long ret_val;
158 unsigned day;
160 /* convert month and year to day-in-year */
161 if (month < 1 || month > 12 || date < 1 || date > 31)
162 return 0;
164 month --;
165 date --;
167 day = date + days_to_date[month];
169 /* convert to Unix JDN (UJDN) */
170 ret_val = days_between_dates (EPOCH_YEAR, EPOCH_DAY, year, day);
172 /* convert from days to seconds, adding time as you go */
173 ret_val *= 24;
174 ret_val += hour;
175 ret_val *= 60;
176 ret_val += min;
177 ret_val *= 60;
178 ret_val += sec;
180 return ret_val;
183 /*****************************************************************************
184 NOTE: this function works only with local time, stored in the CMOS clock.
185 It knows nothing of GMT or timezones. This is a feature, not a bug :)
186 *****************************************************************************/
187 tm *rtc_getcurrtime ()
189 static char init;
190 unsigned date, month, hour, minute, second;
191 int year;
193 if (!init) {
194 /* b2=0 BCD mode, vs. binary (binary mode seems to be buggy)
195 b1=1 24-hour mode, vs. 12-hour mode */
196 outb (0x70, 11);
197 outb (0x71, (inb (0x71) & ~6) | 2);
198 init = 1;
201 /* wait for stored time value to stop changing */
202 outb (0x70, 10);
204 while (inb (0x71) & 128);
206 /* get year/month/date
207 year = read_register (9) + 1900; xxx - OH NO, Y2K!
208 year = read_register (9) + 2000;
209 use the Microsoft method -- this should be good from 1970-2069
210 signed 32-bit time_t will overflow before then, in 2038 */
212 year = read_register (9); /* 0-99 */
214 if(year < 70)
215 year += 2000;
216 else
217 year += 1900;
219 month = read_register (8); /* 1-12 */
220 date = read_register (7); /* 1-31 */
221 hour = read_register (4); /* 0-23 */
222 minute = read_register (2); /* 0-59 */
223 second = read_register (0); /* 0-59 */
225 rtime.tm_sec = second;
226 rtime.tm_min = minute;
227 rtime.tm_hour = hour;
228 rtime.tm_mday = date;
229 rtime.tm_mon = month;
230 rtime.tm_year = year;
231 rtime.tm_isdst = 0;
232 rtime.__tm_gmtoff = date_time_to_time_t (year, month, date, hour, minute, second);
234 return &rtime;