# HG changeset patch # User John Tsiombikas # Date 1308003547 -10800 # Node ID 928b0ebfff4ddb3222b0c73c279d93c93cb568e9 # Parent 2cb5ab18e76e52400908cb9d1c215c1dc0a8ffc6# Parent 710739e33da8cd8d2f829496679f46dca92c9764 merged the timer/rtc/etc from the misc branch diff -r 2cb5ab18e76e -r 928b0ebfff4d Makefile --- a/Makefile Sat May 28 08:06:47 2011 +0300 +++ b/Makefile Tue Jun 14 01:19:07 2011 +0300 @@ -23,6 +23,9 @@ $(bin): $(obj) ld -melf_i386 -o $@ -Ttext 0x100000 -e kentry $(obj) -Map link.map +%.s: %.c + $(CC) $(CFLAGS) -S -o $@ $< + -include $(dep) %.d: %.c diff -r 2cb5ab18e76e -r 928b0ebfff4d run --- a/run Sat May 28 08:06:47 2011 +0300 +++ b/run Tue Jun 14 01:19:07 2011 +0300 @@ -1,3 +1,3 @@ #!/bin/sh -qemu -kernel kernel.elf +qemu -kernel kernel.elf -soundhw pcspk diff -r 2cb5ab18e76e -r 928b0ebfff4d src/asmops.h --- a/src/asmops.h Sat May 28 08:06:47 2011 +0300 +++ b/src/asmops.h Tue Jun 14 01:19:07 2011 +0300 @@ -32,4 +32,6 @@ "outl %0, %1\n\t" \ :: "a" ((unsigned long)(src)), "dN" ((unsigned short)(port))) +#define iodelay() outb(0, 0x80) + #endif /* ASMOPS_H_ */ diff -r 2cb5ab18e76e -r 928b0ebfff4d src/config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/config.h Tue Jun 14 01:19:07 2011 +0300 @@ -0,0 +1,7 @@ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +/* frequency of generated timer ticks in hertz */ +#define TICK_FREQ_HZ 250 + +#endif /* _CONFIG_H_ */ diff -r 2cb5ab18e76e -r 928b0ebfff4d src/intr.c --- a/src/intr.c Sat May 28 08:06:47 2011 +0300 +++ b/src/intr.c Tue Jun 14 01:19:07 2011 +0300 @@ -12,14 +12,6 @@ #define GATE_DEFAULT (1 << 11) #define GATE_PRESENT (1 << 15) -/* offset used to remap IRQ numbers (+32) */ -#define IRQ_OFFSET 32 -/* conversion macros between IRQ and interrupt numbers */ -#define IRQ_TO_INTR(x) ((x) + IRQ_OFFSET) -#define INTR_TO_IRQ(x) ((x) - IRQ_OFFSET) -/* checks whether a particular interrupt is an remapped IRQ */ -#define IS_IRQ(n) ((n) >= IRQ_OFFSET && (n) < IRQ_OFFSET + 16) - /* PIC command and data ports */ #define PIC1_CMD 0x20 #define PIC1_DATA 0x21 diff -r 2cb5ab18e76e -r 928b0ebfff4d src/intr.h --- a/src/intr.h Sat May 28 08:06:47 2011 +0300 +++ b/src/intr.h Tue Jun 14 01:19:07 2011 +0300 @@ -4,6 +4,15 @@ #include #include "asmops.h" +/* offset used to remap IRQ numbers (+32) */ +#define IRQ_OFFSET 32 +/* conversion macros between IRQ and interrupt numbers */ +#define IRQ_TO_INTR(x) ((x) + IRQ_OFFSET) +#define INTR_TO_IRQ(x) ((x) - IRQ_OFFSET) +/* checks whether a particular interrupt is an remapped IRQ */ +#define IS_IRQ(n) ((n) >= IRQ_OFFSET && (n) < IRQ_OFFSET + 16) + + typedef void (*intr_func_t)(int, uint32_t); diff -r 2cb5ab18e76e -r 928b0ebfff4d src/klibc/time.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/klibc/time.c Tue Jun 14 01:19:07 2011 +0300 @@ -0,0 +1,136 @@ +#include +#include "time.h" +#include "rtc.h" +#include "timer.h" +#include "config.h" + +#define MINSEC 60 +#define HOURSEC (60 * MINSEC) +#define DAYSEC (24 * HOURSEC) +#define YEARDAYS(x) (is_leap_year(x) ? 366 : 365) + +/* 1-1-1970 was a thursday */ +#define EPOCH_WDAY 4 + +static int is_leap_year(int yr); + +static int mdays[2][12] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +}; + +static char *wday[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" +}; +static char *mon[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + + +time_t time(time_t *tp) +{ + time_t res = start_time + nticks / TICK_FREQ_HZ; + + if(tp) *tp = res; + return res; +} + +char *asctime(struct tm *tm) +{ + static char buf[64]; + return asctime_r(tm, buf); +} + +char *asctime_r(struct tm *tm, char *buf) +{ + sprintf(buf, "%s %s %d %02d:%02d:%02d %d\n", wday[tm->tm_wday], + mon[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min, + tm->tm_sec, tm->tm_year + 1900); + return buf; +} + +time_t mktime(struct tm *tm) +{ + int i, num_years = tm->tm_year - 70; + int year = 1970; + int days = day_of_year(tm->tm_year + 1900, tm->tm_mon, tm->tm_mday - 1); + + /* set correct yearday */ + tm->tm_yday = days; + + for(i=0; itm_wday = (days + EPOCH_WDAY) % 7; + + return (time_t)days * DAYSEC + tm->tm_hour * HOURSEC + + tm->tm_min * MINSEC + tm->tm_sec; +} + +struct tm *gmtime(time_t *tp) +{ + static struct tm tm; + return gmtime_r(tp, &tm); +} + +struct tm *gmtime_r(time_t *tp, struct tm *tm) +{ + int year, days, leap, yrdays; + time_t t; + + year = 1970; + days = *tp / DAYSEC; + t = *tp % DAYSEC; + + tm->tm_wday = (days + EPOCH_WDAY) % 7; + + while(days >= (yrdays = YEARDAYS(year))) { + days -= yrdays; + year++; + } + tm->tm_year = year - 1900; + tm->tm_yday = days; + + leap = is_leap_year(year); + tm->tm_mon = 0; + while(days >= mdays[leap][tm->tm_mon]) { + days -= mdays[leap][tm->tm_mon++]; + } + + tm->tm_mday = days + 1; + + tm->tm_hour = t / HOURSEC; + t %= HOURSEC; + tm->tm_min = t / MINSEC; + tm->tm_sec = t % MINSEC; + return tm; +} + +int day_of_year(int year, int mon, int day) +{ + int i, yday, leap; + + leap = is_leap_year(year) ? 1 : 0; + yday = day; + + for(i=0; i +#include +#include +#include "rtc.h" + +/* CMOS I/O ports */ +#define PORT_CTL 0x70 +#define PORT_DATA 0x71 + +/* CMOS RTC registers */ +#define REG_SEC 0 +#define REG_ALARM_SEC 1 +#define REG_MIN 2 +#define REG_ALARM_MIN 3 +#define REG_HOUR 4 +#define REG_ALARM_HOUR 5 +#define REG_WEEKDAY 6 +#define REG_DAY 7 +#define REG_MONTH 8 +#define REG_YEAR 9 +#define REG_STATA 10 +#define REG_STATB 11 +#define REG_STATC 12 +#define REG_STATD 13 + +#define STATA_BUSY (1 << 7) +#define STATB_24HR (1 << 1) +#define STATB_BIN (1 << 2) + +#define HOUR_PM_BIT (1 << 7) + +#define BCD_TO_BIN(x) ((((x) >> 4) & 0xf) * 10 + ((x) & 0xf)) + +static void read_rtc(struct tm *tm); +static int read_reg(int reg); + + +void init_rtc(void) +{ + struct tm tm; + + read_rtc(&tm); + start_time = mktime(&tm); + + printf("System real-time clock: %s", asctime(&tm)); +} + + +static void read_rtc(struct tm *tm) +{ + int statb, pm; + + /* wait for any clock updates to finish */ + while(read_reg(REG_STATA) & STATA_BUSY); + + tm->tm_sec = read_reg(REG_SEC); + tm->tm_min = read_reg(REG_MIN); + tm->tm_hour = read_reg(REG_HOUR); + tm->tm_mday = read_reg(REG_DAY); + tm->tm_mon = read_reg(REG_MONTH); + tm->tm_year = read_reg(REG_YEAR); + + /* in 12hour mode, bit 7 means post-meridiem */ + pm = tm->tm_hour & HOUR_PM_BIT; + tm->tm_hour &= ~HOUR_PM_BIT; + + /* convert to binary if needed */ + statb = read_reg(REG_STATB); + if(!(statb & STATB_BIN)) { + tm->tm_sec = BCD_TO_BIN(tm->tm_sec); + tm->tm_min = BCD_TO_BIN(tm->tm_min); + tm->tm_hour = BCD_TO_BIN(tm->tm_hour); + tm->tm_mday = BCD_TO_BIN(tm->tm_mday); + tm->tm_mon = BCD_TO_BIN(tm->tm_mon); + tm->tm_year = BCD_TO_BIN(tm->tm_year); + } + + /* make the year an offset from 1900 */ + if(tm->tm_year < 100) { + tm->tm_year += 100; + } else { + tm->tm_year -= 1900; + } + + /* if tm_hour is in 12h mode, convert to 24h */ + if(!(statb & STATB_24HR)) { + if(tm->tm_hour == 12) { + tm->tm_hour = 0; + } + if(pm) { + tm->tm_hour += 12; + } + } + + tm->tm_mon -= 1; /* we want months to start from 0 */ +} + +static int read_reg(int reg) +{ + unsigned char val; + outb(reg, PORT_CTL); + iodelay(); + inb(val, PORT_DATA); + iodelay(); + return val; +} diff -r 2cb5ab18e76e -r 928b0ebfff4d src/rtc.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rtc.h Tue Jun 14 01:19:07 2011 +0300 @@ -0,0 +1,11 @@ +#ifndef _RTC_H_ +#define _RTC_H_ + +#include + +/* the time read from rtc during init */ +time_t start_time; + +void init_rtc(void); + +#endif /* _RTC_H_ */ diff -r 2cb5ab18e76e -r 928b0ebfff4d src/term.c --- a/src/term.c Sat May 28 08:06:47 2011 +0300 +++ b/src/term.c Tue Jun 14 01:19:07 2011 +0300 @@ -29,7 +29,6 @@ /* output a single character, handles formatting, cursor advancement * and scrolling the screen when we reach the bottom. - * TODO make visible cursor actually move. */ int putchar(int c) { diff -r 2cb5ab18e76e -r 928b0ebfff4d src/timer.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/timer.c Tue Jun 14 01:19:07 2011 +0300 @@ -0,0 +1,77 @@ +#include +#include +#include "intr.h" +#include "asmops.h" +#include "timer.h" +#include "config.h" + +/* frequency of the oscillator driving the 8254 timer */ +#define OSC_FREQ_HZ 1193182 + +/* macro to divide and round to the nearest integer */ +#define DIV_ROUND(a, b) ((a) / (b) + ((a) % (b)) / ((b) / 2)) + +/* I/O ports connected to the 8254 */ +#define PORT_DATA0 0x40 +#define PORT_DATA1 0x41 +#define PORT_DATA2 0x42 +#define PORT_CMD 0x43 + +/* command bits */ +#define CMD_CHAN0 0 +#define CMD_CHAN1 (1 << 6) +#define CMD_CHAN2 (2 << 6) +#define CMD_RDBACK (3 << 6) + +#define CMD_LATCH 0 +#define CMD_ACCESS_LOW (1 << 4) +#define CMD_ACCESS_HIGH (2 << 4) +#define CMD_ACCESS_BOTH (3 << 4) + +#define CMD_OP_INT_TERM 0 +#define CMD_OP_ONESHOT (1 << 1) +#define CMD_OP_RATE (2 << 1) +#define CMD_OP_SQWAVE (3 << 1) +#define CMD_OP_SOFT_STROBE (4 << 1) +#define CMD_OP_HW_STROBE (5 << 1) + +#define CMD_MODE_BIN 0 +#define CMD_MODE_BCD 1 + + +static void intr_handler(); + + +void init_timer(void) +{ + /* calculate the reload count: round(osc / freq) */ + int reload_count = DIV_ROUND(OSC_FREQ_HZ, TICK_FREQ_HZ); + + /* set the mode to square wave for channel 0, both low + * and high reload count bytes will follow... + */ + outb(CMD_CHAN0 | CMD_ACCESS_BOTH | CMD_OP_SQWAVE, PORT_CMD); + + /* write the low and high bytes of the reload count to the + * port for channel 0 + */ + outb(reload_count & 0xff, PORT_DATA0); + outb((reload_count >> 8) & 0xff, PORT_DATA0); + + /* set the timer interrupt handler */ + interrupt(IRQ_TO_INTR(0), intr_handler); +} + +/* This will be called by the interrupt dispatcher approximately + * every 1/250th of a second, so it must be extremely fast. + * For now, just increasing a tick counter will suffice. + */ +static void intr_handler() +{ + nticks++; + + if(nticks % TICK_FREQ_HZ == 0) { + time_t t = time(0); + printf("%s", asctime(gmtime(&t))); + } +} diff -r 2cb5ab18e76e -r 928b0ebfff4d src/timer.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/timer.h Tue Jun 14 01:19:07 2011 +0300 @@ -0,0 +1,8 @@ +#ifndef _TIMER_H_ +#define _TIMER_H_ + +unsigned long nticks; + +void init_timer(void); + +#endif /* _TIMER_H_ */ diff -r 2cb5ab18e76e -r 928b0ebfff4d src/vid.c --- a/src/vid.c Sat May 28 08:06:47 2011 +0300 +++ b/src/vid.c Tue Jun 14 01:19:07 2011 +0300 @@ -1,5 +1,6 @@ #include #include "vid.h" +#include "intr.h" #include "asmops.h" /* height of our virtual console text buffer */ @@ -27,10 +28,15 @@ void clear_scr(void) { + int istate = get_intr_state(); + disable_intr(); + memset16(vmem, VMEM_CHAR(' ', LTGRAY, BLACK), WIDTH * HEIGHT); start_line = 0; set_start_line(0); set_cursor(0, 0); + + set_intr_state(istate); } void set_char(char c, int x, int y, int fg, int bg) @@ -41,6 +47,8 @@ void set_cursor(int x, int y) { int loc; + int istate = get_intr_state(); + disable_intr(); if(x < 0 || x >= WIDTH || y < 0 || y >= HEIGHT) { loc = 0xffff; @@ -52,10 +60,15 @@ outb(loc, CRTC_DATA); outb(CRTC_CURSOR_HIGH, CRTC_ADDR); outb(loc >> 8, CRTC_DATA); + + set_intr_state(istate); } void scroll_scr(void) { + int new_line, istate = get_intr_state(); + disable_intr(); + if(++start_line > VIRT_HEIGHT - HEIGHT) { /* The bottom of the visible range reached the end of our text buffer. * Copy the rest of the lines to the top and reset start_line. @@ -65,10 +78,11 @@ } /* clear the next line that will be revealed by scrolling */ - int new_line = start_line + HEIGHT - 1; + new_line = start_line + HEIGHT - 1; memset16(vmem + new_line * WIDTH, VMEM_CHAR(' ', LTGRAY, BLACK), WIDTH); + set_start_line(start_line); - set_start_line(start_line); + set_intr_state(istate); } static void set_start_line(int line) diff -r 2cb5ab18e76e -r 928b0ebfff4d src/vm.c --- a/src/vm.c Sat May 28 08:06:47 2011 +0300 +++ b/src/vm.c Tue Jun 14 01:19:07 2011 +0300 @@ -259,7 +259,7 @@ void pgfree(int start, int num) { - int i, area, end, intr_state; + int i, area, intr_state; struct page_range *node, *new, *prev, *next; intr_state = get_intr_state(); @@ -276,7 +276,7 @@ panic("pgfree: can't allocate new page_range node to add the freed pages\n"); } new->start = start; - end = new->end = start + num; + new->end = start + num; area = PAGE_TO_ADDR(start) >= KMEM_START ? MEM_KERNEL : MEM_USER;