初始化时间

master
阳光少年 1 year ago
parent 80320033a3
commit c7c6b4ff8b

@ -25,6 +25,7 @@
"stdlib.h": "c",
"cassert": "c",
"*.inc": "c",
"clock.h": "c"
"clock.h": "c",
"time.h": "c"
}
}

@ -53,6 +53,7 @@ $(BUILD_PATH)/kernel.bin:$(BUILD_PATH)/kernel/start.o \
$(BUILD_PATH)/kernel/interrupt.o\
$(BUILD_PATH)/lib/stdlib.o\
$(BUILD_PATH)/kernel/clock.o\
$(BUILD_PATH)/kernel/time.o\
$(shell mkdir -p $(dir $@))
ld -m elf_i386 -static $^ -o $@ -Ttext $(KERNEL_ENTRY_POINT)

@ -12,6 +12,7 @@
#include <onix/interrupt.h>>
#include <onix/stdlib.h>>
#include <onix/clock.h>>
#include <onix/time.h>>
void kernel_init();

@ -3,4 +3,7 @@
#include <onix/types.h>
void delay (u32 count);
void hang();
u8 bcd_to_bin(u8 value);
u8 bin_to_bcd(u8 value);
#endif

@ -0,0 +1,47 @@
#ifndef ONIX_TIME_H
#define ONIX_TIME_H
#include <onix/types.h>
#include <onix/debug.h>
#define CMOS_ADDR 0x70 // CMOS 地址寄存器
#define CMOS_DATA 0x71 // CMOS 数据寄存器
// 下面是 CMOS 信息的寄存器索引
#define CMOS_SECOND 0x00 // (0 ~ 59)
#define CMOS_MINUTE 0x02 // (0 ~ 59)
#define CMOS_HOUR 0x04 // (0 ~ 23)
#define CMOS_WEEKDAY 0x06 // (1 ~ 7) 星期天 = 1星期六 = 7
#define CMOS_DAY 0x07 // (1 ~ 31)
#define CMOS_MONTH 0x08 // (1 ~ 12)
#define CMOS_YEAR 0x09 // (0 ~ 99)
#define CMOS_CENTURY 0x32 // 可能不存在
#define CMOS_NMI 0x80
#define MINUTE 60 // 每分钟的秒数
#define HOUR (60 * MINUTE) // 每小时的秒数
#define DAY (24 * HOUR) // 每天的秒数
#define YEAR (365 * DAY) // 每年的秒数,以 365 天算
typedef struct tm{
u32 tm_sec; // 秒数 [059]
u32 tm_min; // 分钟数 [059]
u32 tm_hour; // 小时数 [059]
u32 tm_mday; // 1 个月的天数 [031]
u32 tm_mon; // 1 年中月份 [011]
u32 tm_year; // 从 1900 年开始的年数
u32 tm_wday; // 1 星期中的某天 [06] (星期天 =0)
u32 tm_yday; // 1 年中的某天 [0365]
u32 tm_isdst; // 夏令时标志
} tm;
void time_read_bcd(tm *time);
void time_read(tm *time);
// 将struct tm 结构体类型表示的时间转换为从 1970 年 1 月 1 日 00:00:00 +0000 (UTC) 到该时间点的秒数
usize mktime(tm *time);
void time_init();
#endif

@ -4,14 +4,10 @@
#include <onix/io.h>
usize counter = 0;
// 时钟中断的处理函数, 等下就替换掉默认的外中断处理函数
void clock_handler(u8 handle_num){
assert(handle_num == 0x20); // 只允许时钟中断调用这个
send_eoi(handle_num); // 通知中断控制器, cpu正在处理这个中断了
counter += 1;
DEBUGK("clock handler %d", counter);
}
void pit_init(){
@ -25,7 +21,7 @@ void clock_init(){
pit_init();
// 设置中断处理函数
set_interrupt_handler(IRQ_CLOCK, clock_handler);
// 打开中断
// 打开时钟中断
set_interrupt_mask(IRQ_CLOCK, true);
}

@ -16,8 +16,13 @@ void kernel_init(){
// 初始化任务
// task_init();
// 初始化时钟中断
clock_init();
// 初始化时间
time_init();
asm volatile("sti\n");
hang();

@ -9,10 +9,6 @@ global _start
_start:
call kernel_init
; int 0x08
; mov bx, 0
; div bx
jmp $

@ -0,0 +1,195 @@
#include <onix/time.h>
// 每个月开始时的已经过去天数
static u32 month[13] = {
0, // 这里占位,没有 0 月,从 1 月开始
0,
(31),
(31 + 29),
(31 + 29 + 31),
(31 + 29 + 31 + 30),
(31 + 29 + 31 + 30 + 31),
(31 + 29 + 31 + 30 + 31 + 30),
(31 + 29 + 31 + 30 + 31 + 30 + 31),
(31 + 29 + 31 + 30 + 31 + 30 + 31 + 31),
(31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30),
(31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31),
(31 + 29 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30)};
// 开机启动的时间点
u32 startup_time;
u32 century;
u32 elapsed_leap_years(u32 year)
{
u32 result = 0;
result += (year - 1) / 4;
result -= (year - 1) / 100;
result += (year + 299) / 400;
result -= (1970 - 1900) / 4;
return result;
}
bool is_leap_year(u32 year)
{
return ((year % 4 == 0) && (year % 100 != 0)) || ((year + 1900) % 400 == 0);
}
void localtime(u32 stamp, tm *time)
{
time->tm_sec = stamp % 60;
u32 remain = stamp / 60;
time->tm_min = remain % 60;
remain /= 60;
time->tm_hour = remain % 24;
u32 days = remain / 24;
time->tm_wday = (days + 4) % 7; // 1970-01-01 是星期四
// 这里产生误差显然需要 365 个闰年,不管了
u32 years = days / 365 + 70;
time->tm_year = years;
u32 offset = 1;
if (is_leap_year(years))
offset = 0;
days -= elapsed_leap_years(years);
time->tm_yday = days % (366 - offset);
u32 mon = 1;
for (; mon < 13; mon++)
{
if ((month[mon] - offset) > time->tm_yday)
break;
}
time->tm_mon = mon - 1;
time->tm_mday = time->tm_yday - month[time->tm_mon] + offset + 1;
}
// 这里生成的时间可能和 UTC 时间有出入
// 与系统具体时区相关,不过也不要紧,顶多差几个小时
// 这段代码的作用是将 struct tm 结构体类型表示的时间转换为从 1970 年 1 月 1 日 00:00:00 +0000 (UTC) 到该时间点的秒数
u32 mktime(tm *time)
{
u32 res;
u32 year; // 1970 年开始的年数
// 下面从 1900 年开始的年数计算
if (time->tm_year >= 70)
year = time->tm_year - 70;
else
year = time->tm_year - 70 + 100;
// 这些年经过的秒数时间
res = YEAR * year;
// 已经过去的闰年,每个加 1 天
res += DAY * ((year + 1) / 4);
// 已经过完的月份的时间
res += month[time->tm_mon] * DAY;
// 如果 2 月已经过了,并且当前不是闰年,那么减去一天
if (time->tm_mon > 2 && ((year + 2) % 4))
res -= DAY;
// 这个月已经过去的天
res += DAY * (time->tm_mday - 1);
// 今天过去的小时
res += HOUR * time->tm_hour;
// 这个小时过去的分钟
res += MINUTE * time->tm_min;
// 这个分钟过去的秒
res += time->tm_sec;
return res;
}
u32 get_yday(tm *time)
{
u32 res = month[time->tm_mon]; // 已经过去的月的天数
res += time->tm_mday; // 这个月过去的天数
u32 year;
if (time->tm_year >= 70)
year = time->tm_year - 70;
else
year = time->tm_year - 70 + 100;
// 如果不是闰年,并且 2 月已经过去了,则减去一天
// 注1972 年是闰年,这样算不太精确,忽略了 100 年的平年
if ((year + 2) % 4 && time->tm_mon > 2)
{
res -= 1;
}
return res;
}
// 从cmos芯片中读取到时间
u8 cmos_read(u8 addr)
{
out_8(CMOS_ADDR, CMOS_NMI | addr);
return in_8(CMOS_DATA);
};
void time_read_bcd(tm *time)
{
// CMOS 的访问速度很慢。为了减小时间误差,在读取了下面循环中所有数值后,
// 若此时 CMOS 中秒值发生了变化,那么就重新读取所有值。
// 这样内核就能把与 CMOS 的时间误差控制在 1 秒之内。
do
{
time->tm_sec = cmos_read(CMOS_SECOND);
time->tm_min = cmos_read(CMOS_MINUTE);
time->tm_hour = cmos_read(CMOS_HOUR);
time->tm_wday = cmos_read(CMOS_WEEKDAY);
time->tm_mday = cmos_read(CMOS_DAY);
time->tm_mon = cmos_read(CMOS_MONTH);
time->tm_year = cmos_read(CMOS_YEAR);
century = cmos_read(CMOS_CENTURY);
} while (time->tm_sec != cmos_read(CMOS_SECOND));
}
void time_read(tm *time)
{
time_read_bcd(time);
time->tm_sec = bcd_to_bin(time->tm_sec);
time->tm_min = bcd_to_bin(time->tm_min);
time->tm_hour = bcd_to_bin(time->tm_hour);
time->tm_wday = bcd_to_bin(time->tm_wday);
time->tm_mday = bcd_to_bin(time->tm_mday);
time->tm_mon = bcd_to_bin(time->tm_mon);
time->tm_year = bcd_to_bin(time->tm_year);
time->tm_yday = get_yday(time);
time->tm_isdst = -1;
century = bcd_to_bin(century);
}
void time_init()
{
tm time;
// 读取时间
time_read(&time);
DEBUGK("startup time: %d%d-%02d-%02d %02d:%02d:%02d\n",
century,
time.tm_year,
time.tm_mon,
time.tm_mday,
time.tm_hour,
time.tm_min,
time.tm_sec);
// 计算得到时间戳
startup_time = mktime(&time);
}

@ -8,4 +8,12 @@ void hang(){
while (true){
}
}
u8 bcd_to_bin(u8 value){
return (value & 0xf) + (value >> 4) * 10;
}
u8 bin_to_bcd(u8 value){
return (value / 10) * 0x10 + (value % 10);
}
Loading…
Cancel
Save