From 348a9048b7bed27335301f5b8d3398baceb5f84e Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Tue, 9 Jan 2024 21:28:37 +0100 Subject: [PATCH 1/4] Convert _time_t_to_tm to asm 46 bytes size gain, -8% cycles on the unit tests --- libsrc/common/_time_t_to_tm.c | 64 ----------------- libsrc/common/_time_t_to_tm.s | 129 ++++++++++++++++++++++++++++++++++ libsrc/common/mktime.c | 9 ++- 3 files changed, 135 insertions(+), 67 deletions(-) delete mode 100644 libsrc/common/_time_t_to_tm.c create mode 100644 libsrc/common/_time_t_to_tm.s diff --git a/libsrc/common/_time_t_to_tm.c b/libsrc/common/_time_t_to_tm.c deleted file mode 100644 index 684cff752..000000000 --- a/libsrc/common/_time_t_to_tm.c +++ /dev/null @@ -1,64 +0,0 @@ -/*****************************************************************************/ -/* */ -/* gmtime.c */ -/* */ -/* Convert calendar time into broken down time in UTC */ -/* */ -/* */ -/* */ -/* (C) 2002 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ -/* */ -/* */ -/* This software is provided 'as-is', without any expressed or implied */ -/* warranty. In no event will the authors be held liable for any damages */ -/* arising from the use of this software. */ -/* */ -/* Permission is granted to anyone to use this software for any purpose, */ -/* including commercial applications, and to alter it and redistribute it */ -/* freely, subject to the following restrictions: */ -/* */ -/* 1. The origin of this software must not be misrepresented; you must not */ -/* claim that you wrote the original software. If you use this software */ -/* in a product, an acknowledgment in the product documentation would be */ -/* appreciated but is not required. */ -/* 2. Altered source versions must be plainly marked as such, and must not */ -/* be misrepresented as being the original software. */ -/* 3. This notice may not be removed or altered from any source */ -/* distribution. */ -/* */ -/*****************************************************************************/ - - - -#include - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - - -struct tm* __fastcall__ _time_t_to_tm (const time_t t) -{ - static struct tm timebuf; - /* Since our ints are just 16 bits, split the given time into seconds, - ** hours and days. Each of the values will fit in a 16 bit variable. - ** The mktime routine will then do the rest. - */ - timebuf.tm_sec = t % 3600; - timebuf.tm_min = 0; - timebuf.tm_hour = (t / 3600) % 24; - timebuf.tm_mday = (t / (3600UL * 24UL)) + 1; - timebuf.tm_mon = 0; - timebuf.tm_year = 70; /* Base value is 1/1/1970 */ - - /* Call mktime to do the final conversion */ - mktime (&timebuf); - - /* Return the result */ - return &timebuf; -} diff --git a/libsrc/common/_time_t_to_tm.s b/libsrc/common/_time_t_to_tm.s new file mode 100644 index 000000000..ffabf15fc --- /dev/null +++ b/libsrc/common/_time_t_to_tm.s @@ -0,0 +1,129 @@ +; +; Colin Leroy-Mira, 2024 +; +; struct tm* __fastcall__ _time_t_to_tm (const time_t t) +; +; Helper to gmtime and localtime. Breaks down a number of +; seconds since Jan 1, 1970 into days, hours and seconds, +; so that each of them fits in 16 bits; passes the +; result to _mktime which fixes all values in the struct, +; and returns a pointer to the struct to callers. +; + + .export __time_t_to_tm + .import udiv32, _mktime + .importzp sreg, tmp3, ptr1, ptr2, ptr3, ptr4 + + .include "time.inc" + + .macpack cpu + +__time_t_to_tm: + ; Divide number of seconds since epoch, in ptr1:sreg, + ; by 86400 to get the number of days since epoch, and + ; the number of seconds today in the remainder. + + ; Load t as dividend (sreg is already set by the caller) + sta ptr1 + stx ptr1+1 + + ; Load 86400 as divisor + lda #$80 + sta ptr3 + lda #$51 + sta ptr3+1 + lda #$01 + sta ptr4 + lda #$00 + sta ptr4+1 + + ; Clear TM buf while we have zero in A + ldx #.sizeof(tm)-1 +: sta TM,x + dex + bne :- + + ; Divide t/86400 + jsr udiv32 + + ; Store the quotient (the number of full days), and increment + ; by one as epoch starts at day 1. + clc + lda ptr1 + adc #1 + sta TM + tm::tm_mday + lda ptr1+1 + adc #0 + sta TM + tm::tm_mday+1 + + ; Now divide the number of remaining seconds by 3600, + ; to get the number of hours, and the seconds in the + ; current hour, in neat 16-bit integers. + + ; Load the previous division's remainder (in ptr2:tmp3:tmp4) + ; as dividend + lda ptr2 + sta ptr1 + lda ptr2+1 + sta ptr1+1 + lda tmp3 + sta sreg + ; We ignore the high byte stored in tmp4 because it will be + ; zero. We'll zero sreg+1 right below, when we'll have + ; a convenient zero already in A. + + ; Load divisor + lda #<3600 + sta ptr3 + lda #>3600 + sta ptr3+1 + + ; Zero the two high bytes of the divisor and the high byte + ; of the dividend. + .if .cpu .bitand CPU_ISET_65SC02 + stz ptr4 + stz ptr4+1 + stz sreg+1 + .else + lda #$00 + sta ptr4 + sta ptr4+1 + sta sreg+1 + .endif + + ; Do the division + jsr udiv32 + + ; Store year + lda #70 + sta TM + tm::tm_year + + ; Store hours (the quotient of the last division) + lda ptr1 + sta TM + tm::tm_hour + lda ptr1+1 + sta TM + tm::tm_hour+1 + + ; Store seconds (the remainder of the last division) + lda ptr2 + sta TM + tm::tm_sec + lda ptr2+1 + sta TM + tm::tm_sec+1 + + ; The rest of the struct tm fields are zero. mktime + ; will take care of shifting extra seconds to minutes, + ; and extra days to months and years. + + ; Call mktime + lda #TM + jsr _mktime + + ; And return our pointer + lda #TM + rts + + .bss + +TM: .tag tm diff --git a/libsrc/common/mktime.c b/libsrc/common/mktime.c index c9ac1652c..7ea3e2bff 100644 --- a/libsrc/common/mktime.c +++ b/libsrc/common/mktime.c @@ -65,7 +65,8 @@ static const unsigned MonthDays [] = { /* Code */ /*****************************************************************************/ - +/* use statics for size optimisation (~34 bytes) */ +#pragma static-locals(push, on) time_t __fastcall__ mktime (register struct tm* TM) /* Make a time in seconds since 1/1/1970 from the broken down time in TM. @@ -74,8 +75,8 @@ time_t __fastcall__ mktime (register struct tm* TM) */ { register div_t D; - static int Max; - static unsigned DayCount; + int Max; + unsigned DayCount; /* Check if TM is valid */ if (TM == 0) { @@ -182,3 +183,5 @@ time_t __fastcall__ mktime (register struct tm* TM) ((unsigned) TM->tm_sec) - _tz.timezone; } + +#pragma static-locals(pop) From 03d5e5fba0ab9394d1f4ccd75068fb6d31cabe72 Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Wed, 10 Jan 2024 22:20:43 +0100 Subject: [PATCH 2/4] Rewrite mktime in assembly -415 bytes, -39% cycles, Unit test expanded to cover more cases (there was a bug in 2100 before!) --- libsrc/common/_is_leap_year.h | 22 -- libsrc/common/_is_leap_year.s | 23 -- libsrc/common/_time_t_to_tm.s | 2 +- libsrc/common/divt.s | 2 +- libsrc/common/mktime.c | 187 ---------- libsrc/common/mktime.s | 476 +++++++++++++++++++++++++ test/val/lib_common_gmtime_localtime.c | 27 +- test/val/lib_common_mktime.c | 119 ++++--- 8 files changed, 575 insertions(+), 283 deletions(-) delete mode 100644 libsrc/common/_is_leap_year.h delete mode 100644 libsrc/common/_is_leap_year.s delete mode 100644 libsrc/common/mktime.c create mode 100644 libsrc/common/mktime.s diff --git a/libsrc/common/_is_leap_year.h b/libsrc/common/_is_leap_year.h deleted file mode 100644 index 378c462ff..000000000 --- a/libsrc/common/_is_leap_year.h +++ /dev/null @@ -1,22 +0,0 @@ -/* -** _is_leap_year.h -** -** (C) Copyright 2024, Colin Leroy-Mira -** -*/ - - - -#ifndef __IS_LEAP_YEAR_H -#define __IS_LEAP_YEAR_H - - - -unsigned char __fastcall__ IsLeapYear (unsigned char Year); -/* Returns 1 if the given year is a leap year. Expects a year from 0 to 206, - * without 1900 added */ - - - -/* End of _is_leap_year.h */ -#endif diff --git a/libsrc/common/_is_leap_year.s b/libsrc/common/_is_leap_year.s deleted file mode 100644 index d3136c1c8..000000000 --- a/libsrc/common/_is_leap_year.s +++ /dev/null @@ -1,23 +0,0 @@ -; -; Colin Leroy-Mira, 2024 -; -; unsigned char __fastcall__ IsLeapYear (unsigned char Year) -; Returns 1 in A if the given year is a leap year. Expects a year from 0 to 206, -; without 1900 added. -; - - .export _IsLeapYear - -_IsLeapYear: - ldx #$00 ; Prepare X for rts - cmp #$00 ; Y 0 (1900) is not a leap year - beq NotLeap - cmp #$C8 ; Y 200 (2100) is not a leap year - beq NotLeap - and #$03 ; Year % 4 == 0 means leap year - bne NotLeap - lda #$01 ; Return 1 - rts -NotLeap: - lda #$00 ; Return 0 - rts diff --git a/libsrc/common/_time_t_to_tm.s b/libsrc/common/_time_t_to_tm.s index ffabf15fc..9bcf84184 100644 --- a/libsrc/common/_time_t_to_tm.s +++ b/libsrc/common/_time_t_to_tm.s @@ -41,7 +41,7 @@ __time_t_to_tm: ldx #.sizeof(tm)-1 : sta TM,x dex - bne :- + bpl :- ; Divide t/86400 jsr udiv32 diff --git a/libsrc/common/divt.s b/libsrc/common/divt.s index 7f2b4e1bb..52b6efd04 100644 --- a/libsrc/common/divt.s +++ b/libsrc/common/divt.s @@ -3,7 +3,7 @@ ; 2002-10-22, Greg King ; ; This signed-division function returns both the quotient and the remainder, -; in this structure: +; in this structure: (quotient in sreg, remainder in AX) ; ; typedef struct { ; int rem, quot; diff --git a/libsrc/common/mktime.c b/libsrc/common/mktime.c deleted file mode 100644 index 7ea3e2bff..000000000 --- a/libsrc/common/mktime.c +++ /dev/null @@ -1,187 +0,0 @@ -/*****************************************************************************/ -/* */ -/* mktime.c */ -/* */ -/* Make calendar time from broken down time and cleanup */ -/* */ -/* */ -/* */ -/* (C) 2002 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ -/* */ -/* */ -/* This software is provided 'as-is', without any expressed or implied */ -/* warranty. In no event will the authors be held liable for any damages */ -/* arising from the use of this software. */ -/* */ -/* Permission is granted to anyone to use this software for any purpose, */ -/* including commercial applications, and to alter it and redistribute it */ -/* freely, subject to the following restrictions: */ -/* */ -/* 1. The origin of this software must not be misrepresented; you must not */ -/* claim that you wrote the original software. If you use this software */ -/* in a product, an acknowledgment in the product documentation would be */ -/* appreciated but is not required. */ -/* 2. Altered source versions must be plainly marked as such, and must not */ -/* be misrepresented as being the original software. */ -/* 3. This notice may not be removed or altered from any source */ -/* distribution. */ -/* */ -/*****************************************************************************/ - - - -#include -#include -#include -#include "_is_leap_year.h" - - -/*****************************************************************************/ -/* Data */ -/*****************************************************************************/ - - - -#define JANUARY 0 -#define FEBRUARY 1 -#define DECEMBER 11 -#define JAN_1_1970 4 /* 1/1/1970 is a thursday */ - - - -static const unsigned char MonthLength [] = { - 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 -}; -static const unsigned MonthDays [] = { - 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 -}; - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - -/* use statics for size optimisation (~34 bytes) */ -#pragma static-locals(push, on) - -time_t __fastcall__ mktime (register struct tm* TM) -/* Make a time in seconds since 1/1/1970 from the broken down time in TM. -** A call to mktime does also correct the time in TM to contain correct -** values. -*/ -{ - register div_t D; - int Max; - unsigned DayCount; - - /* Check if TM is valid */ - if (TM == 0) { - /* Invalid data */ - return (time_t) -1L; - } - - /* Adjust seconds. */ - D = div (TM->tm_sec, 60); - TM->tm_sec = D.rem; - - /* Adjust minutes */ - TM->tm_min += D.quot; - D = div (TM->tm_min, 60); - TM->tm_min = D.rem; - - /* Adjust hours */ - TM->tm_hour += D.quot; - D = div (TM->tm_hour, 24); - TM->tm_hour = D.rem; - - /* Adjust days */ - TM->tm_mday += D.quot; - - /* Adjust year */ - while (1) { - Max = 365UL + IsLeapYear (TM->tm_year); - if ((unsigned int)TM->tm_mday > Max) { - ++TM->tm_year; - TM->tm_mday -= Max; - } else { - break; - } - } - - /* Adjust month and year. This is an iterative process, since changing - ** the month will change the allowed days for this month. - */ - while (1) { - - /* Make sure, month is in the range 0..11 */ - D = div (TM->tm_mon, 12); - TM->tm_mon = D.rem; - TM->tm_year += D.quot; - - /* Now check if mday is in the correct range, if not, correct month - ** and eventually year and repeat the process. - */ - if (TM->tm_mon == FEBRUARY && IsLeapYear (TM->tm_year)) { - Max = 29; - } else { - Max = MonthLength[TM->tm_mon]; - } - if ((unsigned int)TM->tm_mday > Max) { - /* Must correct month and eventually, year */ - if (TM->tm_mon == DECEMBER) { - TM->tm_mon = JANUARY; - ++TM->tm_year; - } else { - ++TM->tm_mon; - } - TM->tm_mday -= Max; - } else { - /* Done */ - break; - } - } - - /* Ok, all time/date fields are now correct. Calculate the days in this - ** year. - */ - TM->tm_yday = MonthDays[TM->tm_mon] + TM->tm_mday - 1; - if (TM->tm_mon > FEBRUARY && IsLeapYear (TM->tm_year)) { - ++TM->tm_yday; - } - - /* Calculate days since 1/1/1970. In the complete epoch (1/1/1970 to - ** somewhere in 2106) all years dividable by 4 are leap years(1), - ** so dividing by 4 gives the days that must be added because of leap years. - ** (and the last leap year before 1970 was 1968) - ** (1): Exception on 2100, which is not leap, and handled just after. - */ - DayCount = ((unsigned) (TM->tm_year-70)) * 365U + - (((unsigned) (TM->tm_year-(68+1))) / 4) + - TM->tm_yday; - - /* Handle the 2100 exception */ - if (TM->tm_year == 200 && TM->tm_mon > FEBRUARY) { - DayCount--; - } else if (TM->tm_year > 200) { - DayCount--; - } - - /* Calculate the weekday */ - TM->tm_wday = (JAN_1_1970 + DayCount) % 7; - - /* No (US) daylight saving (for now) */ - TM->tm_isdst = 0; - - /* Return seconds since 1970 */ - return DayCount * 86400UL + - ((unsigned) TM->tm_hour) * 3600UL + - ((unsigned) TM->tm_min) * 60U + - ((unsigned) TM->tm_sec) - - _tz.timezone; -} - -#pragma static-locals(pop) diff --git a/libsrc/common/mktime.s b/libsrc/common/mktime.s new file mode 100644 index 000000000..ac5755a45 --- /dev/null +++ b/libsrc/common/mktime.s @@ -0,0 +1,476 @@ +; +; Colin Leroy-Mira, 2024 +; +; time_t __fastcall__ mktime (register struct tm* TM) +; +; Converts a struct tm to a time_t timestamp, making sure +; day, month, year, hour, minute and seconds are in the +; correct range. +; + + .export _mktime + .import __tz + .import pushax, pusha0, pusheax + .import shrax2, _div, tosumulax, tosumodax, tossubeax, tosaddeax, tosumuleax + .importzp ptr2, tmp3, sreg + + .include "time.inc" + +; ------------------------------------------------------------------------ +; Special values + +FEBRUARY = 1 +MARCH = 2 +JAN_1_1970 = 4 +N_SEC = 60 +N_MIN = 60 +N_HOUR = 24 +N_MON = 12 +N_DAY_YEAR = 365 +; ------------------------------------------------------------------------ +; Helpers + + ; Helper to shift overflows from one field to the next + ; Current field in Y, divisor in A + ; Keeps remainder in current field, and adds the quotient + ; to the next one +adjust_field: + pha ; Push divisor + iny ; Point to high byte of current field + lda (ptr2),y + tax + dey + sty tmp3 ; Store current field (_div will mess with + lda (ptr2),y ; tmp1 and tmp2) + jsr pushax + pla ; Load divisor + ldx #$00 + + jsr _div + + ldy tmp3 ; Store remainder in current field + sta (ptr2),y + iny + txa + sta (ptr2),y + + lda sreg ; Add quotient to next field + iny + clc + adc (ptr2),y + sta (ptr2),y + iny + lda sreg+1 + adc (ptr2),y + sta (ptr2),y + rts + + ; Returns 1 in A if the given year is a leap year. Expects a year + ; from 0 to 206, without 1900 added. +is_leap_year: + cmp #$00 ; Y 0 (1900) is not a leap year + beq not_leap + cmp #$C8 ; Y 200 (2100) is not a leap year + beq not_leap + and #$03 ; Year % 4 == 0 means leap year + bne not_leap + lda #$01 ; Return 1 + rts +not_leap: + lda #$00 ; Return 0 + rts + + ; Returns the number of days in the current month/year in A +get_days_in_month: + ldy #tm::tm_mon + lda (ptr2),y + tax + lda months_len,x + cpx #FEBRUARY + beq :+ + rts +: tax + ldy #tm::tm_year ; Adjust for leap years + lda (ptr2),y + jsr is_leap_year + beq :+ + inx +: txa + rts + + ; Add AX to counter +addaxcounter: + clc + adc Counter + sta Counter ; Store in Counter + txa + adc Counter+1 + sta Counter+1 + rts + + ; Helpers for long chain of arithmetic on day counter. + ; Reload Counter and push it on the stack +load_and_push_counter: + lda Counter+3 + sta sreg+1 + lda Counter+2 + sta sreg + lda Counter + ldx Counter+1 + jsr pusheax + rts + + ; Store result in AX:sreg to Counter +store_counter: + sta Counter + stx Counter+1 + lda sreg + sta Counter+2 + lda sreg+1 + sta Counter+3 + rts + +; ------------------------------------------------------------------------ +; Code + +_mktime: + sta ptr2 ; Store struct to ptr2, which arithmetic + stx ptr2+1 ; functions won't touch + + ; Check pointer validity + ora ptr2+1 + bne :+ + lda #$FF + tax + sta sreg + sta sreg+1 + rts + + ; Adjust seconds +: ldy #tm::tm_sec + lda #N_SEC + jsr adjust_field + + ; Adjust minutes + ldy #tm::tm_min + lda #N_MIN + jsr adjust_field + + ; Adjust hours + ldy #tm::tm_hour + lda #N_HOUR + jsr adjust_field + + ;Shift one year as long as tm_mday is more than a year + ldy #tm::tm_year + lda (ptr2),y + +dec_by_year: + jsr is_leap_year ; Compute max numbers of days in year + clc + adc #N_DAY_YEAR + beq :+ ; High byte equal, check low byte + bcs do_year_dec ; High byte greater, decrement + bcc dec_by_month ; Low byte lower, we're done +: dey + lda (ptr2),y + cmp Max + bcc dec_by_month + beq dec_by_month + +do_year_dec: + ; Decrement days + ldy #tm::tm_mday + lda (ptr2),y + sbc Max ; Carry already set + sta (ptr2),y + iny + lda (ptr2),y + sbc #>N_DAY_YEAR + sta (ptr2),y + + ; Increment year + ldy #tm::tm_year + lda (ptr2),y + clc + adc #1 + sta (ptr2),y ; No carry possible here either + bcc dec_by_year ; bra, go check next year + +dec_by_month: + ; We're done decrementing days by full years, now do it + ; month per month. + ldy #tm::tm_mon + lda #N_MON + jsr adjust_field + + ; Get max day for this month + jsr get_days_in_month + sta Max + + ; So, do we have more days than this month? + ldy #tm::tm_mday+1 + lda (ptr2),y + bne do_month_dec ; High byte not zero, sure we do + dey + lda (ptr2),y + cmp Max + bcc calc_tm_yday ; No + beq calc_tm_yday + +do_month_dec: + ; Decrement days + ldy #tm::tm_mday + lda (ptr2),y + sec + sbc Max + sta (ptr2),y + iny + lda (ptr2),y + sbc #$00 + sta (ptr2),y + + ; Increment month + ldy #tm::tm_mon + lda (ptr2),y + clc + adc #1 + sta (ptr2),y + + bne dec_by_month ; Check next month + +calc_tm_yday: + ; We finished decrementing tm_mday and have put it in the correct + ; year/month range. Now compute the day of the year. + ldy #tm::tm_mday ; Get current day of month + lda (ptr2),y + sta Counter ; Store it in Counter + + lda #$00 ; Init counter high bytes + sta Counter+1 + sta Counter+2 + sta Counter+3 + + ldy #tm::tm_mon ; Get current month + lda (ptr2),y + asl + tax + clc + lda yday_by_month,x ; Get yday for this month's start + adc Counter ; Add it to counter + sta Counter + inx + lda yday_by_month,x + adc Counter+1 + sta Counter+1 + + ldy #tm::tm_year ; Adjust for leap years (if after feb) + lda (ptr2),y + jsr is_leap_year + beq dec_counter + ldy #tm::tm_mon ; Leap year, get current month + lda (ptr2),y + cmp #MARCH + bcs store_yday + +dec_counter: + lda Counter ; Decrease counter by one (yday starts at 0), + bne :+ ; unless we're after february in a leap year + dec Counter+1 +: dec Counter + +store_yday: + ldy #tm::tm_yday ; Store tm_yday + lda Counter + sta (ptr2),y + iny + lda Counter+1 + sta (ptr2),y + + ; Now calculate total day count since epoch with the formula: + ; ((unsigned) (TM->tm_year-70)) * 365U + (number of days per year since 1970) + ; (((unsigned) (TM->tm_year-(68+1))) / 4) + (one extra day per leap year since 1970) + ; TM->tm_yday (number of days in this year) + + ldy #tm::tm_year ; Get full years + lda (ptr2),y + sec + sbc #70 + ldx #0 + jsr pushax + lda #N_DAY_YEAR + + jsr tosumulax + jsr addaxcounter + + ; Add one day per leap year + ldy #tm::tm_year ; Get full years + lda (ptr2),y + sec + sbc #69 + ldx #0 + jsr shrax2 ; Divide by 4 + + jsr addaxcounter + + ; Handle the 2100 exception (which was considered leap by "Add one day + ; per leap year" just before) + ldy #tm::tm_year ; Get full years + lda (ptr2),y + cmp #201 + bcc finish_calc ; <= 200, nothing to do + + lda Counter + bne :+ + dec Counter+1 +: dec Counter + +finish_calc: + ; Now we can compute the weekday. + lda Counter + clc + adc #JAN_1_1970 + pha + lda Counter+1 + adc #0 + tax + pla + jsr pushax + + lda #7 ; Modulo 7 + ldx #0 + jsr tosumodax + + ldy #tm::tm_wday ; Store tm_wday + sta (ptr2),y + iny + txa + sta (ptr2),y + + ; DST + lda #$00 ; Store tm_isdst + ldy #tm::tm_isdst + sta (ptr2),y + iny + sta (ptr2),y + + ; Our struct tm is all fixed and every field calculated. + ; We can finally count seconds according to this formula: + ; seconds = (full days since epoch) * 86400UL + + ; ((unsigned) TM->tm_hour) * 3600UL + + ; ((unsigned) TM->tm_min) * 60U + + ; ((unsigned) TM->tm_sec) - + ; _tz.timezone; + + ; We already have the number of days since epoch in our counter, + ; from just before when we computed tm_wday. Reuse it. + jsr load_and_push_counter + lda #$00 ; Multiply by 86400 + sta sreg+1 + lda #$01 + sta sreg + lda #$80 + ldx #$51 + jsr tosumuleax + jsr store_counter ; Store into counter + + ; Push counter to add 3600 * hours to it + jsr load_and_push_counter + + ldx #$00 ; Load hours + stx sreg + stx sreg+1 + ldy #tm::tm_hour + lda (ptr2),y + jsr pusheax ; Push + ldx #$00 ; Load 3600 + stx sreg + stx sreg+1 + lda #<3600 + ldx #>3600 + jsr tosumuleax ; Multiply (pops the pushed hours) + jsr tosaddeax ; Add to counter (pops the pushed counter) + jsr store_counter ; Store counter + + ; Push counter to add 60 * min to it + jsr load_and_push_counter + + ldy #tm::tm_min ; Load minutes + lda (ptr2),y + jsr pusha0 ; Push + lda #N_MIN + ldx #0 + stx sreg + stx sreg+1 + jsr tosumulax ; Multiply + jsr tosaddeax ; Add to pushed counter + jsr store_counter ; Store + + ; Add seconds + jsr load_and_push_counter + + ldy #tm::tm_sec ; Load seconds + lda (ptr2),y + ldx #0 + stx sreg + stx sreg+1 + jsr tosaddeax ; Simple addition there + + ; No need to store/load/push the counter here, simply to push it + ; for the last substraction + jsr pusheax + + ; Substract timezone + lda __tz+1+3 + sta sreg+1 + lda __tz+1+2 + sta sreg + ldx __tz+1+1 + lda __tz+1 + jsr tossubeax + + ; And we're done! + rts + + .data + +months_len: + .byte 31 + .byte 28 + .byte 31 + .byte 30 + .byte 31 + .byte 30 + .byte 31 + .byte 31 + .byte 30 + .byte 31 + .byte 30 + .byte 31 + +yday_by_month: + .word 0 + .word 31 + .word 59 + .word 90 + .word 120 + .word 151 + .word 181 + .word 212 + .word 243 + .word 273 + .word 304 + .word 334 + + + .bss + +Max: .res 1 ; We won't need a high byte +Counter: + .res 4 diff --git a/test/val/lib_common_gmtime_localtime.c b/test/val/lib_common_gmtime_localtime.c index 143d15831..9ba4d6a0d 100644 --- a/test/val/lib_common_gmtime_localtime.c +++ b/test/val/lib_common_gmtime_localtime.c @@ -6,6 +6,9 @@ int fails = 0; time_t timestamps[] = { 0, + 0x41eb00, + 0x1e7cb00, + 0x21c8700, 0x2FFFFFFF, 0x6FFFFFFF, 0xF48656FF, @@ -19,6 +22,9 @@ time_t timestamps[] = { /* Values checked against glibc 2.37's implementation of ctime() */ const char *dates_gmt[] = { "Thu Jan 1 00:00:00 1970\n", + "Fri Feb 20 00:00:00 1970\n", + "Wed Jan 6 00:00:00 1971\n", + "Mon Feb 15 00:00:00 1971\n", "Sun Jul 9 16:12:47 1995\n", "Wed Jul 18 05:49:51 2029\n", "Thu Dec 31 23:59:59 2099\n", @@ -32,6 +38,9 @@ const char *dates_gmt[] = { const char *dates_gmt_plus_one[] = { "Thu Jan 1 01:00:00 1970\n", + "Fri Feb 20 01:00:00 1970\n", + "Wed Jan 6 01:00:00 1971\n", + "Mon Feb 15 01:00:00 1971\n", "Sun Jul 9 17:12:47 1995\n", "Wed Jul 18 06:49:51 2029\n", "Fri Jan 1 00:59:59 2100\n", @@ -70,15 +79,15 @@ int main (void) timestamps[i], dates_gmt[i], str); } - /* Check localtime at UTC+1 */ - _tz.timezone = 3600; - tm = localtime(×tamps[i]); - str = asctime(tm); - if (strcmp(str, dates_gmt_plus_one[i])) { - fails++; - printf("localtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", - timestamps[i], dates_gmt_plus_one[i], str); - } + // /* Check localtime at UTC+1 */ + // _tz.timezone = 3600; + // tm = localtime(×tamps[i]); + // str = asctime(tm); + // if (strcmp(str, dates_gmt_plus_one[i])) { + // fails++; + // printf("localtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", + // timestamps[i], dates_gmt_plus_one[i], str); + // } } return fails; } diff --git a/test/val/lib_common_mktime.c b/test/val/lib_common_mktime.c index 832ce3834..5d42db874 100644 --- a/test/val/lib_common_mktime.c +++ b/test/val/lib_common_mktime.c @@ -4,58 +4,97 @@ int fails = 0; -time_t timestamps[] = { - 0, - 0x2FFFFFFF, - 0x6FFFFFFF, - 0xF48656FF, - 0xF4865700, - 0xFC5A3EFF, - 0x6D6739FF, - 0x6D673A00, - 0xFFFFFFFF, +typedef struct _test_data { + time_t t; + struct tm tm; + char *str; +} test_data; + +/* Test data generated using glibc 2.37 */ +test_data data[] = { + /* First year */ + {0x00000000, {0, 0, 0, 1, 0, 70, 0, 4}, "Thu Jan 1 00:00:00 1970\n"}, + {0x004e7970, {56, 34, 12, 1, 2, 70, 59, 0}, "Sun Mar 1 12:34:56 1970\n"}, + {0x01e1337f, {59, 59, 23, 31, 11, 70, 364, 4}, "Thu Dec 31 23:59:59 1970\n"}, + + /* First leap year */ + {0x03c26700, {0, 0, 0, 1, 0, 72, 0, 6}, "Sat Jan 1 00:00:00 1972\n"}, + {0x03c8fe7f, {59, 59, 23, 5, 0, 72, 4, 3}, "Wed Jan 5 23:59:59 1972\n"}, + {0x041180ff, {59, 59, 23, 29, 1, 72, 59, 2}, "Tue Feb 29 23:59:59 1972\n"}, + {0x04118100, {0, 0, 0, 1, 2, 72, 60, 3}, "Wed Mar 1 00:00:00 1972\n"}, + {0x05a4ebff, {59, 59, 23, 31, 11, 72, 365, 0}, "Sun Dec 31 23:59:59 1972\n"}, + + /* A non-leap year */ + {0x63b0cd00, {0, 0, 0, 1, 0, 123, 0, 0}, "Sun Jan 1 00:00:00 2023\n"}, + {0x63fe957f, {59, 59, 23, 28, 1, 123, 58, 2}, "Tue Feb 28 23:59:59 2023\n"}, + {0x63fe9580, {0, 0, 0, 1, 2, 123, 59, 3}, "Wed Mar 1 00:00:00 2023\n"}, + {0x656d4ec0, {0, 0, 4, 4, 11, 123, 337, 1}, "Mon Dec 4 04:00:00 2023\n"}, + {0x6592007f, {59, 59, 23, 31, 11, 123, 364, 0}, "Sun Dec 31 23:59:59 2023\n"}, + + /* Another leap year */ + {0x65920080, {0, 0, 0, 1, 0, 124, 0, 1}, "Mon Jan 1 00:00:00 2024\n"}, + {0x65e11a7f, {59, 59, 23, 29, 1, 124, 59, 4}, "Thu Feb 29 23:59:59 2024\n"}, + {0x65e11a80, {0, 0, 0, 1, 2, 124, 60, 5}, "Fri Mar 1 00:00:00 2024\n"}, + {0x6774857f, {59, 59, 23, 31, 11, 124, 365, 2}, "Tue Dec 31 23:59:59 2024\n"}, + + /* End of century */ + {0xf48656ff, {59, 59, 23, 31, 11, 199, 364, 4}, "Thu Dec 31 23:59:59 2099\n"}, + + /* A non-leap year for exceptional reasons */ + {0xf4865700, {0, 0, 0, 1, 0, 200, 0, 5}, "Fri Jan 1 00:00:00 2100\n"}, + {0xf4d41f7f, {59, 59, 23, 28, 1, 200, 58, 0}, "Sun Feb 28 23:59:59 2100\n"}, + {0xf4d41f80, {0, 0, 0, 1, 2, 200, 59, 1}, "Mon Mar 1 00:00:00 2100\n"}, + {0xf4fceff0, {0, 0, 23, 31, 2, 200, 89, 3}, "Wed Mar 31 23:00:00 2100\n"}, + {0xf6678a7f, {59, 59, 23, 31, 11, 200, 364, 5}, "Fri Dec 31 23:59:59 2100\n"}, + + /* First post-2100 leap year */ + {0xfc0b2500, {0, 0, 0, 1, 0, 204, 0, 2}, "Tue Jan 1 00:00:00 2104\n"}, + {0xfc5a3eff, {59, 59, 23, 29, 1, 204, 59, 5}, "Fri Feb 29 23:59:59 2104\n"}, + {0xfc5a3f00, {0, 0, 0, 1, 2, 204, 60, 6}, "Sat Mar 1 00:00:00 2104\n"}, + {0xfcaa9c70, {0, 0, 23, 30, 3, 204, 120, 3}, "Wed Apr 30 23:00:00 2104\n"}, + + /* End of epoch */ + {0xfdedaa00, {0, 0, 0, 1, 0, 205, 0, 4}, "Thu Jan 1 00:00:00 2105\n"}, + {0xffffffff, {15, 28, 6, 7, 1, 206, 37, 0}, "Sun Feb 7 06:28:15 2106\n"} }; -/* Values checked against glibc 2.37's implementation of ctime() */ -const char *dates[] = { - "Thu Jan 1 00:00:00 1970\n", - "Sun Jul 9 16:12:47 1995\n", - "Wed Jul 18 05:49:51 2029\n", - "Thu Dec 31 23:59:59 2099\n", - "Fri Jan 1 00:00:00 2100\n", - "Fri Feb 29 23:59:59 2104\n", - "Tue Feb 29 23:59:59 2028\n", - "Wed Mar 1 00:00:00 2028\n", - "Sun Feb 7 06:28:15 2106\n", - NULL -}; +static int compare_tm(time_t t, struct tm *tm, struct tm *ref) { + if (memcmp(tm, ref, sizeof(tm))) { + printf("0x%lx: unexpected tm from gmtime: " + "expected {%u, %u, %u, %u, %u, %u, %u, %u}, " + "got {%u, %u, %u, %u, %u, %u, %u, %u}\n", + t, + ref->tm_sec, ref->tm_min, ref->tm_hour, ref->tm_mday, ref->tm_mon, ref->tm_year, ref->tm_yday, ref->tm_wday, + tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_yday, tm->tm_wday); + return 1; + } + return 0; +} int main (void) { - struct tm tm; - time_t t; int i; /* Verify conversion both ways */ - for (t = 0x0FFFFFFF; ; t += 0x10000000) { + for (i = 0; ; i++) { + time_t t = data[i].t; + time_t r; struct tm *tm = gmtime(&t); - time_t r = mktime(tm); - if (t != r) { - fails++; - printf("Unexpected result for t %lx: %lx\n", t, r); - } - if (t == 0xFFFFFFFF) { - break; - } - } + r = mktime(tm); - for (i = 0; dates[i] != NULL; i++) { - char *str = ctime(×tamps[i]); - if (strcmp(str, dates[i])) { + if (t != r) { + printf("unexpected timestamp from mktime: expected 0x%lx, got 0x%lx\n", t, r); fails++; - printf("Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", - timestamps[i], dates[i], str); } + if (compare_tm(t, tm, &data[i].tm)) { + fails++; + } + if (strcmp(data[i].str, ctime(&t))) { + printf("0x%lx: unexpected ctime result: expected %s, got %s", t, data[i].str, ctime(&t)); + } + + if (t == 0xFFFFFFFF) + break; } return fails; } From 10282a9b7402f5390fb93b6938ed902e495f7a7c Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Wed, 10 Jan 2024 22:26:55 +0100 Subject: [PATCH 3/4] Rewrite asctime() in assembler (-7 bytes) --- libsrc/common/asctime.c | 59 ------------------------------ libsrc/common/asctime.s | 81 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 59 deletions(-) delete mode 100644 libsrc/common/asctime.c create mode 100644 libsrc/common/asctime.s diff --git a/libsrc/common/asctime.c b/libsrc/common/asctime.c deleted file mode 100644 index b46f29128..000000000 --- a/libsrc/common/asctime.c +++ /dev/null @@ -1,59 +0,0 @@ -/*****************************************************************************/ -/* */ -/* asctime.c */ -/* */ -/* Convert a broken down time into a string */ -/* */ -/* */ -/* */ -/* (C) 2002 Ullrich von Bassewitz */ -/* Wacholderweg 14 */ -/* D-70597 Stuttgart */ -/* EMail: uz@musoftware.de */ -/* */ -/* */ -/* This software is provided 'as-is', without any expressed or implied */ -/* warranty. In no event will the authors be held liable for any damages */ -/* arising from the use of this software. */ -/* */ -/* Permission is granted to anyone to use this software for any purpose, */ -/* including commercial applications, and to alter it and redistribute it */ -/* freely, subject to the following restrictions: */ -/* */ -/* 1. The origin of this software must not be misrepresented; you must not */ -/* claim that you wrote the original software. If you use this software */ -/* in a product, an acknowledgment in the product documentation would be */ -/* appreciated but is not required. */ -/* 2. Altered source versions must be plainly marked as such, and must not */ -/* be misrepresented as being the original software. */ -/* 3. This notice may not be removed or altered from any source */ -/* distribution. */ -/* */ -/*****************************************************************************/ - - - -#include -#include - - - -/*****************************************************************************/ -/* Code */ -/*****************************************************************************/ - -/* - CAUTION: we need to reserve enough space to be able to hold the maximum - length string: - - 1234567890123456789012345678901234567 - "Wednesday September ..1 00:00:00 1970" -*/ - -char* __fastcall__ asctime (const struct tm* timep) -{ - static char buf[38]; - - /* Format into given buffer and return the result */ - return strftime (buf, sizeof (buf), "%c\n", timep)? buf : 0; -} diff --git a/libsrc/common/asctime.s b/libsrc/common/asctime.s new file mode 100644 index 000000000..efcf34b41 --- /dev/null +++ b/libsrc/common/asctime.s @@ -0,0 +1,81 @@ +; +; Colin Leroy-Mira, 2024 +; +; char* __fastcall__ asctime (const struct tm* timep) +; + + .export _asctime + .import _strftime, pushax + .importzp ptr1 + .include "time.inc" + + .macpack cpu + +; ------------------------------------------------------------------------ +; Special values + +; We need to be able to store up to 38 bytes: +; 1234567890123456789012345678901234567 +; "Wednesday September ..1 00:00:00 1970" +MAX_BUF_LEN = 38 + +; ------------------------------------------------------------------------ +; Code + +_asctime: + ; Backup timep + .if (.cpu .bitand ::CPU_ISET_65SC02) + pha + phx + .else + sta ptr1 + stx ptr1+1 + .endif + + ; Push buf + lda #buf + jsr pushax + + ; Push sizeof(buf) + lda #MAX_BUF_LEN + jsr pushax + + ; Push format string + lda #fmt + jsr pushax + + ; Restore timep + .if (.cpu .bitand ::CPU_ISET_65SC02) + plx + pla + .else + lda ptr1 + ldx ptr1+1 + .endif + + ; Call formatter + jsr _strftime + + ; Check return status + bne :+ + cpx #$00 + bne :+ + rts + +: lda #buf + rts + + .data + +fmt: .byte '%' + .byte 'c' + .byte $0A + .byte $00 + + .bss + +buf: .res MAX_BUF_LEN From 01ee903cdfadf418e84c9e2243b652d64b9ca06f Mon Sep 17 00:00:00 2001 From: Colin Leroy-Mira Date: Wed, 10 Jan 2024 22:43:49 +0100 Subject: [PATCH 4/4] Fixup gmtime/localtime/mktime tests with all cases --- test/val/lib_common_gmtime_localtime.c | 151 ++++++++++++++----------- test/val/lib_common_mktime.c | 6 + 2 files changed, 90 insertions(+), 67 deletions(-) diff --git a/test/val/lib_common_gmtime_localtime.c b/test/val/lib_common_gmtime_localtime.c index 9ba4d6a0d..f0d9a8332 100644 --- a/test/val/lib_common_gmtime_localtime.c +++ b/test/val/lib_common_gmtime_localtime.c @@ -4,90 +4,107 @@ int fails = 0; -time_t timestamps[] = { - 0, - 0x41eb00, - 0x1e7cb00, - 0x21c8700, - 0x2FFFFFFF, - 0x6FFFFFFF, - 0xF48656FF, - 0xF4865700, - 0xFC5A3EFF, - 0x6D6739FF, - 0x6D673A00, - 0xFFFFFFFF, -}; +typedef struct _test_data { + time_t t; + char *gmt; + char *local; +} test_data; -/* Values checked against glibc 2.37's implementation of ctime() */ -const char *dates_gmt[] = { - "Thu Jan 1 00:00:00 1970\n", - "Fri Feb 20 00:00:00 1970\n", - "Wed Jan 6 00:00:00 1971\n", - "Mon Feb 15 00:00:00 1971\n", - "Sun Jul 9 16:12:47 1995\n", - "Wed Jul 18 05:49:51 2029\n", - "Thu Dec 31 23:59:59 2099\n", - "Fri Jan 1 00:00:00 2100\n", - "Fri Feb 29 23:59:59 2104\n", - "Tue Feb 29 23:59:59 2028\n", - "Wed Mar 1 00:00:00 2028\n", - "Sun Feb 7 06:28:15 2106\n", - NULL -}; +/* Test data generated using glibc 2.37 */ +test_data data[] = { + /* First year */ + {0x00000000, "Thu Jan 1 00:00:00 1970\n", "Thu Jan 1 01:00:00 1970\n"}, + {0x004e7970, "Sun Mar 1 12:34:56 1970\n", "Sun Mar 1 13:34:56 1970\n"}, + {0x01e1337f, "Thu Dec 31 23:59:59 1970\n", "Fri Jan 1 00:59:59 1971\n"}, -const char *dates_gmt_plus_one[] = { - "Thu Jan 1 01:00:00 1970\n", - "Fri Feb 20 01:00:00 1970\n", - "Wed Jan 6 01:00:00 1971\n", - "Mon Feb 15 01:00:00 1971\n", - "Sun Jul 9 17:12:47 1995\n", - "Wed Jul 18 06:49:51 2029\n", - "Fri Jan 1 00:59:59 2100\n", - "Fri Jan 1 01:00:00 2100\n", - "Sat Mar 1 00:59:59 2104\n", - "Wed Mar 1 00:59:59 2028\n", - "Wed Mar 1 01:00:00 2028\n", - "Thu Jan 1 00:59:59 1970\n", - NULL + /* First leap year */ + {0x03c26700, "Sat Jan 1 00:00:00 1972\n", "Sat Jan 1 01:00:00 1972\n"}, + {0x03c8fe7f, "Wed Jan 5 23:59:59 1972\n", "Thu Jan 6 00:59:59 1972\n"}, + {0x041180ff, "Tue Feb 29 23:59:59 1972\n", "Wed Mar 1 00:59:59 1972\n"}, + {0x04118100, "Wed Mar 1 00:00:00 1972\n", "Wed Mar 1 01:00:00 1972\n"}, + {0x05a4ebff, "Sun Dec 31 23:59:59 1972\n", "Mon Jan 1 00:59:59 1973\n"}, + + /* A non-leap year */ + {0x63b0cd00, "Sun Jan 1 00:00:00 2023\n", "Sun Jan 1 01:00:00 2023\n"}, + {0x63fe957f, "Tue Feb 28 23:59:59 2023\n", "Wed Mar 1 00:59:59 2023\n"}, + {0x63fe9580, "Wed Mar 1 00:00:00 2023\n", "Wed Mar 1 01:00:00 2023\n"}, + {0x656d4ec0, "Mon Dec 4 04:00:00 2023\n", "Mon Dec 4 05:00:00 2023\n"}, + {0x6592007f, "Sun Dec 31 23:59:59 2023\n", "Mon Jan 1 00:59:59 2024\n"}, + + /* Another leap year */ + {0x65920080, "Mon Jan 1 00:00:00 2024\n", "Mon Jan 1 01:00:00 2024\n"}, + {0x65e11a7f, "Thu Feb 29 23:59:59 2024\n", "Fri Mar 1 00:59:59 2024\n"}, + {0x65e11a80, "Fri Mar 1 00:00:00 2024\n", "Fri Mar 1 01:00:00 2024\n"}, + {0x6774857f, "Tue Dec 31 23:59:59 2024\n", "Wed Jan 1 00:59:59 2025\n"}, + + /* End of century */ + {0xf48656ff, "Thu Dec 31 23:59:59 2099\n", "Fri Jan 1 00:59:59 2100\n"}, + + /* A non-leap year for exceptional reasons */ + {0xf4865700, "Fri Jan 1 00:00:00 2100\n", "Fri Jan 1 01:00:00 2100\n"}, + {0xf4d41f7f, "Sun Feb 28 23:59:59 2100\n", "Mon Mar 1 00:59:59 2100\n"}, + {0xf4d41f80, "Mon Mar 1 00:00:00 2100\n", "Mon Mar 1 01:00:00 2100\n"}, + {0xf4fceff0, "Wed Mar 31 23:00:00 2100\n", "Thu Apr 1 00:00:00 2100\n"}, + {0xf6678a7f, "Fri Dec 31 23:59:59 2100\n", "Sat Jan 1 00:59:59 2101\n"}, + + /* First post-2100 leap year */ + {0xfc0b2500, "Tue Jan 1 00:00:00 2104\n", "Tue Jan 1 01:00:00 2104\n"}, + {0xfc5a3eff, "Fri Feb 29 23:59:59 2104\n", "Sat Mar 1 00:59:59 2104\n"}, + {0xfc5a3f00, "Sat Mar 1 00:00:00 2104\n", "Sat Mar 1 01:00:00 2104\n"}, + {0xfcaa9c70, "Wed Apr 30 23:00:00 2104\n", "Thu May 1 00:00:00 2104\n"}, + + /* End of epoch */ + {0xfdedaa00, "Thu Jan 1 00:00:00 2105\n", "Thu Jan 1 01:00:00 2105\n"}, + {0xffffffff, "Sun Feb 7 06:28:15 2106\n", "Thu Jan 1 00:59:59 1970\n"} }; int main (void) { int i; + struct tm *tm; + char *str; - for (i = 0; dates_gmt[i] != NULL; i++) { - struct tm *tm; - char *str; + tm = gmtime(NULL); + if (tm != NULL) { + printf("gmtime should return NULL with a NULL parameter\n"); + fails++; + } - /* Check gmtime */ - tm = gmtime(×tamps[i]); + tm = localtime(NULL); + if (tm != NULL) { + printf("localtime should return NULL with a NULL parameter\n"); + fails++; + } + + /* Verify conversion both ways */ + for (i = 0; ; i++) { + time_t t = data[i].t; + + tm = gmtime(&t); str = asctime(tm); - if (strcmp(str, dates_gmt[i])) { + if (strcmp(data[i].gmt, str)) { + printf("0x%lx: gmtime: unexpected result: expected %s, got %s\n", t, data[i].gmt, str); fails++; - printf("gmtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", - timestamps[i], dates_gmt[i], str); } - - /* Check localtime with UTC timezone */ + _tz.timezone = 0; - tm = localtime(×tamps[i]); + tm = localtime(&t); str = asctime(tm); - if (strcmp(str, dates_gmt[i])) { + if (strcmp(data[i].gmt, str)) { + printf("0x%lx: localtime (UTC+0): unexpected result: expected %s, got %s\n", t, data[i].gmt, str); fails++; - printf("localtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", - timestamps[i], dates_gmt[i], str); } - // /* Check localtime at UTC+1 */ - // _tz.timezone = 3600; - // tm = localtime(×tamps[i]); - // str = asctime(tm); - // if (strcmp(str, dates_gmt_plus_one[i])) { - // fails++; - // printf("localtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n", - // timestamps[i], dates_gmt_plus_one[i], str); - // } + _tz.timezone = 3600; + tm = localtime(&t); + str = asctime(tm); + if (strcmp(data[i].local, str)) { + printf("0x%lx: localtime (UTC+1): unexpected result: expected %s, got %s\n", t, data[i].local, str); + fails++; + } + + if (t == 0xFFFFFFFF) + break; } return fails; } diff --git a/test/val/lib_common_mktime.c b/test/val/lib_common_mktime.c index 5d42db874..6cabef2be 100644 --- a/test/val/lib_common_mktime.c +++ b/test/val/lib_common_mktime.c @@ -75,6 +75,11 @@ int main (void) { int i; + if (mktime(NULL) != (time_t)-1) { + printf("mktime should return -1 with a NULL parameter\n"); + fails++; + } + /* Verify conversion both ways */ for (i = 0; ; i++) { time_t t = data[i].t; @@ -91,6 +96,7 @@ int main (void) } if (strcmp(data[i].str, ctime(&t))) { printf("0x%lx: unexpected ctime result: expected %s, got %s", t, data[i].str, ctime(&t)); + fails++; } if (t == 0xFFFFFFFF)