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