1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
/*
Copyright (C) 2025 Mikael Hildenborg
SPDX-License-Identifier: BSD-2-Clause
*/
#include <sys/time.h>
#include <time.h>
#include "atari-gem_errno.h"
#include "atari-traps.h"
/*
Atari can only handle dates from 1980 to 2107.
time_t on m68k is 32bit signed, so that gives us an upper limit of 2038.
The code below uses that limitation to simplify the code.
*/
#define SEC_1970_TO_1980 315529200
#define SEC_1970_TO_MAX 0x7fffffff
#define SECONDS_IN_A_DAY (24 * 60 * 60)
#define SEC_JAN_AND_FEB ((31 + 29) * SECONDS_IN_A_DAY) // In a leap year
#define SECONDS_IN_A_YEAR (365 * SECONDS_IN_A_DAY)
static const short month_to_day[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365};
int gettimeofday(struct timeval* tv, void* __tz)
{
struct timezone* tz = __tz;
if (tz != 0)
{
// Support for timezone have been removed from linux glibc, so we just fill in a zero timezone.
tz->tz_minuteswest = 0;
tz->tz_dsttime = 0;
}
if (tv != 0)
{
unsigned short date = trap1_2a();
unsigned short time = trap1_2c();
int year = ((date >> 9) & 0x7f); // from 1980
int month = ((date >> 5) & 0xf) - 1;
int day = date & 0x1f;
int hour = ((time >> 11) & 0x1f);
int min = ((time >> 5) & 0x3f);
int sec = (time & 0x1f) * 2;
// Days passed in current year
time_t days = day + month_to_day[month];
// Add days for every passed year
days += year * 365;
// Add days for every passed leap year
int months = year * 12 + month; // total months
months -= 2; // remove januari and februari
if (months > 0)
{
// We must have passed at least one leap day.
int leap_days = (months / (12*4)) + 1;
days += leap_days;
}
// Add it all together
tv->tv_sec = (((((days * 24) + hour) * 60) + min) * 60) + sec + SEC_1970_TO_1980;
tv->tv_usec = 0;
}
return 0;
}
int settimeofday(const struct timeval* tv, const struct timezone* tz)
{
// Support for timezone have been removed from linux glibc, so we just ignore it.
if (tv != 0)
{
if (tv->tv_sec < SEC_1970_TO_1980 || tv->tv_sec >= SEC_1970_TO_MAX)
{
// Outside the ranges we can handle.
gem_error_to_errno(GEM_EBADRQ);
return -1;
}
time_t seconds = tv->tv_sec - SEC_1970_TO_1980;
int year = 0;
time_t ysec = SECONDS_IN_A_YEAR;
do
{
ysec = SECONDS_IN_A_YEAR;
if ((year % 4) == 0)
{
ysec += SECONDS_IN_A_DAY;
}
++year;
seconds -= ysec;
} while (seconds >= 0);
--year;
seconds += ysec;
int minutes = seconds / 60;
int hours = minutes / 60;
int days = hours / 24;
int month = 0;
int leap = year % 4;
short ld = month_to_day[0];
for (int m = 0; m < 12; ++m)
{
short d = month_to_day[m + 1];
if (leap == 0 && m > 0) {d += 1;}
if (d > days)
{
month = m;
days -= ld;
break;
}
ld = d;
}
unsigned short date = (unsigned short)(year << 9);
date |= (unsigned short)((month + 1) << 5);
date |= (unsigned short)days;
unsigned short time = (unsigned short)((hours % 24) << 11);
time |= (unsigned short)((minutes % 60) << 5);
time |= (unsigned short)((seconds % 60) >> 1);
int err;
if ((err = trap1_2b(date)) < 0)
{
gem_error_to_errno(err);
return -1;
}
if ((err = trap1_2d(time)) < 0)
{
gem_error_to_errno(err);
return -1;
}
}
return 0;
}
|