diff --git a/.gitignore b/.gitignore index 6caafaa..266d49b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,8 @@ build/** # Some notes. LIBC + +# Output files +libc.a +libgint.a +gintdemo.g1a diff --git a/Makefile b/Makefile index d6d809c..6081443 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ # Modules modules-gint = core keyboard mmu mpu rtc screen timer \ bopti display gray tales -modules-libc = setjmp string stdio stdlib +modules-libc = setjmp string stdio stdlib time # Targets target-g1a = gintdemo.g1a diff --git a/README.md b/README.md index 5892e31..f3b5fad 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ register access and implements a few standard functions. Building and installing ----------------------- -There a some dependencies: +There are some dependencies: * The `sh3eb-elf` toolchain somewhere in the PATH * The fxSDK installed and available in the PATH diff --git a/TODO b/TODO index 56f0be3..9abcd57 100644 --- a/TODO +++ b/TODO @@ -18,11 +18,10 @@ + partial transparency + gint vs. ML with 248x124 at (-60, -28) + call exit handlers -+ compute frequencies ++ compute frequencies and CLOCKS_PER_SEC + test all font encodings - improve exception handler debugging information (if possible) -- full rtc driver (time) - callbacks and complete user API ~ packed bit fields diff --git a/gintdemo.g1a b/gintdemo.g1a index ead9e38..3a5351f 100644 Binary files a/gintdemo.g1a and b/gintdemo.g1a differ diff --git a/include/internals/time.h b/include/internals/time.h new file mode 100644 index 0000000..236dcf6 --- /dev/null +++ b/include/internals/time.h @@ -0,0 +1,16 @@ +#ifndef _INTERNALS_TIME_H +#define _INTERNALS_TIME_H 1 + +/* + isLeap() + Determines whether the given year is a leap year. +*/ +int isLeap(int year); + +/* + daysInMonth() + Returns number of days for the given month (between 0 and 11) and year. +*/ +int daysInMonth(int month, int year); + +#endif // _INTERNALS_TIME_H diff --git a/include/rtc.h b/include/rtc.h index 2266713..08d9880 100644 --- a/include/rtc.h +++ b/include/rtc.h @@ -15,9 +15,7 @@ /* struct RTCTime - Defines a point in time. This structure *is* the standard struct tm to - avoid useless data copy in the interface between this module and the - standard time module. + Defines a point in time. */ struct RTCTime { @@ -26,21 +24,21 @@ struct RTCTime int hours; // Hours in range 0-23 int month_day; // Day of month in range 1-31 int month; // Month in range 0-11 - int year; // Number of years since 1900 + int year; // Years (full value) int week_day; // Day of week in range 0(Sunday)-6(Saturday). - int year_day; // Day of the year in range 0-365. - int daylight_saving; // As far as I known the RTC does not use DST. }; /* rtc_getTime() - Reads the current time from the RTC. + Reads the current time from the RTC. There is no guarantee that the + week day is correct (use the time API for that). */ struct RTCTime rtc_getTime(void); /* rtc_setTime() - Sets the time in the RTC registers. + Sets the time in the RTC registers. The week day is set to 0 if greater + than 6. Other fields are not checked. */ void rtc_setTime(struct RTCTime time); diff --git a/include/time.h b/include/time.h index 2fd712a..107e4fc 100644 --- a/include/time.h +++ b/include/time.h @@ -29,7 +29,7 @@ struct tm int tm_year; // Number of years since 1900 int tm_wday; // Day of week in range 0(Sunday)-6(Saturday). int tm_yday; // Day of the year in range 0-365. - int tm_isdst; // Always -1 (not available). + int tm_isdst; // This will always be 0. }; /* @@ -57,6 +57,17 @@ typedef signed int time_t; */ clock_t clock(void); +/* + time() + Returns the current time as calendar time. If you need a broken-down + time, either use the RTC API or gmtime(). However, this function is + already based on mktime() (for hardware reasons) so it would be much + faster to use the RTC API if possible. + If timeptr is not NULL, it is set to the current time, that is, the + value that is returned. +*/ +time_t time(time_t *timeptr); + /* difftime() Returns the number of seconds between the given points. @@ -71,15 +82,45 @@ double difftime(time_t end, time_t beginning); // Time representation. //--- +/* + asctime() + Converts broken-down time to string representation on the form + "Wed Jun 30 21:49:08 1993\n". The returned string is statically + allocated and may be overwritten by any subsequent call to a time + function. +*/ +char *asctime(const struct tm *time); + +/* + ctime() + Converts calendar time to string representation on the form + "Wed Jun 30 21:49:08 1993\n". The returned string is statically + allocated and may be overwritten by any subsequent call to a time + function. +*/ +char *ctime(const time_t *timer); + + + //--- -// +// Time conversion. //--- /* mktime() - Computes fields tm_wday and tm_yday using the other fields. Member - structures outside their range are normalized and tm_isdst is set. + Converts broken-down time to calendar time. Computes structure fields + tm_wday and tm_yday using the other fields. Member structures outside + their range are normalized (e.g. 40 October becomes 9 November) and + tm_isdst is set. */ time_t mktime(struct tm *time); +/* + gmtime() + Converts calendar time to broken-down time. The returned pointer is + statically allocated and may be overwritten by any subsequent call to + a time function. +*/ +struct tm *gmtime(const time_t *t); + #endif // _TIME_H diff --git a/libc.a b/libc.a index b460e14..87fbcfa 100644 Binary files a/libc.a and b/libc.a differ diff --git a/libgint.a b/libgint.a index ce45882..422f0d1 100644 Binary files a/libgint.a and b/libgint.a differ diff --git a/src/rtc/rtc_getTime.c b/src/rtc/rtc_getTime.c index 2874d70..5e9f653 100644 --- a/src/rtc/rtc_getTime.c +++ b/src/rtc/rtc_getTime.c @@ -16,23 +16,11 @@ static int integer16(int bcd) + 1000 * (bcd >> 12); } -static int year_day(int year, int year_bcd, int month, int month_day) -{ - int days_in_month[12] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - int leap; - int hundreds = integer8(year_bcd >> 8); - int day, i; - - leap = !(year & 3); // Take multiples of 4 - if(!((year_bcd >> 8) & 0xf)) leap = 0; // Remove multiples of 100 - if(!(hundreds & 3)) leap = 1; // Take multiples of 400 - - day = leap && (month > 2); - for(i = 0; i < month - 1; i++) day += days_in_month[i]; - return day + month_day - 1; -} - +/* + rtc_getTime() + Reads the current time from the RTC. There is no guarantee that the + week day is correct (use the time API for that). +*/ struct RTCTime rtc_getTime(void) { volatile struct mod_rtc *rtc = isSH3() ? RTC_SH7705 : RTC_SH7305; @@ -43,11 +31,8 @@ struct RTCTime rtc_getTime(void) time.hours = integer8(rtc->RHRCNT.BYTE); time.month_day = integer8(rtc->RDAYCNT.BYTE); time.month = integer8(rtc->RMONCNT.BYTE); - time.year = integer16(rtc->RYRCNT.WORD) - 1900; + time.year = integer16(rtc->RYRCNT.WORD); time.week_day = rtc->RWKCNT; - time.year_day = year_day(time.year + 1900, rtc->RYRCNT.WORD, - time.month, time.month_day); - time.daylight_saving = 0; return time; } diff --git a/src/rtc/rtc_setTime.c b/src/rtc/rtc_setTime.c index b00bcf5..d343c51 100644 --- a/src/rtc/rtc_setTime.c +++ b/src/rtc/rtc_setTime.c @@ -17,6 +17,11 @@ static int bcd16(int integer) return (bcd8(integer / 100) << 8) | bcd8(integer % 100); } +/* + rtc_setTime() + Sets the time in the RTC registers. The week day is set to 0 if greater + than 6. Other fields are not checked. +*/ void rtc_setTime(struct RTCTime time) { volatile struct mod_rtc *rtc = isSH3() ? RTC_SH7705 : RTC_SH7305; @@ -26,6 +31,6 @@ void rtc_setTime(struct RTCTime time) rtc->RHRCNT.BYTE = bcd8(time.hours); rtc->RDAYCNT.BYTE = bcd8(time.month_day); rtc->RMONCNT.BYTE = bcd8(time.month); - rtc->RYRCNT.WORD = bcd16(time.year + 1900); + rtc->RYRCNT.WORD = bcd16(time.year); rtc->RWKCNT = time.week_day < 7 ? time.week_day : 0; } diff --git a/src/time/asctime.c b/src/time/asctime.c new file mode 100644 index 0000000..ed3d0a3 --- /dev/null +++ b/src/time/asctime.c @@ -0,0 +1,57 @@ +#include + +static char str[26]; + +/* + asctime() + Converts broken-down time to string representation on the form + "Wed Jun 30 21:49:08 1993\n". The returned string is statically + allocated and may be overwritten by any subsequent call to a time + function. +*/ +char *asctime(const struct tm *time) +{ + const char *days[] = { + "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" + }, *months[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", + "Sep", "Oct", "Nov", "Dec" + }; + int year = time->tm_year + 1900; + + str[0] = days[time->tm_wday][0]; + str[1] = days[time->tm_wday][1]; + str[2] = days[time->tm_wday][2]; + str[3] = ' '; + + str[4] = months[time->tm_mon][0]; + str[5] = months[time->tm_mon][1]; + str[6] = months[time->tm_mon][2]; + str[7] = ' '; + + str[8] = '0' + (time->tm_mday / 10); + str[9] = '0' + (time->tm_mday % 10); + str[10] = ' '; + + str[11] = '0' + (time->tm_hour / 10); + str[12] = '0' + (time->tm_hour % 10); + str[13] = ':'; + str[14] = '0' + (time->tm_min / 10); + str[15] = '0' + (time->tm_min % 10); + str[16] = ':'; + str[17] = '0' + (time->tm_sec / 10); + str[18] = '0' + (time->tm_sec % 10); + str[19] = ' '; + + str[20] = '0' + (year / 1000); + year %= 1000; + str[21] = '0' + (year / 100); + year %= 100; + str[22] = '0' + (year / 10); + str[23] = '0' + (year % 10); + + str[24] = '\n'; + str[25] = 0; + + return str; +} diff --git a/src/time/ctime.c b/src/time/ctime.c new file mode 100644 index 0000000..f3768e0 --- /dev/null +++ b/src/time/ctime.c @@ -0,0 +1,13 @@ +#include + +/* + ctime() + Converts calendar time to string representation on the form + "Wed Jun 30 21:49:08 1993\n". The returned string is statically + allocated and may be overwritten by any subsequent call to a time + function. +*/ +char *ctime(const time_t *timer) +{ + return asctime(gmtime(timer)); +} diff --git a/src/time/gmtime.c b/src/time/gmtime.c new file mode 100644 index 0000000..4d84587 --- /dev/null +++ b/src/time/gmtime.c @@ -0,0 +1,45 @@ +#include +#include +#include + +static struct tm tm; + +/* + gmtime() + Converts calendar time to broken-down time. The returned pointer is + statically allocated and may be overwritten by any subsequent call to + a time function. +*/ +struct tm *gmtime(const time_t *timeptr) +{ + time_t t = *timeptr; + div_t d; + int sec; + + tm.tm_year = 1970; + tm.tm_mon = 0; + + sec = daysInMonth(tm.tm_mon, tm.tm_year) * 24 * 3600; + while(t >= sec) + { + t -= sec; + if(++tm.tm_mon == 12) + { + tm.tm_year++; + tm.tm_mon = 0; + } + sec = daysInMonth(tm.tm_mon, tm.tm_year) * 24 * 3600; + } + tm.tm_year -= 1900; + + d = div(sec, 24 * 3600); + tm.tm_mday = d.quot; + d = div(d.rem, 3600); + tm.tm_hour = d.quot; + d = div(d.rem, 60); + tm.tm_min = d.quot; + tm.tm_sec = d.rem; + + mktime(&tm); + return &tm; +} diff --git a/src/time/mktime.c b/src/time/mktime.c index e29b212..928cf6f 100644 --- a/src/time/mktime.c +++ b/src/time/mktime.c @@ -1,42 +1,17 @@ #include - -/* - isLeap() - Determines whether the given year is a leap year. -*/ -int isLeap(int year) -{ - int leap = !(year & 3); // Take multiples of 4 - if(!(year % 100)) leap = 0; // Remove multiples of 100 - if(!(year % 400)) leap = 1; // Take multiples of 400 - - return leap; -} - -/* - daysInMonth() - Returns number of days for the given month (between 0 and 11) and year. -*/ -int daysInMonth(int month, int year) -{ - int days[12] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 - }; - - if(month != 1) return days[month]; - return days[month] + isLeap(year); -} +#include /* mktime() - Computes fields tm_wday and tm_yday using the other fields. Member - structures outside their range are normalized and tm_isdst is set. + Converts broken-down time to calendar time. Computes structure fields + tm_wday and tm_yday using the other fields. Member structures outside + their range are normalized (e.g. 40 October becomes 9 November) and + tm_isdst is set. */ time_t mktime(struct tm *time) { - int first, leaps, yr; + int leaps, yr; int days, i; - time_t elapsed; // Normalizing time. time->tm_min += (time->tm_sec / 60); @@ -88,6 +63,6 @@ time_t mktime(struct tm *time) // days may become negative, so add the calendar period. if(days < 0) days += 146097; - return (24 * 3600) * days + 3600 * time->tm_hour + 60 * time->tm_min + - time->tm_sec; + return (time_t)((24 * 3600) * days + 3600 * time->tm_hour + + 60 * time->tm_min + time->tm_sec); } diff --git a/src/time/time.c b/src/time/time.c new file mode 100644 index 0000000..16189d4 --- /dev/null +++ b/src/time/time.c @@ -0,0 +1,29 @@ +#include +#include + +/* + time() + Returns the current time as calendar time. If you need a broken-down + time, either use the RTC API or gmtime(). However, this function is + already based on mktime() (for hardware reasons) so it would be much + faster to use the RTC API if possible. + If timeptr is not NULL, it is set to the current time, that is, the + value that is returned. +*/ +time_t time(time_t *timeptr) +{ + struct RTCTime rtc = rtc_getTime(); + struct tm tm; + time_t calendar; + + tm.tm_sec = rtc.seconds; + tm.tm_min = rtc.minutes; + tm.tm_hour = rtc.hours; + tm.tm_mday = rtc.month_day; + tm.tm_mon = rtc.month; + tm.tm_year = rtc.year - 1900; + + calendar = mktime(&tm); + if(timeptr) *timeptr = calendar; + return calendar; +} diff --git a/src/time/time_misc.c b/src/time/time_misc.c index c3d21d6..88e01fc 100644 --- a/src/time/time_misc.c +++ b/src/time/time_misc.c @@ -1,4 +1,5 @@ #include +#undef difftime /* clock() diff --git a/src/time/time_util.c b/src/time/time_util.c new file mode 100644 index 0000000..11c7048 --- /dev/null +++ b/src/time/time_util.c @@ -0,0 +1,28 @@ +#include + +/* + isLeap() + Determines whether the given year is a leap year. +*/ +int isLeap(int year) +{ + int leap = !(year & 3); // Take multiples of 4 + if(!(year % 100)) leap = 0; // Remove multiples of 100 + if(!(year % 400)) leap = 1; // Take multiples of 400 + + return leap; +} + +/* + daysInMonth() + Returns number of days for the given month (between 0 and 11) and year. +*/ +int daysInMonth(int month, int year) +{ + int days[12] = { + 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + + if(month != 1) return days[month]; + return days[month] + isLeap(year); +}