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