1 // license:BSD-3-Clause
2 // copyright-holders:Ryan Holtz
3 /***************************************************************************
4
5 DP8573 Real Time Clock (RTC)
6
7 ***************************************************************************/
8
9 #include "emu.h"
10 #include "machine/dp8573.h"
11 #include "machine/timehelp.h"
12
13 #define LOG_GENERAL (1 << 0)
14 #define LOG_TICKS (1 << 1)
15 #define LOG_ALL (LOG_GENERAL | LOG_TICKS)
16
17 #define VERBOSE (0)
18 #include "logmacro.h"
19
20 // device type definition
21 DEFINE_DEVICE_TYPE(DP8573, dp8573_device, "dp8573", "DP8573 Real-Time Clock")
22
dp8573_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)23 dp8573_device::dp8573_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
24 : device_t(mconfig, DP8573, tag, owner, clock)
25 , device_nvram_interface(mconfig, *this)
26 , m_intr_cb(*this)
27 , m_mfo_cb(*this)
28 {
29 }
30
device_start()31 void dp8573_device::device_start()
32 {
33 save_item(NAME(m_ram));
34 save_item(NAME(m_tscr));
35 save_item(NAME(m_pfr));
36 save_item(NAME(m_millis));
37
38 m_timer = timer_alloc(TIMER_ID);
39 m_timer->adjust(attotime::never);
40
41 m_intr_cb.resolve_safe();
42 m_mfo_cb.resolve_safe();
43
44 memset(m_ram, 0, 32);
45 sync_time();
46
47 m_tscr = 0;
48
49 m_timer->adjust(attotime::from_msec(1), 0, attotime::from_msec(1));
50 }
51
sync_time()52 void dp8573_device::sync_time()
53 {
54 system_time systime;
55 machine().base_datetime(systime);
56
57 m_millis = 0;
58 m_ram[REG_HUNDREDTH] = 0;
59 m_ram[REG_SECOND] = time_helper::make_bcd(systime.utc_time.second);
60 m_ram[REG_MINUTE] = time_helper::make_bcd(systime.utc_time.minute);
61 m_ram[REG_HOUR] = time_helper::make_bcd(systime.utc_time.hour);
62 m_ram[REG_DAY] = time_helper::make_bcd(systime.utc_time.mday);
63 m_ram[REG_MONTH] = time_helper::make_bcd(systime.utc_time.month + 1);
64 m_ram[REG_YEAR] = time_helper::make_bcd(systime.utc_time.year % 100);
65 m_ram[REG_DAYOFWEEK] = time_helper::make_bcd(systime.utc_time.weekday + 1);
66
67 m_pfr = 0;
68
69 // FIXME: should probably rely on nvram start/stop state
70 m_ram[REG_RTMR] = RTMR_CSS;
71 }
72
save_registers()73 void dp8573_device::save_registers()
74 {
75 m_ram[REG_SAVE_SECOND] = m_ram[REG_SECOND];
76 m_ram[REG_SAVE_MINUTE] = m_ram[REG_MINUTE];
77 m_ram[REG_SAVE_HOUR] = m_ram[REG_HOUR];
78 m_ram[REG_SAVE_DAY] = m_ram[REG_DAY];
79 m_ram[REG_SAVE_MONTH] = m_ram[REG_MONTH];
80 }
81
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)82 void dp8573_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
83 {
84 if ((m_pfr & PFR_OSF) || !(m_ram[REG_RTMR] & RTMR_CSS))
85 {
86 LOGMASKED(LOG_TICKS, "Tick suppressed due to OSF or !CSS\n");
87 return;
88 }
89
90 m_pfr |= PFR_1MS;
91
92 bool carry = false;
93 bool tens_carry = false;
94 time_helper::inc_bcd(&m_millis, 0xff, 0x00, 0x09, &tens_carry);
95 if (tens_carry)
96 {
97 m_pfr |= PFR_10MS;
98 carry = time_helper::inc_bcd(&m_ram[REG_HUNDREDTH], 0xff, 0x00, 0x99, &tens_carry);
99 if (tens_carry)
100 m_pfr |= PFR_100MS;
101 }
102 if (carry)
103 {
104 m_pfr |= PFR_1S;
105 carry = time_helper::inc_bcd(&m_ram[REG_SECOND], 0xff, 0x00, 0x59, &tens_carry);
106 if (tens_carry)
107 m_pfr |= PFR_10S;
108 }
109 if (carry)
110 {
111 m_pfr |= PFR_1MIN;
112 carry = time_helper::inc_bcd(&m_ram[REG_MINUTE], 0xff, 0x00, 0x59);
113 }
114 if (carry)
115 {
116 if (m_ram[REG_RTMR] & RTMR_12H)
117 {
118 carry = time_helper::inc_bcd(&m_ram[REG_HOUR], 0xff, 0x01, 0x12);
119 if (carry)
120 {
121 m_ram[REG_HOUR] |= 0x20;
122 carry = !(m_ram[REG_HOUR] & 0x20);
123 }
124 }
125 else
126 {
127 carry = time_helper::inc_bcd(&m_ram[REG_HOUR], 0xff, 0x00, 0x23);
128 }
129 }
130 if (carry)
131 {
132 static const uint8_t daysinmonth[] = { 0x31, 0x28, 0x31, 0x30, 0x31, 0x30, 0x31, 0x31, 0x30, 0x31, 0x30, 0x31 };
133
134 time_helper::inc_bcd(&m_ram[REG_DAYOFWEEK], 0xff, 0x01, 0x07);
135
136 uint8_t month = time_helper::from_bcd(m_ram[REG_MONTH]);
137
138 uint8_t maxdays;
139 if (month == 2 && (m_ram[REG_RTMR] & RTMR_LY) == 0)
140 {
141 maxdays = 0x29;
142 }
143 else if (month >= 1 && month <= 12)
144 {
145 maxdays = daysinmonth[month - 1];
146 }
147 else
148 {
149 maxdays = 0x31;
150 }
151
152 carry = time_helper::inc_bcd(&m_ram[REG_DAY], 0xff, 0x01, maxdays);
153 }
154 if (carry)
155 {
156 carry = time_helper::inc_bcd(&m_ram[REG_MONTH], 0xff, 0x01, 0x12);
157 }
158 if (carry)
159 {
160 carry = time_helper::inc_bcd(&m_ram[REG_YEAR], 0xff, 0x00, 0x99);
161 }
162 if (carry)
163 {
164 // Advance the leap-year counter
165 uint8_t leap = m_ram[REG_RTMR] & RTMR_LY;
166 leap = (leap + 1) & RTMR_LY;
167 m_ram[REG_RTMR] &= ~RTMR_LY;
168 m_ram[REG_RTMR] |= leap;
169 }
170
171 // Check for Time Save mode
172 if (m_tscr & TSCR_TS)
173 {
174 save_registers();
175 }
176
177 // Check for periodic interrupts
178 const uint8_t icr0 = m_ram[REG_PFR_ICR0] & ~ICR0_RAM_MASK;
179 const uint8_t pfr = m_pfr & ~ICR0_RAM_MASK;
180 if (icr0 & pfr)
181 {
182 set_interrupt(MSR_PER);
183 }
184
185 const uint8_t icr1 = m_ram[REG_TSCR_ICR1] & ICR1_COMPARE_MASK;
186 if (icr1)
187 {
188 if (m_ram[REG_SECOND] == m_ram[REG_COMP_SECOND] ||
189 m_ram[REG_MINUTE] == m_ram[REG_COMP_MINUTE] ||
190 m_ram[REG_HOUR] == m_ram[REG_COMP_HOUR] ||
191 m_ram[REG_DAY] == m_ram[REG_COMP_DAY] ||
192 m_ram[REG_MONTH] == m_ram[REG_COMP_MONTH] ||
193 m_ram[REG_DAYOFWEEK] == m_ram[REG_COMP_DAYOFWEEK])
194 {
195 set_interrupt(MSR_AL);
196 }
197 }
198 }
199
set_interrupt(uint8_t mask)200 void dp8573_device::set_interrupt(uint8_t mask)
201 {
202 bool was_intr = m_ram[REG_MSR] & MSR_INT;
203 m_ram[REG_MSR] |= mask;
204
205 if (m_ram[REG_MSR] & MSR_INT_MASK)
206 m_ram[REG_MSR] |= MSR_INT;
207
208 if (!was_intr && (m_ram[REG_MSR] & MSR_INT))
209 m_intr_cb(0);
210 }
211
clear_interrupt(uint8_t mask)212 void dp8573_device::clear_interrupt(uint8_t mask)
213 {
214 bool was_intr = m_ram[REG_MSR] & MSR_INT;
215 m_ram[REG_MSR] &= ~mask;
216
217 if (was_intr && !(m_ram[REG_MSR] & MSR_INT))
218 m_intr_cb(1);
219 }
220
write(offs_t offset,u8 data)221 void dp8573_device::write(offs_t offset, u8 data)
222 {
223 LOGMASKED(LOG_GENERAL, "%s: DP8573 - Register Write: %02x = %02x\n", machine().describe_context(), offset, data);
224
225 switch (offset)
226 {
227 case REG_MSR: // Main Status Register
228 m_ram[offset] &= ~MSR_RAM_MASK;
229 m_ram[offset] |= data & MSR_RAM_MASK;
230 if (data & MSR_CLEARABLE_MASK)
231 clear_interrupt(data & MSR_CLEARABLE_MASK);
232 break;
233
234 case REG_RTMR: // Not Applicable / Real-Time Mode Register
235 if (m_ram[REG_MSR] & MSR_RS)
236 {
237 const uint8_t old = m_ram[offset];
238 m_ram[offset] = data;
239 if ((old ^ data) & RTMR_12H)
240 {
241 uint8_t hour;
242 if (old & RTMR_12H)
243 hour = time_helper::from_bcd(m_ram[REG_HOUR] & 0x1f) + (BIT(m_ram[REG_HOUR], 5) ? 12 : 0);
244 else
245 hour = time_helper::from_bcd(m_ram[REG_HOUR]);
246
247 if (data & RTMR_12H)
248 {
249 m_ram[REG_HOUR] = time_helper::make_bcd(hour % 12);
250 m_ram[REG_HOUR] |= (hour > 11) ? 0x20 : 0;
251 }
252 else
253 {
254 m_ram[REG_HOUR] = time_helper::make_bcd(hour);
255 }
256 }
257 }
258 break;
259
260 case REG_OMR: // Not Applicable / Output Mode Register
261 if (m_ram[REG_MSR] & MSR_RS)
262 {
263 // Not yet implemented: Buffered Crystal Oscillator output on MFO pin
264 m_ram[offset] = data;
265 }
266 break;
267
268 case REG_PFR_ICR0: // Periodic Flag Register / Interrupt Control Register 0
269 if (m_ram[REG_MSR] & MSR_RS)
270 {
271 m_ram[offset] = data;
272 }
273 else
274 {
275 m_pfr &= ~PFR_TM;
276 m_pfr |= data & PFR_TM;
277 }
278 break;
279
280 case REG_TSCR_ICR1: // Time Save Control Register / Interrupt Control Register 1
281 if (m_ram[REG_MSR] & MSR_RS)
282 {
283 m_ram[offset] = data;
284 }
285 else
286 {
287 m_tscr = data & ~TSCR_NA;
288 if (data & TSCR_TS)
289 save_registers();
290 }
291 break;
292
293 case REG_RAM_D1D0: // RAM, D1/D0 bits only
294 m_ram[offset] = data & 3;
295 break;
296
297 case REG_NA_0FH:
298 case REG_NA_10H:
299 case REG_NA_11H:
300 case REG_NA_12H:
301 break;
302
303 default:
304 m_ram[offset] = data;
305 break;
306 }
307 }
308
read(offs_t offset)309 u8 dp8573_device::read(offs_t offset)
310 {
311 uint8_t ret = m_ram[offset];
312
313 if (offset >= REG_RTMR && offset <= REG_TSCR_ICR1)
314 {
315 if (m_ram[REG_MSR] & MSR_RS)
316 {
317 }
318 else
319 {
320 switch (offset)
321 {
322 case REG_RTMR:
323 case REG_OMR:
324 ret = 0;
325 break;
326 case REG_PFR_ICR0:
327 ret = m_pfr;
328 m_pfr &= ~PFR_READ_CLEAR_MASK;
329 break;
330 case REG_TSCR_ICR1:
331 ret = m_tscr;
332 break;
333 default:
334 break;
335 }
336 }
337 }
338
339 LOGMASKED(LOG_GENERAL, "%s: DP8573 - Register Read: %02x = %02x\n", machine().describe_context(), offset, ret);
340 return ret;
341 }
342
nvram_default()343 void dp8573_device::nvram_default()
344 {
345 memset(m_ram, 0, 32);
346 sync_time();
347 }
348
nvram_read(emu_file & file)349 void dp8573_device::nvram_read(emu_file &file)
350 {
351 file.read(m_ram, 32);
352 sync_time();
353 }
354
nvram_write(emu_file & file)355 void dp8573_device::nvram_write(emu_file &file)
356 {
357 file.write(m_ram, 32);
358 }
359