mirror of
https://github.com/cc65/cc65.git
synced 2024-06-25 13:29:41 +00:00
Merge branch 'master' into fptest
This commit is contained in:
commit
bdd8e65464
|
@ -1,4 +1,6 @@
|
|||
This document contains all kinds of information that you should know if you want to contribute to the cc65 project. Before you start, please read all of it. If something is not clear to you, please ask - this document is an ongoing effort and may well be incomplete.
|
||||
This document contains all kinds of information that you should know if you want to contribute to the cc65 project. Before you start, please read all of it. If something is not clear to you, please ask - this document is an ongoing effort and may well be incomplete.
|
||||
|
||||
Also, before you put a lot of work into implementing something you want to contribute, please get in touch with one of the developers and ask if what you are going to do is actually wanted and has a chance of being merged. Perhaps someone else is already working on it, or perhaps what you have in mind is not how we'd expect it to be - talking to us before you start might save you a lot of work in those cases.
|
||||
|
||||
(''Note:'' The word "must" indicates a requirement. The word "should" indicates a recomendation.)
|
||||
|
||||
|
|
|
@ -330,6 +330,7 @@ usage.
|
|||
<item>_dos_type
|
||||
<item>_filetype
|
||||
<item>_datetime
|
||||
<item>beep
|
||||
<item>get_ostype
|
||||
<item>gmtime_dt
|
||||
<item>mktime_dt
|
||||
|
|
|
@ -331,6 +331,7 @@ usage.
|
|||
<item>_dos_type
|
||||
<item>_filetype
|
||||
<item>_datetime
|
||||
<item>beep
|
||||
<item>get_ostype
|
||||
<item>gmtime_dt
|
||||
<item>mktime_dt
|
||||
|
|
|
@ -95,6 +95,7 @@ function.
|
|||
|
||||
<itemize>
|
||||
<item>_dos_type
|
||||
<item><ref id="beep" name="beep">
|
||||
<item><ref id="get_ostype" name="get_ostype">
|
||||
<item><ref id="gmtime_dt" name="gmtime_dt">
|
||||
<item><ref id="mktime_dt" name="mktime_dt">
|
||||
|
@ -106,6 +107,7 @@ function.
|
|||
|
||||
<itemize>
|
||||
<item>_dos_type
|
||||
<item><ref id="beep" name="beep">
|
||||
<item><ref id="get_ostype" name="get_ostype">
|
||||
<item><ref id="gmtime_dt" name="gmtime_dt">
|
||||
<item><ref id="mktime_dt" name="mktime_dt">
|
||||
|
@ -1771,10 +1773,11 @@ used in presence of a prototype.
|
|||
<descrip>
|
||||
<tag/Function/Beep sound.
|
||||
<tag/Header/<tt/<ref id="sym1.h" name="sym1.h">/
|
||||
<tag/Header/<tt/<ref id="apple2.h" name="apple2.h">/
|
||||
<tag/Declaration/<tt/void beep(void);/
|
||||
<tag/Description/<tt/beep/ makes a brief tone.
|
||||
<tag/Notes/<itemize>
|
||||
<item>The function is specific to the Sym-1.
|
||||
<item>The function is specific to the Sym-1 and Apple2 platforms.
|
||||
</itemize>
|
||||
<tag/Availability/cc65
|
||||
<tag/See also/
|
||||
|
|
|
@ -197,6 +197,9 @@ extern void a2_lo_tgi[];
|
|||
|
||||
|
||||
|
||||
void beep (void);
|
||||
/* Beep beep. */
|
||||
|
||||
unsigned char get_ostype (void);
|
||||
/* Get the machine type. Returns one of the APPLE_xxx codes. */
|
||||
|
||||
|
|
20
libsrc/apple2/beep.s
Normal file
20
libsrc/apple2/beep.s
Normal file
|
@ -0,0 +1,20 @@
|
|||
;
|
||||
; Colin Leroy-Mira, 2024
|
||||
;
|
||||
; void beep(void)
|
||||
;
|
||||
|
||||
.export _beep
|
||||
.import BELL
|
||||
|
||||
.include "apple2.inc"
|
||||
|
||||
.segment "LOWCODE"
|
||||
|
||||
_beep:
|
||||
lda CH ; Bell scrambles CH in 80col mode on IIgs, storing
|
||||
pha ; it in OURCH and resetting CH to 0. Save it.
|
||||
jsr BELL
|
||||
pla
|
||||
sta CH ; Restore CH
|
||||
rts
|
20
libsrc/apple2/bell.s
Normal file
20
libsrc/apple2/bell.s
Normal file
|
@ -0,0 +1,20 @@
|
|||
;
|
||||
; Colin Leroy-Mira, 2024
|
||||
;
|
||||
; BELL routine
|
||||
;
|
||||
|
||||
.export BELL
|
||||
|
||||
.include "apple2.inc"
|
||||
|
||||
.segment "LOWCODE"
|
||||
|
||||
BELL:
|
||||
; Switch in ROM and call BELL
|
||||
bit $C082
|
||||
jsr $FF3A ; BELL
|
||||
|
||||
; Switch in LC bank 2 for R/O and return
|
||||
bit $C080
|
||||
rts
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
** _is_leap_year.h
|
||||
**
|
||||
** (C) Copyright 2024, Colin Leroy-Mira <colin@colino.net>
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
|
||||
#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
|
|
@ -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
|
|
@ -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 <time.h>
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* 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;
|
||||
}
|
129
libsrc/common/_time_t_to_tm.s
Normal file
129
libsrc/common/_time_t_to_tm.s
Normal file
|
@ -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
|
||||
bpl :-
|
||||
|
||||
; 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
|
||||
ldx #>TM
|
||||
jsr _mktime
|
||||
|
||||
; And return our pointer
|
||||
lda #<TM
|
||||
ldx #>TM
|
||||
rts
|
||||
|
||||
.bss
|
||||
|
||||
TM: .tag tm
|
|
@ -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 <stdio.h>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* 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;
|
||||
}
|
81
libsrc/common/asctime.s
Normal file
81
libsrc/common/asctime.s
Normal file
|
@ -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
|
||||
ldx #>buf
|
||||
jsr pushax
|
||||
|
||||
; Push sizeof(buf)
|
||||
lda #<MAX_BUF_LEN
|
||||
ldx #>MAX_BUF_LEN
|
||||
jsr pushax
|
||||
|
||||
; Push format string
|
||||
lda #<fmt
|
||||
ldx #>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
|
||||
ldx #>buf
|
||||
rts
|
||||
|
||||
.data
|
||||
|
||||
fmt: .byte '%'
|
||||
.byte 'c'
|
||||
.byte $0A
|
||||
.byte $00
|
||||
|
||||
.bss
|
||||
|
||||
buf: .res MAX_BUF_LEN
|
|
@ -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;
|
||||
|
|
|
@ -1,184 +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 <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#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 */
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
|
||||
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;
|
||||
static int Max;
|
||||
static 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;
|
||||
}
|
476
libsrc/common/mktime.s
Normal file
476
libsrc/common/mktime.s
Normal file
|
@ -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 ; No care about carry,
|
||||
sta Max ; 365+1 doesn't overflow low byte
|
||||
|
||||
ldy #tm::tm_mday+1 ; Do we have more days in store?
|
||||
lda (ptr2),y
|
||||
cmp #>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
|
||||
ldx #>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
|
|
@ -4,81 +4,107 @@
|
|||
|
||||
int fails = 0;
|
||||
|
||||
time_t timestamps[] = {
|
||||
0,
|
||||
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",
|
||||
"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",
|
||||
"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]);
|
||||
tm = localtime(&t);
|
||||
str = asctime(tm);
|
||||
if (strcmp(str, dates_gmt_plus_one[i])) {
|
||||
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++;
|
||||
printf("localtime: Unexpected result for t %lx: Expected \"%s\", got \"%s\"\n",
|
||||
timestamps[i], dates_gmt_plus_one[i], str);
|
||||
}
|
||||
|
||||
if (t == 0xFFFFFFFF)
|
||||
break;
|
||||
}
|
||||
return fails;
|
||||
}
|
||||
|
|
|
@ -4,58 +4,103 @@
|
|||
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
if (mktime(NULL) != (time_t)-1) {
|
||||
printf("mktime should return -1 with a NULL parameter\n");
|
||||
fails++;
|
||||
}
|
||||
|
||||
for (i = 0; dates[i] != NULL; i++) {
|
||||
char *str = ctime(×tamps[i]);
|
||||
if (strcmp(str, dates[i])) {
|
||||
/* Verify conversion both ways */
|
||||
for (i = 0; ; i++) {
|
||||
time_t t = data[i].t;
|
||||
time_t r;
|
||||
struct tm *tm = gmtime(&t);
|
||||
r = mktime(tm);
|
||||
|
||||
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));
|
||||
fails++;
|
||||
}
|
||||
|
||||
if (t == 0xFFFFFFFF)
|
||||
break;
|
||||
}
|
||||
return fails;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user