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