1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /***************************************************************************
4 
5   RIOT 6532 emulation
6 
7 The timer seems to follow these rules:
8 - When the timer flag changes from 0 to 1 the timer continues to count
9   down at a 1 cycle rate.
10 - When the timer is being read or written the timer flag is reset.
11 - When the timer flag is set and the timer contents are 0, the counting
12   stops.
13 
14 ***************************************************************************/
15 
16 #include "emu.h"
17 #include "6532riot.h"
18 
19 
20 //**************************************************************************
21 //  CONSTANTS
22 //**************************************************************************
23 
24 // device type definition
25 DEFINE_DEVICE_TYPE(RIOT6532, riot6532_device, "riot6532", "6532 RIOT")
26 
27 enum
28 {
29 	TIMER_IDLE,
30 	TIMER_COUNTING,
31 	TIMER_FINISHING
32 };
33 
34 #define TIMER_FLAG      0x80
35 #define PA7_FLAG        0x40
36 
37 
38 
39 /***************************************************************************
40     INTERNAL FUNCTIONS
41 ***************************************************************************/
42 
43 /*-------------------------------------------------
44     update_irqstate - update the IRQ state
45     based on interrupt enables
46 -------------------------------------------------*/
47 
update_irqstate()48 void riot6532_device::update_irqstate()
49 {
50 	int irq = (m_irqstate & m_irqenable) ? ASSERT_LINE : CLEAR_LINE;
51 
52 	if (m_irq != irq)
53 	{
54 		m_irq_cb(irq);
55 		m_irq = irq;
56 	}
57 }
58 
59 
60 /*-------------------------------------------------
61     apply_ddr - combine inputs and outputs
62     according to the DDR
63 -------------------------------------------------*/
64 
apply_ddr(const riot6532_port * port)65 uint8_t riot6532_device::apply_ddr(const riot6532_port *port)
66 {
67 	return (port->m_out & port->m_ddr) | (port->m_in & ~port->m_ddr);
68 }
69 
70 
71 /*-------------------------------------------------
72     update_pa7_state - see if PA7 has changed
73     and signal appropriately
74 -------------------------------------------------*/
75 
update_pa7_state()76 void riot6532_device::update_pa7_state()
77 {
78 	uint8_t data = apply_ddr(&m_port[0]) & 0x80;
79 
80 	/* if the state changed in the correct direction, set the PA7 flag and update IRQs */
81 	if ((m_pa7prev ^ data) && (m_pa7dir ^ data) == 0)
82 	{
83 		m_irqstate |= PA7_FLAG;
84 		update_irqstate();
85 	}
86 	m_pa7prev = data;
87 }
88 
89 
90 /*-------------------------------------------------
91     get_timer - return the current timer value
92 -------------------------------------------------*/
93 
get_timer()94 uint8_t riot6532_device::get_timer()
95 {
96 	/* if idle, return 0 */
97 	if (m_timerstate == TIMER_IDLE)
98 	{
99 		return 0;
100 	}
101 
102 	/* if counting, return the number of ticks remaining */
103 	else if (m_timerstate == TIMER_COUNTING)
104 	{
105 		return m_timer->remaining().as_ticks(clock()) >> m_timershift;
106 	}
107 
108 	/* if finishing, return the number of ticks without the shift */
109 	else
110 	{
111 		return m_timer->remaining().as_ticks(clock());
112 	}
113 }
114 
115 
116 
timer_end()117 void riot6532_device::timer_end()
118 {
119 	assert(m_timerstate != TIMER_IDLE);
120 
121 	/* if we finished counting, switch to the finishing state */
122 	if(m_timerstate == TIMER_COUNTING)
123 	{
124 		m_timerstate = TIMER_FINISHING;
125 		m_timer->adjust(attotime::from_ticks(256, clock()));
126 
127 		/* signal timer IRQ as well */
128 		m_irqstate |= TIMER_FLAG;
129 		update_irqstate();
130 	}
131 
132 	/* if we finished finishing, keep spinning */
133 	else if (m_timerstate == TIMER_FINISHING)
134 	{
135 		m_timer->adjust(attotime::from_ticks(256, clock()));
136 	}
137 }
138 
139 
140 
141 /***************************************************************************
142     I/O ACCESS
143 ***************************************************************************/
144 
145 /*-------------------------------------------------
146     riot6532_w - master I/O write access
147 -------------------------------------------------*/
148 
write(offs_t offset,uint8_t data)149 void riot6532_device::write(offs_t offset, uint8_t data)
150 {
151 	reg_w(offset, data);
152 }
153 
reg_w(uint8_t offset,uint8_t data)154 void riot6532_device::reg_w(uint8_t offset, uint8_t data)
155 {
156 	/* if A4 == 1 and A2 == 1, we are writing to the timer */
157 	if ((offset & 0x14) == 0x14)
158 	{
159 		static const uint8_t timershift[4] = { 0, 3, 6, 10 };
160 		attotime curtime = machine().time();
161 		int64_t target;
162 
163 		/* A0-A1 contain the timer divisor */
164 		m_timershift = timershift[offset & 3];
165 
166 		/* A3 contains the timer IRQ enable */
167 		if (offset & 8)
168 			m_irqenable |= TIMER_FLAG;
169 		else
170 			m_irqenable &= ~TIMER_FLAG;
171 
172 		/* writes here clear the timer flag */
173 		if (m_timerstate != TIMER_FINISHING || get_timer() != 0xff)
174 		{
175 			m_irqstate &= ~TIMER_FLAG;
176 		}
177 		update_irqstate();
178 
179 		/* update the timer */
180 		m_timerstate = TIMER_COUNTING;
181 		target = curtime.as_ticks(clock()) + 1 + (data << m_timershift);
182 		m_timer->adjust(attotime::from_ticks(target, clock()) - curtime);
183 	}
184 
185 	/* if A4 == 0 and A2 == 1, we are writing to the edge detect control */
186 	else if ((offset & 0x14) == 0x04)
187 	{
188 		/* A1 contains the A7 IRQ enable */
189 		if (offset & 2)
190 		{
191 			m_irqenable |= PA7_FLAG;
192 		}
193 		else
194 		{
195 			m_irqenable &= ~PA7_FLAG;
196 		}
197 
198 		/* A0 specifies the edge detect direction: 0=negative, 1=positive */
199 		m_pa7dir = (offset & 1) << 7;
200 	}
201 
202 	/* if A4 == anything and A2 == 0, we are writing to the I/O section */
203 	else
204 	{
205 		/* A1 selects the port */
206 		riot6532_port *port = &m_port[BIT(offset, 1)];
207 
208 		/* if A0 == 1, we are writing to the port's DDR */
209 		if (offset & 1)
210 		{
211 			port->m_ddr = data;
212 		}
213 
214 		/* if A0 == 0, we are writing to the port's output */
215 		else
216 		{
217 			port->m_out = data;
218 			(*port->m_out_cb)((offs_t)0, data);
219 		}
220 
221 		/* writes to port A need to update the PA7 state */
222 		if (port == &m_port[0])
223 		{
224 			update_pa7_state();
225 		}
226 	}
227 }
228 
229 
230 /*-------------------------------------------------
231     riot6532_r - master I/O read access
232 -------------------------------------------------*/
233 
read(offs_t offset)234 uint8_t riot6532_device::read(offs_t offset)
235 {
236 	return reg_r(offset, machine().side_effects_disabled());
237 }
238 
reg_r(uint8_t offset,bool debugger_access)239 uint8_t riot6532_device::reg_r(uint8_t offset, bool debugger_access)
240 {
241 	uint8_t val;
242 
243 	/* if A2 == 1 and A0 == 1, we are reading interrupt flags */
244 	if ((offset & 0x05) == 0x05)
245 	{
246 		val = m_irqstate;
247 
248 		if ( ! debugger_access )
249 		{
250 			/* implicitly clears the PA7 flag */
251 			m_irqstate &= ~PA7_FLAG;
252 			update_irqstate();
253 		}
254 	}
255 
256 	/* if A2 == 1 and A0 == 0, we are reading the timer */
257 	else if ((offset & 0x05) == 0x04)
258 	{
259 		val = get_timer();
260 
261 		if ( ! debugger_access )
262 		{
263 			/* A3 contains the timer IRQ enable */
264 			if (offset & 8)
265 			{
266 				m_irqenable |= TIMER_FLAG;
267 			}
268 			else
269 			{
270 				m_irqenable &= ~TIMER_FLAG;
271 			}
272 
273 			/* implicitly clears the timer flag */
274 			if (m_timerstate != TIMER_FINISHING || val != 0xff)
275 			{
276 				m_irqstate &= ~TIMER_FLAG;
277 			}
278 			update_irqstate();
279 		}
280 	}
281 
282 	/* if A2 == 0 and A0 == anything, we are reading from ports */
283 	else
284 	{
285 		/* A1 selects the port */
286 		riot6532_port *port = &m_port[BIT(offset, 1)];
287 
288 		/* if A0 == 1, we are reading the port's DDR */
289 		if (offset & 1)
290 		{
291 			val = port->m_ddr;
292 		}
293 
294 		/* if A0 == 0, we are reading the port as an input */
295 		else
296 		{
297 			/* call the input callback if it exists */
298 			if (!(*port->m_in_cb).isnull())
299 			{
300 				port->m_in = (*port->m_in_cb)(0);
301 
302 				/* changes to port A need to update the PA7 state */
303 				if (port == &m_port[0])
304 				{
305 					if (!debugger_access)
306 					{
307 						update_pa7_state();
308 					}
309 				}
310 			}
311 
312 			/* apply the DDR to the result */
313 			val = apply_ddr(port);
314 		}
315 	}
316 	return val;
317 }
318 
319 
320 /*-------------------------------------------------
321     porta_in_set - set port A input value
322 -------------------------------------------------*/
323 
porta_in_set(uint8_t data,uint8_t mask)324 void riot6532_device::porta_in_set(uint8_t data, uint8_t mask)
325 {
326 	m_port[0].m_in = (m_port[0].m_in & ~mask) | (data & mask);
327 	update_pa7_state();
328 }
329 
330 
331 /*-------------------------------------------------
332     portb_in_set - set port B input value
333 -------------------------------------------------*/
334 
portb_in_set(uint8_t data,uint8_t mask)335 void riot6532_device::portb_in_set(uint8_t data, uint8_t mask)
336 {
337 	m_port[1].m_in = (m_port[1].m_in & ~mask) | (data & mask);
338 }
339 
340 
341 /*-------------------------------------------------
342     porta_in_get - return port A input value
343 -------------------------------------------------*/
344 
porta_in_get()345 uint8_t riot6532_device::porta_in_get()
346 {
347 	return m_port[0].m_in;
348 }
349 
350 
351 /*-------------------------------------------------
352     portb_in_get - return port B input value
353 -------------------------------------------------*/
354 
portb_in_get()355 uint8_t riot6532_device::portb_in_get()
356 {
357 	return m_port[1].m_in;
358 }
359 
360 
361 /*-------------------------------------------------
362     porta_in_get - return port A output value
363 -------------------------------------------------*/
364 
porta_out_get()365 uint8_t riot6532_device::porta_out_get()
366 {
367 	return m_port[0].m_out;
368 }
369 
370 
371 /*-------------------------------------------------
372     portb_in_get - return port B output value
373 -------------------------------------------------*/
374 
portb_out_get()375 uint8_t riot6532_device::portb_out_get()
376 {
377 	return m_port[1].m_out;
378 }
379 
380 
381 //-------------------------------------------------
382 //  paN_w - write Port A lines individually
383 //-------------------------------------------------
384 
WRITE_LINE_MEMBER(riot6532_device::pa0_w)385 WRITE_LINE_MEMBER(riot6532_device::pa0_w) { porta_in_set(state ? 0x01 : 0x00, 0x01); }
WRITE_LINE_MEMBER(riot6532_device::pa1_w)386 WRITE_LINE_MEMBER(riot6532_device::pa1_w) { porta_in_set(state ? 0x02 : 0x00, 0x02); }
WRITE_LINE_MEMBER(riot6532_device::pa2_w)387 WRITE_LINE_MEMBER(riot6532_device::pa2_w) { porta_in_set(state ? 0x04 : 0x00, 0x04); }
WRITE_LINE_MEMBER(riot6532_device::pa3_w)388 WRITE_LINE_MEMBER(riot6532_device::pa3_w) { porta_in_set(state ? 0x08 : 0x00, 0x08); }
WRITE_LINE_MEMBER(riot6532_device::pa4_w)389 WRITE_LINE_MEMBER(riot6532_device::pa4_w) { porta_in_set(state ? 0x10 : 0x00, 0x10); }
WRITE_LINE_MEMBER(riot6532_device::pa5_w)390 WRITE_LINE_MEMBER(riot6532_device::pa5_w) { porta_in_set(state ? 0x20 : 0x00, 0x20); }
WRITE_LINE_MEMBER(riot6532_device::pa6_w)391 WRITE_LINE_MEMBER(riot6532_device::pa6_w) { porta_in_set(state ? 0x40 : 0x00, 0x40); }
WRITE_LINE_MEMBER(riot6532_device::pa7_w)392 WRITE_LINE_MEMBER(riot6532_device::pa7_w) { porta_in_set(state ? 0x80 : 0x00, 0x80); }
393 
394 //-------------------------------------------------
395 //  pbN_w - write Port B lines individually
396 //-------------------------------------------------
397 
WRITE_LINE_MEMBER(riot6532_device::pb0_w)398 WRITE_LINE_MEMBER(riot6532_device::pb0_w) { portb_in_set(state ? 0x01 : 0x00, 0x01); }
WRITE_LINE_MEMBER(riot6532_device::pb1_w)399 WRITE_LINE_MEMBER(riot6532_device::pb1_w) { portb_in_set(state ? 0x02 : 0x00, 0x02); }
WRITE_LINE_MEMBER(riot6532_device::pb2_w)400 WRITE_LINE_MEMBER(riot6532_device::pb2_w) { portb_in_set(state ? 0x04 : 0x00, 0x04); }
WRITE_LINE_MEMBER(riot6532_device::pb3_w)401 WRITE_LINE_MEMBER(riot6532_device::pb3_w) { portb_in_set(state ? 0x08 : 0x00, 0x08); }
WRITE_LINE_MEMBER(riot6532_device::pb4_w)402 WRITE_LINE_MEMBER(riot6532_device::pb4_w) { portb_in_set(state ? 0x10 : 0x00, 0x10); }
WRITE_LINE_MEMBER(riot6532_device::pb5_w)403 WRITE_LINE_MEMBER(riot6532_device::pb5_w) { portb_in_set(state ? 0x20 : 0x00, 0x20); }
WRITE_LINE_MEMBER(riot6532_device::pb6_w)404 WRITE_LINE_MEMBER(riot6532_device::pb6_w) { portb_in_set(state ? 0x40 : 0x00, 0x40); }
WRITE_LINE_MEMBER(riot6532_device::pb7_w)405 WRITE_LINE_MEMBER(riot6532_device::pb7_w) { portb_in_set(state ? 0x80 : 0x00, 0x80); }
406 
407 
408 //**************************************************************************
409 //  LIVE DEVICE
410 //**************************************************************************
411 
412 //-------------------------------------------------
413 //  riot6532_device - constructor
414 //-------------------------------------------------
415 
riot6532_device(const machine_config & mconfig,const char * tag,device_t * owner,uint32_t clock)416 riot6532_device::riot6532_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
417 	: device_t(mconfig, RIOT6532, tag, owner, clock),
418 		m_in_pa_cb(*this),
419 		m_out_pa_cb(*this),
420 		m_in_pb_cb(*this),
421 		m_out_pb_cb(*this),
422 		m_irq_cb(*this),
423 		m_irqstate(0),
424 		m_irqenable(0),
425 		m_irq(CLEAR_LINE),
426 		m_pa7dir(0),
427 		m_pa7prev(0),
428 		m_timershift(0),
429 		m_timerstate(0),
430 		m_timer(nullptr)
431 {
432 	memset(m_port, 0x00, sizeof(m_port));
433 }
434 
435 /*-------------------------------------------------
436     device_start - device-specific startup
437 -------------------------------------------------*/
438 
device_start()439 void riot6532_device::device_start()
440 {
441 	/* resolve callbacks */
442 	m_in_pa_cb.resolve();
443 	m_port[0].m_in_cb = &m_in_pa_cb;
444 	m_out_pa_cb.resolve_safe();
445 	m_port[0].m_out_cb = &m_out_pa_cb;
446 	m_in_pb_cb.resolve();
447 	m_port[1].m_in_cb = &m_in_pb_cb;
448 	m_out_pb_cb.resolve_safe();
449 	m_port[1].m_out_cb = &m_out_pb_cb;
450 	m_irq_cb.resolve_safe();
451 
452 	/* allocate timers */
453 	m_timer = timer_alloc(TIMER_END_CB);
454 
455 	/* register for save states */
456 	save_item(NAME(m_port[0].m_in));
457 	save_item(NAME(m_port[0].m_out));
458 	save_item(NAME(m_port[0].m_ddr));
459 	save_item(NAME(m_port[1].m_in));
460 	save_item(NAME(m_port[1].m_out));
461 	save_item(NAME(m_port[1].m_ddr));
462 
463 	save_item(NAME(m_irqstate));
464 	save_item(NAME(m_irqenable));
465 	save_item(NAME(m_irq));
466 
467 	save_item(NAME(m_pa7dir));
468 	save_item(NAME(m_pa7prev));
469 
470 	save_item(NAME(m_timershift));
471 	save_item(NAME(m_timerstate));
472 }
473 
474 
475 
476 /*-------------------------------------------------
477     device_reset - device-specific reset
478 -------------------------------------------------*/
479 
device_reset()480 void riot6532_device::device_reset()
481 {
482 	/* reset I/O states */
483 	m_port[0].m_in = 0;
484 	m_port[0].m_out = 0;
485 	m_port[0].m_ddr = 0;
486 	m_port[1].m_in = 0;
487 	m_port[1].m_out = 0;
488 	m_port[1].m_ddr = 0;
489 
490 	/* reset IRQ states */
491 	m_irqenable = 0;
492 	m_irqstate = 0;
493 	update_irqstate();
494 
495 	/* reset PA7 states */
496 	m_pa7dir = 0;
497 	m_pa7prev = 0;
498 
499 	/* reset timer states */
500 	m_timershift = 10;
501 	m_timerstate = TIMER_COUNTING;
502 	m_timer->adjust(attotime::from_ticks(256 << m_timershift, clock()));
503 }
504 
device_timer(emu_timer & timer,device_timer_id id,int param,void * ptr)505 void riot6532_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
506 {
507 	switch (id)
508 	{
509 		case TIMER_END_CB:
510 			timer_end();
511 			break;
512 		default:
513 			throw emu_fatalerror("Unknown id in riot6532_device::device_timer");
514 	}
515 }
516