33#include "schedule.h"
44#include "interrupt.h"
55
6+ #include <assert.h>
67#include <string.h>
78#include <stdio.h>
89
910/* Global GPT state */
1011rtc_state_t rtc ;
1112
12- static void rtc_event (enum sched_item_id id ) {
13- /* Update exactly once a second */
14- sched_repeat (id , 32768 );
15-
16- if (rtc .control & 64 ) { /* (Bit 6) -- Load time */
17- rtc .readSec = rtc .writeSec ;
18- rtc .readMin = rtc .writeMin ;
19- rtc .readHour = rtc .writeHour ;
20- rtc .readDay = rtc .writeDay ;
21- rtc .control &= ~64 ;
22- rtc .interrupt |= 32 ; /* Load operation complete */
23- intrpt_set (INT_RTC , true);
13+ #define RTC_TIME_BITS (8 * 3)
14+ #define RTC_DATETIME_BITS (RTC_TIME_BITS + 16)
15+ #define RTC_DATETIME_MASK ((UINT64_C(1) << RTC_DATETIME_BITS) - 1)
16+
17+ #define TICKS_PER_SECOND 32768
18+ #define LATCH_TICK_OFFSET 16429
19+ #define LOAD_LATCH_TICK_OFFSET (LATCH_TICK_OFFSET + 7)
20+
21+ /* Load status gets set 1 tick after each load completes */
22+ #define LOAD_SEC_FINISHED (1 + 8)
23+ #define LOAD_MIN_FINISHED (LOAD_SEC_FINISHED + 8)
24+ #define LOAD_HOUR_FINISHED (LOAD_MIN_FINISHED + 8)
25+ #define LOAD_DAY_FINISHED (LOAD_HOUR_FINISHED + 16)
26+ #define LOAD_TOTAL_TICKS (LOAD_DAY_FINISHED + 10)
27+ #define LOAD_PENDING UINT8_MAX
28+
29+ static void rtc_process_load (uint8_t endTick ) {
30+ assert (endTick <= LOAD_TOTAL_TICKS );
31+ uint8_t startTick = rtc .loadTicksProcessed ;
32+ assert (startTick != LOAD_PENDING );
33+ if (endTick <= startTick ) {
34+ assert (endTick == startTick );
35+ return ;
36+ }
37+ rtc .loadTicksProcessed = endTick ;
38+ if (startTick >= RTC_DATETIME_BITS ) {
39+ return ;
40+ }
41+ if (endTick >= RTC_DATETIME_BITS ) {
42+ rtc .control &= ~64 ; /* Load bit is cleared after all bits are loaded */
43+ endTick = RTC_DATETIME_BITS ;
2444 }
45+ /* Load is processed 1 bit at a time in each register from most to least significant */
46+ uint64_t writeMask = (RTC_DATETIME_MASK >> startTick ) & ~(RTC_DATETIME_MASK >> endTick );
47+ rtc .counter .value = (rtc .counter .value & ~writeMask ) | (rtc .load .value & writeMask );
48+ }
2549
26- rtc .readSec ++ ;
27- if (rtc .control & 2 ) {
28- rtc .interrupt |= 1 ;
29- intrpt_set (INT_RTC , true);
50+ static void rtc_update_load (void ) {
51+ if (likely (rtc .loadTicksProcessed >= LOAD_TOTAL_TICKS )) {
52+ return ;
3053 }
31- if (rtc .readSec > 59 ) {
32- rtc .readSec = 0 ;
33- if (rtc .control & 4 ) {
34- rtc .interrupt |= 2 ;
35- intrpt_set (INT_RTC , true);
36- }
37- rtc .readMin ++ ;
38- if (rtc .readMin > 59 ) {
39- rtc .readMin = 0 ;
40- if (rtc .control & 8 ) {
41- rtc .interrupt |= 4 ;
42- intrpt_set (INT_RTC , true);
54+ /* Get the number of ticks passed since the latch event */
55+ uint16_t ticks = sched_ticks_remaining (SCHED_RTC );
56+ if (rtc .mode == RTC_TICK ) {
57+ ticks = (TICKS_PER_SECOND - LATCH_TICK_OFFSET ) - ticks ;
58+ } else {
59+ assert (rtc .mode == RTC_LOAD_LATCH );
60+ ticks = (LOAD_LATCH_TICK_OFFSET - LATCH_TICK_OFFSET ) - ticks ;
61+ }
62+ /* Add 1 because the end of the tick range is exclusive */
63+ rtc_process_load (ticks < LOAD_TOTAL_TICKS ? ticks + 1 : LOAD_TOTAL_TICKS );
64+ }
65+
66+ static void rtc_event (enum sched_item_id id ) {
67+ uint8_t control = rtc .control , interrupts ;
68+
69+ switch (rtc .mode ) {
70+ case RTC_TICK :
71+ /* Process any remaining load operations */
72+ if (rtc .loadTicksProcessed < LOAD_TOTAL_TICKS ) {
73+ rtc_process_load (LOAD_TOTAL_TICKS );
74+ }
75+
76+ /* Next event is latch */
77+ rtc .mode = RTC_LATCH ;
78+ sched_repeat (id , LATCH_TICK_OFFSET );
79+
80+ if (!likely (control & 1 )) { /* (Bit 0) -- Enable ticking */
81+ break ;
82+ }
83+
84+ interrupts = 1 ;
85+ if (unlikely (++ rtc .counter .sec >= 60 )) {
86+ if (likely (rtc .counter .sec == 60 )) {
87+ interrupts |= 2 ;
88+ if (unlikely (++ rtc .counter .min >= 60 )) {
89+ if (likely (rtc .counter .min == 60 )) {
90+ interrupts |= 4 ;
91+ if (unlikely (++ rtc .counter .hour >= 24 )) {
92+ if (likely (rtc .counter .hour == 24 )) {
93+ interrupts |= 8 ;
94+ rtc .counter .day ++ ;
95+ }
96+ rtc .counter .hour = 0 ;
97+ }
98+ }
99+ rtc .counter .min = 0 ;
100+ }
101+ }
102+ rtc .counter .sec = 0 ;
103+ }
104+
105+ if ((uint32_t )(rtc .counter .value >> (RTC_DATETIME_BITS - RTC_TIME_BITS )) == rtc .alarm .value ) {
106+ interrupts |= 16 ;
43107 }
44- rtc .readHour ++ ;
45- if (rtc .readHour > 23 ) {
46- rtc .readHour = 0 ;
47- if (rtc .control & 16 ) {
48- rtc .interrupt |= 8 ;
108+ if (interrupts &= control >> 1 ) {
109+ if (!rtc .interrupt ) {
49110 intrpt_set (INT_RTC , true);
50111 }
51- rtc .readDay ++ ;
112+ rtc .interrupt |= interrupts ;
52113 }
53- }
54- }
114+ break ;
55115
56- if ((rtc .control & 32 ) && (rtc .readSec == rtc .alarmSec ) &&
57- (rtc .readMin == rtc .alarmMin ) && (rtc .readHour == rtc .alarmHour )) {
58- rtc .interrupt |= 16 ;
116+ case RTC_LATCH :
117+ if (likely (control & 128 )) { /* (Bit 7) -- Latch enable */
118+ rtc .latched = rtc .counter ;
119+ }
120+ if (unlikely (control & 64 )) { /* (Bit 6) -- Load operation */
121+ /* Enable load processing */
122+ assert (rtc .loadTicksProcessed == LOAD_PENDING );
123+ rtc .loadTicksProcessed = 0 ;
124+ /* Next event is load latch */
125+ rtc .mode = RTC_LOAD_LATCH ;
126+ sched_repeat (id , LOAD_LATCH_TICK_OFFSET - LATCH_TICK_OFFSET );
127+ } else {
128+ /* Next event is tick */
129+ rtc .mode = RTC_TICK ;
130+ sched_repeat (id , TICKS_PER_SECOND - LATCH_TICK_OFFSET );
131+ }
132+ break ;
133+
134+ case RTC_LOAD_LATCH :
135+ /* Always latches regardless of control register */
136+ rtc .latched = rtc .load ;
137+ /* Load latch complete interrupt */
138+ rtc .interrupt |= 32 ;
59139 intrpt_set (INT_RTC , true);
60- }
61- }
140+ /* Next event is tick */
141+ rtc .mode = RTC_TICK ;
142+ sched_repeat (id , TICKS_PER_SECOND - LOAD_LATCH_TICK_OFFSET );
143+ break ;
62144
63- static void hold_read (void ) {
64- rtc .holdSec = rtc .readSec ;
65- rtc .holdMin = rtc .readMin ;
66- rtc .holdHour = rtc .readHour ;
67- rtc .holdDay = rtc .readDay ;
145+ default :
146+ unreachable ();
147+ }
68148}
69149
70150static uint8_t rtc_read (const uint16_t pio , bool peek ) {
@@ -76,50 +156,67 @@ static uint8_t rtc_read(const uint16_t pio, bool peek) {
76156
77157 switch (index ) {
78158 case 0x00 :
79- value = ( rtc .control & 128 ) ? rtc . readSec : rtc . holdSec ;
159+ value = rtc .latched . sec ;
80160 break ;
81161 case 0x04 :
82- value = ( rtc .control & 128 ) ? rtc . readMin : rtc . holdMin ;
162+ value = rtc .latched . min ;
83163 break ;
84164 case 0x08 :
85- value = ( rtc .control & 128 ) ? rtc . readHour : rtc . holdHour ;
165+ value = rtc .latched . hour ;
86166 break ;
87167 case 0x0C : case 0x0D :
88- value = read8 (( rtc .control & 128 ) ? rtc . readDay : rtc . holdDay , bit_offset );
168+ value = read8 (rtc .latched . day , bit_offset );
89169 break ;
90170 case 0x10 :
91- value = rtc .alarmSec ;
171+ value = rtc .alarm . sec ;
92172 break ;
93173 case 0x14 :
94- value = rtc .alarmMin ;
174+ value = rtc .alarm . min ;
95175 break ;
96176 case 0x18 :
97- value = rtc .alarmHour ;
177+ value = rtc .alarm . hour ;
98178 break ;
99179 case 0x20 :
180+ rtc_update_load ();
100181 value = rtc .control ;
101182 break ;
102183 case 0x24 :
103- value = rtc .writeSec ;
184+ value = rtc .load . sec ;
104185 break ;
105186 case 0x28 :
106- value = rtc .writeMin ;
187+ value = rtc .load . min ;
107188 break ;
108189 case 0x2C :
109- value = rtc .writeHour ;
190+ value = rtc .load . hour ;
110191 break ;
111192 case 0x30 : case 0x31 :
112- value = read8 (rtc .writeDay , bit_offset );
193+ value = read8 (rtc .load . day , bit_offset );
113194 break ;
114195 case 0x34 :
115196 value = rtc .interrupt ;
116197 break ;
117198 case 0x3C : case 0x3D : case 0x3E : case 0x3F :
118- value = read8 (rtc .revision , bit_offset );
199+ value = read8 (0x00010500 , bit_offset );
200+ break ;
201+ case 0x40 : {
202+ rtc_update_load ();
203+ /* Convert to signed to treat LOAD_PENDING as negative */
204+ int8_t ticks = rtc .loadTicksProcessed ;
205+ if (likely (ticks >= LOAD_TOTAL_TICKS )) {
206+ value = 0 ;
207+ } else {
208+ value = 8 | ((ticks < LOAD_SEC_FINISHED ) << 4 )
209+ | ((ticks < LOAD_MIN_FINISHED ) << 5 )
210+ | ((ticks < LOAD_HOUR_FINISHED ) << 6 )
211+ | ((ticks < LOAD_DAY_FINISHED ) << 7 );
212+ }
119213 break ;
120- case 0x44 : case 0x45 : case 0x46 : case 0x47 :
121- value = read8 (rtc .readSec | (rtc .readMin <<6 ) | (rtc .readHour <<12 ) | (rtc .readDay <<17 ), bit_offset );
214+ }
215+ case 0x44 : case 0x45 : case 0x46 : case 0x47 : {
216+ uint32_t combined = rtc .latched .sec | (rtc .latched .min << 6 ) | (rtc .latched .hour << 12 ) | (rtc .latched .day << 17 );
217+ value = read8 (combined , bit_offset );
122218 break ;
219+ }
123220 default :
124221 break ;
125222 }
@@ -134,40 +231,46 @@ static void rtc_write(const uint16_t pio, const uint8_t byte, bool poke) {
134231
135232 switch (index ) {
136233 case 0x10 :
137- rtc .alarmSec = byte ;
234+ rtc .alarm . sec = byte & 63 ;
138235 break ;
139236 case 0x14 :
140- rtc .alarmMin = byte ;
237+ rtc .alarm . min = byte & 63 ;
141238 break ;
142239 case 0x18 :
143- rtc .alarmHour = byte ;
240+ rtc .alarm . hour = byte & 31 ;
144241 break ;
145242 case 0x20 :
146- rtc .control = byte ;
147- if (rtc .control & 1 ) {
148- sched_repeat_relative (SCHED_RTC , SCHED_SECOND , 0 , 0 );
243+ if (byte & 64 ) {
244+ rtc_update_load ();
245+ if (!(rtc .control & 64 )) {
246+ /* Load can be pended as soon as the previous one is finished */
247+ assert (rtc .loadTicksProcessed >= RTC_DATETIME_BITS );
248+ rtc .loadTicksProcessed = LOAD_PENDING ;
249+ }
250+ rtc .control = byte ;
149251 } else {
150- sched_clear (SCHED_RTC );
151- }
152- if (!(rtc .control & 128 )) {
153- hold_read ();
252+ /* Don't allow resetting the load bit via write */
253+ rtc .control = byte | (rtc .control & 64 );
154254 }
155255 break ;
156256 case 0x24 :
157- rtc .writeSec = byte ;
257+ rtc_update_load ();
258+ rtc .load .sec = byte & 63 ;
158259 break ;
159260 case 0x28 :
160- rtc .writeMin = byte ;
261+ rtc_update_load ();
262+ rtc .load .min = byte & 63 ;
161263 break ;
162264 case 0x2C :
163- rtc .writeHour = byte ;
265+ rtc_update_load ();
266+ rtc .load .hour = byte & 31 ;
164267 break ;
165268 case 0x30 : case 0x31 :
166- write8 (rtc .writeDay , bit_offset , byte );
269+ rtc_update_load ();
270+ write8 (rtc .load .day , bit_offset , byte );
167271 break ;
168272 case 0x34 :
169- rtc .interrupt &= ~byte ;
170- intrpt_set (INT_RTC , rtc .interrupt & rtc .control & 15 );
273+ intrpt_set (INT_RTC , rtc .interrupt &= ~byte );
171274 break ;
172275 default :
173276 break ;
@@ -180,9 +283,11 @@ static void rtc_init_events(void) {
180283
181284void rtc_reset (void ) {
182285 memset (& rtc , 0 , sizeof rtc );
183- rtc .revision = 0x00010500 ;
286+ rtc .mode = RTC_LATCH ;
287+ rtc .loadTicksProcessed = LOAD_TOTAL_TICKS ;
184288
185289 rtc_init_events ();
290+ sched_repeat_relative (SCHED_RTC , SCHED_SECOND , 0 , LATCH_TICK_OFFSET );
186291
187292 gui_console_printf ("[CEmu] RTC reset.\n" );
188293}
0 commit comments