1 // license:BSD-3-Clause
2 // copyright-holders:Curt Coder
3 /**********************************************************************
4
5 Ricoh RP5C15 Real Time Clock emulation
6
7 *********************************************************************/
8
9 /*
10
11 TODO:
12
13 - 12 hour clock
14 - test register
15 - timer reset
16
17 */
18
19 #include "emu.h"
20 #include "rp5c15.h"
21
22 //#define VERBOSE 1
23 #include "logmacro.h"
24
25
26
27 //**************************************************************************
28 // MACROS / CONSTANTS
29 //**************************************************************************
30
31 // registers
32 enum
33 {
34 REGISTER_1_SECOND = 0, REGISTER_CLOCK_OUTPUT = REGISTER_1_SECOND,
35 REGISTER_10_SECOND, REGISTER_ADJUST = REGISTER_10_SECOND,
36 REGISTER_1_MINUTE,
37 REGISTER_10_MINUTE,
38 REGISTER_1_HOUR,
39 REGISTER_10_HOUR,
40 REGISTER_DAY_OF_THE_WEEK,
41 REGISTER_1_DAY,
42 REGISTER_10_DAY,
43 REGISTER_1_MONTH,
44 REGISTER_10_MONTH, REGISTER_12_24_SELECT = REGISTER_10_MONTH,
45 REGISTER_1_YEAR, REGISTER_LEAP_YEAR = REGISTER_1_YEAR,
46 REGISTER_10_YEAR,
47 REGISTER_MODE,
48 REGISTER_TEST,
49 REGISTER_RESET
50 };
51
52
53 // clock output select
54 enum
55 {
56 CLKOUT_Z = 0,
57 CLKOUT_16384_HZ,
58 CLKOUT_1024_HZ,
59 CLKOUT_128_HZ,
60 CLKOUT_16_HZ,
61 CLKOUT_1_HZ,
62 CLKOUT_1_DIV_60_HZ,
63 CLKOUT_L
64 };
65
66
67 // register write mask
68 static const int register_write_mask[2][16] =
69 {
70 { 0xf, 0x7, 0xf, 0x7, 0xf, 0x3, 0x7, 0xf, 0x3, 0xf, 0x1, 0xf, 0xf, 0xf, 0xf, 0xf },
71 { 0x3, 0x1, 0xf, 0x7, 0xf, 0x3, 0x7, 0xf, 0x3, 0x0, 0x1, 0x3, 0x0, 0xf, 0xf, 0xf }
72 };
73
74
75 // modes
76 enum
77 {
78 MODE00 = 0,
79 MODE01
80 };
81
82
83 // mode register
84 #define MODE_MASK 0x01
85 #define MODE_ALARM_EN 0x04
86 #define MODE_TIMER_EN 0x08
87
88
89 // test register
90 #define TEST_0 0x01
91 #define TEST_1 0x02
92 #define TEST_2 0x04
93 #define TEST_3 0x08
94
95
96 // reset register
97 #define RESET_ALARM 0x01
98 #define RESET_TIMER 0x02
99 #define RESET_16_HZ 0x04
100 #define RESET_1_HZ 0x08
101
102
103
104 //**************************************************************************
105 // GLOBAL VARIABLES
106 //**************************************************************************
107
108 // devices
109 DEFINE_DEVICE_TYPE(RP5C15, rp5c15_device, "rp5c15", "Ricoh RP5C15 RTC")
110
111
112
113 //**************************************************************************
114 // INLINE HELPERS
115 //**************************************************************************
116
117 //-------------------------------------------------
118 // set_alarm_line -
119 //-------------------------------------------------
120
set_alarm_line()121 inline void rp5c15_device::set_alarm_line()
122 {
123 int alarm = ((m_mode & MODE_ALARM_EN) ? m_alarm_on : 1) &
124 ((m_reset & RESET_16_HZ) ? 1 : m_16hz) &
125 ((m_reset & RESET_1_HZ) ? 1 : m_1hz);
126
127 if (m_alarm != alarm)
128 {
129 LOG("RP5C15 Alarm %u\n", alarm);
130
131 m_out_alarm_cb(alarm);
132 m_alarm = alarm;
133 }
134 }
135
136
137 //-------------------------------------------------
138 // read_counter -
139 //-------------------------------------------------
140
read_counter(int counter)141 inline int rp5c15_device::read_counter(int counter)
142 {
143 return (m_reg[MODE00][counter + 1] * 10) + m_reg[MODE00][counter];
144 }
145
146
147 //-------------------------------------------------
148 // write_counter -
149 //-------------------------------------------------
150
write_counter(int counter,int value)151 inline void rp5c15_device::write_counter(int counter, int value)
152 {
153 m_reg[MODE00][counter] = value % 10;
154 m_reg[MODE00][counter + 1] = value / 10;
155 }
156
157
158 //-------------------------------------------------
159 // check_alarm -
160 //-------------------------------------------------
161
check_alarm()162 inline void rp5c15_device::check_alarm()
163 {
164 bool all_match = true;
165 bool all_zeroes = true;
166
167 for (int i = REGISTER_1_MINUTE; i < REGISTER_1_MONTH; i++)
168 {
169 if (m_reg[MODE01][i] != 0) all_zeroes = false;
170 if (m_reg[MODE01][i] != m_reg[MODE00][i]) all_match = false;
171 }
172
173 m_alarm_on = (all_match || (!m_alarm_on && all_zeroes)) ? 0 : 1;
174 }
175
176
177
178 //**************************************************************************
179 // LIVE DEVICE
180 //**************************************************************************
181
182 //-------------------------------------------------
183 // rp5c15_device - constructor
184 //-------------------------------------------------
185
rp5c15_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)186 rp5c15_device::rp5c15_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
187 : device_t(mconfig, RP5C15, tag, owner, clock),
188 device_rtc_interface(mconfig, *this),
189 m_out_alarm_cb(*this),
190 m_out_clkout_cb(*this),
191 m_alarm(1),
192 m_alarm_on(1),
193 m_1hz(1),
194 m_16hz(1),
195 m_clkout(1)
196 {
197 }
198
199 //-------------------------------------------------
200 // device_start - device-specific startup
201 //-------------------------------------------------
202
device_start()203 void rp5c15_device::device_start()
204 {
205 // resolve callbacks
206 m_out_alarm_cb.resolve_safe();
207 m_out_clkout_cb.resolve_safe();
208
209 // allocate timers
210 m_clock_timer = timer_alloc(TIMER_CLOCK);
211 m_clock_timer->adjust(attotime::from_hz(clock() / 16384), 0, attotime::from_hz(clock() / 16384));
212
213 m_16hz_timer = timer_alloc(TIMER_16HZ);
214 m_16hz_timer->adjust(attotime::from_hz(clock() / 1024), 0, attotime::from_hz(clock() / 1024));
215
216 m_clkout_timer = timer_alloc(TIMER_CLKOUT);
217
218 memset(m_reg, 0, sizeof(m_reg));
219 memset(m_ram, 0, sizeof(m_ram));
220 m_mode = 0;
221 m_reset = 0;
222 m_alarm = 0;
223 m_alarm_on = 0;
224 m_1hz = 0;
225 m_16hz = 0;
226 m_clkout = 0;
227
228 // state saving
229 save_item(NAME(m_reg[MODE00]));
230 save_item(NAME(m_reg[MODE01]));
231 save_item(NAME(m_mode));
232 save_item(NAME(m_reset));
233 save_item(NAME(m_alarm));
234 save_item(NAME(m_alarm_on));
235 save_item(NAME(m_1hz));
236 save_item(NAME(m_16hz));
237 save_item(NAME(m_clkout));
238 }
239
240
241 //-------------------------------------------------
242 // device_timer - handler timer events
243 //-------------------------------------------------
244
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)245 void rp5c15_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
246 {
247 switch (id)
248 {
249 case TIMER_CLOCK:
250 if (m_1hz && (m_mode & MODE_TIMER_EN))
251 {
252 advance_seconds();
253 }
254
255 m_1hz = !m_1hz;
256 set_alarm_line();
257 break;
258
259 case TIMER_16HZ:
260 m_16hz = !m_16hz;
261 set_alarm_line();
262 break;
263
264 case TIMER_CLKOUT:
265 m_clkout = !m_clkout;
266 m_out_clkout_cb(m_clkout);
267 break;
268 }
269 }
270
271
272 //-------------------------------------------------
273 // rtc_clock_updated -
274 //-------------------------------------------------
275
rtc_clock_updated(int year,int month,int day,int day_of_week,int hour,int minute,int second)276 void rp5c15_device::rtc_clock_updated(int year, int month, int day, int day_of_week, int hour, int minute, int second)
277 {
278 m_reg[MODE01][REGISTER_LEAP_YEAR] = year % 4;
279 write_counter(REGISTER_1_YEAR, year);
280 write_counter(REGISTER_1_MONTH, month);
281 write_counter(REGISTER_1_DAY, day);
282 m_reg[MODE00][REGISTER_DAY_OF_THE_WEEK] = day_of_week;
283 write_counter(REGISTER_1_HOUR, hour);
284 write_counter(REGISTER_1_MINUTE, minute);
285 write_counter(REGISTER_1_SECOND, second);
286
287 check_alarm();
288 set_alarm_line();
289 }
290
291
292 //-------------------------------------------------
293 // read -
294 //-------------------------------------------------
295
read(offs_t offset)296 uint8_t rp5c15_device::read(offs_t offset)
297 {
298 uint8_t data = 0;
299 offset &= 0x0f;
300
301 switch (offset)
302 {
303 case REGISTER_MODE:
304 data = m_mode;
305 break;
306
307 case REGISTER_TEST:
308 case REGISTER_RESET:
309 // write only
310 break;
311
312 default:
313 data = m_reg[m_mode & MODE_MASK][offset];
314 break;
315 }
316
317 LOG("RP5C15 Register %u Read %02x\n", offset, data);
318
319 return data & 0x0f;
320 }
321
322
323 //-------------------------------------------------
324 // write -
325 //-------------------------------------------------
326
write(offs_t offset,uint8_t data)327 void rp5c15_device::write(offs_t offset, uint8_t data)
328 {
329 data &= 0x0f;
330 offset &= 0x0f;
331
332 switch (offset)
333 {
334 case REGISTER_MODE:
335 m_mode = data;
336
337 LOG("RP5C15 Mode %u\n", data & MODE_MASK);
338 LOG("RP5C15 Timer %s\n", (data & MODE_TIMER_EN) ? "enabled" : "disabled");
339 LOG("RP5C15 Alarm %s\n", (data & MODE_ALARM_EN) ? "enabled" : "disabled");
340 break;
341
342 case REGISTER_TEST:
343 LOG("RP5C15 Test %u not supported!\n", data);
344 break;
345
346 case REGISTER_RESET:
347 m_reset = data;
348
349 if (data & RESET_ALARM)
350 {
351 // reset alarm registers
352 for (int i = REGISTER_1_MINUTE; i < REGISTER_1_MONTH; i++)
353 {
354 m_reg[MODE01][i] = 0;
355 }
356 }
357
358 if (data & RESET_ALARM) LOG("RP5C15 Alarm Reset\n");
359 if (data & RESET_TIMER) LOG("RP5C15 Timer Reset not supported!\n");
360 LOG("RP5C15 16Hz Signal %s\n", (data & RESET_16_HZ) ? "disabled" : "enabled");
361 LOG("RP5C15 1Hz Signal %s\n", (data & RESET_1_HZ) ? "disabled" : "enabled");
362 break;
363
364 default:
365 switch (m_mode & MODE_MASK)
366 {
367 case MODE00:
368 m_reg[MODE00][offset] = data & register_write_mask[MODE00][offset];
369
370 set_time(false, read_counter(REGISTER_1_YEAR), read_counter(REGISTER_1_MONTH), read_counter(REGISTER_1_DAY), m_reg[MODE00][REGISTER_DAY_OF_THE_WEEK],
371 read_counter(REGISTER_1_HOUR), read_counter(REGISTER_1_MINUTE), read_counter(REGISTER_1_SECOND));
372 break;
373
374 case MODE01:
375 switch (offset)
376 {
377 case REGISTER_CLOCK_OUTPUT:
378 switch (data & 0x07)
379 {
380 case CLKOUT_16384_HZ:
381 m_clkout_timer->adjust(attotime::from_hz(clock()), 0, attotime::from_hz(clock()));
382 break;
383
384 case CLKOUT_1024_HZ:
385 m_clkout_timer->adjust(attotime::from_hz(clock() / 16), 0, attotime::from_hz(clock() / 16));
386 break;
387
388 case CLKOUT_128_HZ:
389 m_clkout_timer->adjust(attotime::from_hz(clock() / 128), 0, attotime::from_hz(clock() / 128));
390 break;
391
392 case CLKOUT_16_HZ:
393 m_clkout_timer->adjust(attotime::from_hz(clock() / 1024), 0, attotime::from_hz(clock() / 1024));
394 break;
395
396 case CLKOUT_1_HZ:
397 m_clkout_timer->adjust(attotime::from_hz(clock() / 16384), 0, attotime::from_hz(clock() / 16384));
398 break;
399
400 case CLKOUT_1_DIV_60_HZ:
401 // TODO
402 break;
403
404 case CLKOUT_L:
405 case CLKOUT_Z:
406 m_clkout = 1;
407 m_clkout_timer->adjust(attotime::zero, 0);
408 break;
409 }
410
411 m_reg[MODE01][offset] = data & register_write_mask[MODE01][offset];
412 break;
413
414 case REGISTER_ADJUST:
415 if (data & 0x01)
416 {
417 adjust_seconds();
418 }
419 m_reg[MODE01][offset] = data & register_write_mask[MODE01][offset];
420 break;
421
422 default:
423 m_reg[MODE01][offset] = data & register_write_mask[MODE01][offset];
424 break;
425 }
426 break;
427 }
428
429 LOG("RP5C15 Register %u Write %02x\n", offset, data);
430 break;
431 }
432 }
433