1
/**********************************************************************
3
MOS 6526/8520 CIA interface and emulation
5
This function emulates all the functionality MOS6526 or
6
MOS8520 complex interface adapters.
8
**********************************************************************/
13
/***************************************************************************
15
***************************************************************************/
26
#define CIA_TOD0 8 /* 6526: 1/10 seconds 8520: bits 0- 7 */
27
#define CIA_TOD1 9 /* 6526: seconds 8520: bits 8-15 */
28
#define CIA_TOD2 10 /* 6526: minutes 8520: bits 16-23 */
29
#define CIA_TOD3 11 /* 6526: hours 8520: N/A */
35
#define CIA_CRA_START 0x01
36
#define CIA_CRA_PBON 0x02
37
#define CIA_CRA_OUTMODE 0x04
38
#define CIA_CRA_RUNMODE 0x08
39
#define CIA_CRA_LOAD 0x10
40
#define CIA_CRA_INMODE 0x20
41
#define CIA_CRA_SPMODE 0x40
42
#define CIA_CRA_TODIN 0x80
44
//**************************************************************************
45
// DEVICE CONFIGURATION
46
//**************************************************************************
49
//**************************************************************************
51
//**************************************************************************
53
// device type definition
54
const device_type MOS6526R1 = &device_creator<mos6526r1_device>;
55
const device_type MOS6526R2 = &device_creator<mos6526r2_device>;
56
const device_type MOS8520 = &device_creator<mos8520_device>;
58
//-------------------------------------------------
59
// mos6526_device - constructor
60
//-------------------------------------------------
62
mos6526_device::mos6526_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock)
63
: device_t(mconfig, type, name, tag, owner, clock)
67
mos6526r1_device::mos6526r1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
68
: mos6526_device(mconfig, MOS6526R1, "MOS6526r1", tag, owner, clock) { }
70
mos6526r2_device::mos6526r2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
71
: mos6526_device(mconfig, MOS6526R2, "MOS6526r2", tag, owner, clock) { }
73
mos8520_device::mos8520_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
74
: mos6526_device(mconfig, MOS8520, "MOS8520", tag, owner, clock) { }
77
//-------------------------------------------------
78
// device_reset - device-specific reset
79
//-------------------------------------------------
81
void mos6526_device::device_reset()
83
/* clear things out */
84
m_port[0].m_latch = 0x00;
85
m_port[0].m_in = 0x00;
86
m_port[0].m_out = 0x00;
87
m_port[0].m_mask_value = 0xff;
88
m_port[1].m_latch = 0x00;
89
m_port[1].m_in = 0x00;
90
m_port[1].m_out = 0x00;
91
m_port[1].m_mask_value = 0xff;
103
/* initialize data direction registers */
104
m_port[0].m_ddr = !strcmp(tag(), "cia_0") ? 0x03 : 0xff;
105
m_port[1].m_ddr = !strcmp(tag(), "cia_0") ? 0x00 : 0xff;
107
/* TOD running by default */
108
m_tod_running = TRUE;
110
/* initialize timers */
111
for(int t = 0; t < 2; t++)
113
cia_timer *timer = &m_timer[t];
115
timer->m_clock = clock();
116
timer->m_latch = 0xffff;
117
timer->m_count = 0x0000;
118
timer->m_mode = 0x00;
123
//-------------------------------------------------
124
// device_config_complete - perform any
125
// operations now that the configuration is
127
//-------------------------------------------------
129
void mos6526_device::device_config_complete()
131
// inherit a copy of the static data
132
const mos6526_interface *intf = reinterpret_cast<const mos6526_interface *>(static_config());
134
*static_cast<mos6526_interface *>(this) = *intf;
136
// or initialize to defaults if none provided
140
memset(&m_out_irq_cb, 0, sizeof(m_out_irq_cb));
141
memset(&m_out_pc_cb, 0, sizeof(m_out_pc_cb));
142
memset(&m_out_cnt_cb, 0, sizeof(m_out_cnt_cb));
143
memset(&m_out_sp_cb, 0, sizeof(m_out_sp_cb));
144
memset(&m_in_pa_cb, 0, sizeof(m_in_pa_cb));
145
memset(&m_out_pa_cb, 0, sizeof(m_out_pa_cb));
146
memset(&m_in_pb_cb, 0, sizeof(m_in_pb_cb));
147
memset(&m_out_pb_cb, 0, sizeof(m_out_pb_cb));
152
//-------------------------------------------------
153
// device_start - device-specific startup
154
//-------------------------------------------------
156
void mos6526_device::device_start()
158
/* clear out CIA structure, and copy the interface */
159
m_out_irq_func.resolve(m_out_irq_cb, *this);
160
m_out_pc_func.resolve(m_out_pc_cb, *this);
161
m_out_cnt_func.resolve(m_out_cnt_cb, *this);
162
m_out_sp_func.resolve(m_out_sp_cb, *this);
166
m_port[0].m_read.resolve(m_in_pa_cb, *this);
167
m_port[0].m_write.resolve(m_out_pa_cb, *this);
168
m_port[1].m_read.resolve(m_in_pb_cb, *this);
169
m_port[1].m_write.resolve(m_out_pb_cb, *this);
171
for (int p = 0; p < (sizeof(m_port) / sizeof(m_port[0])); p++)
173
m_port[p].m_mask_value = 0xff;
177
for (int t = 0; t < (sizeof(m_timer) / sizeof(m_timer[0])); t++)
179
cia_timer *timer = &m_timer[t];
180
timer->m_timer = machine().scheduler().timer_alloc(FUNC(timer_proc), (void*)this);
182
timer->m_irq = 0x01 << t;
185
/* setup TOD timer, if appropriate */
186
if (m_tod_clock != 0)
188
machine().scheduler().timer_pulse(attotime::from_hz(m_tod_clock), FUNC(clock_tod_callback), 0, (void *)this);
191
/* state save support */
192
save_item(NAME(m_port[0].m_ddr));
193
save_item(NAME(m_port[0].m_latch));
194
save_item(NAME(m_port[0].m_in));
195
save_item(NAME(m_port[0].m_out));
196
save_item(NAME(m_port[0].m_mask_value));
197
save_item(NAME(m_port[1].m_ddr));
198
save_item(NAME(m_port[1].m_latch));
199
save_item(NAME(m_port[1].m_in));
200
save_item(NAME(m_port[1].m_out));
201
save_item(NAME(m_port[1].m_mask_value));
202
save_item(NAME(m_timer[0].m_latch));
203
save_item(NAME(m_timer[0].m_count));
204
save_item(NAME(m_timer[0].m_mode));
205
save_item(NAME(m_timer[0].m_irq));
206
save_item(NAME(m_timer[1].m_latch));
207
save_item(NAME(m_timer[1].m_count));
208
save_item(NAME(m_timer[1].m_mode));
209
save_item(NAME(m_timer[1].m_irq));
210
save_item(NAME(m_tod));
211
save_item(NAME(m_tod_latch));
212
save_item(NAME(m_tod_latched));
213
save_item(NAME(m_tod_running));
214
save_item(NAME(m_alarm));
215
save_item(NAME(m_icr));
216
save_item(NAME(m_ics));
217
save_item(NAME(m_irq));
218
save_item(NAME(m_flag));
219
save_item(NAME(m_loaded));
220
save_item(NAME(m_sdr));
221
save_item(NAME(m_sp));
222
save_item(NAME(m_cnt));
223
save_item(NAME(m_shift));
224
save_item(NAME(m_serial));
228
/*-------------------------------------------------
230
-------------------------------------------------*/
232
void mos6526_device::set_port_mask_value(int port, int data)
234
m_port[port].m_mask_value = data;
237
/*-------------------------------------------------
238
update_pc - pulse /pc output
239
-------------------------------------------------*/
241
void mos6526_device::update_pc()
243
/* this should really be one cycle long */
248
/*-------------------------------------------------
250
-------------------------------------------------*/
252
void mos6526_device::update_interrupts()
256
/* always update the high bit of ICS */
266
/* based on what is enabled, set/clear the IRQ via the custom chip */
267
new_irq = (m_ics & m_icr) ? 1 : 0;
268
if (m_irq != new_irq)
271
m_out_irq_func(m_irq);
276
/*-------------------------------------------------
278
-------------------------------------------------*/
280
void mos6526_device::timer_bump(int timer)
282
m_timer[timer].update(timer, -1);
284
if (m_timer[timer].m_count == 0x00)
286
timer_underflow(timer);
290
m_timer[timer].update(timer, m_timer[timer].m_count - 1);
294
/*-------------------------------------------------
296
-------------------------------------------------*/
298
void mos6526_device::timer_underflow(int timer)
300
assert((timer == 0) || (timer == 1));
302
/* set the status and update interrupts */
303
m_ics |= m_timer[timer].m_irq;
306
/* if one-shot mode, turn it off */
307
if (m_timer[timer].m_mode & 0x08)
309
m_timer[timer].m_mode &= 0xfe;
312
/* reload the timer */
313
m_timer[timer].update(timer, m_timer[timer].m_latch);
315
/* timer A has some interesting properties */
318
/* such as cascading to timer B */
319
if ((m_timer[1].m_mode & 0x41) == 0x41)
321
if (m_cnt || !(m_timer[1].m_mode & 0x20))
327
/* also the serial line */
328
if ((m_timer[timer].m_irq == 0x01) && (m_timer[timer].m_mode & CIA_CRA_SPMODE))
330
if (m_loaded || m_shift)
337
/* load shift register */
343
m_sp = BIT(m_serial, 7);
348
m_out_cnt_func(m_cnt);
356
/* signal interrupt */
365
m_out_cnt_func(m_cnt);
377
/*-------------------------------------------------
378
TIMER_CALLBACK( cia_timer_proc )
379
-------------------------------------------------*/
381
TIMER_CALLBACK( mos6526_device::timer_proc )
383
mos6526_device *cia = reinterpret_cast<mos6526_device *>(ptr);
385
cia->timer_underflow(param);
388
/*-------------------------------------------------
390
-------------------------------------------------*/
392
static UINT8 bcd_increment(UINT8 value)
395
if ((value & 0x0f) >= 0x0a)
396
value += 0x10 - 0x0a;
400
/*-------------------------------------------------
402
-------------------------------------------------*/
404
void mos6526_device::increment()
406
/* break down TOD value into components */
407
UINT8 subsecond = (UINT8) (m_tod >> 0);
408
UINT8 second = (UINT8) (m_tod >> 8);
409
UINT8 minute = (UINT8) (m_tod >> 16);
410
UINT8 hour = (UINT8) (m_tod >> 24);
412
subsecond = bcd_increment(subsecond);
413
if (subsecond >= 0x10)
416
second = bcd_increment(second);
417
if (second >= ((m_timer[0].m_mode & 0x80) ? 0x50 : 0x60))
420
minute = bcd_increment(minute);
426
else if (hour == 0x89)
428
else if (hour == 0x11)
430
else if (hour == 0x09)
438
/* update the TOD with new value */
439
m_tod = (((UINT32) subsecond) << 0)
440
| (((UINT32) second) << 8)
441
| (((UINT32) minute) << 16)
442
| (((UINT32) hour) << 24);
445
/*-------------------------------------------------
446
cia_clock_tod - Update TOD on CIA A
447
-------------------------------------------------*/
449
void mos6526_device::clock_tod()
453
if ((type() == MOS6526R1) || (type() == MOS6526R2))
455
/* The 6526 split the value into hours, minutes, seconds and
459
else if (type() == MOS8520)
461
/* the 8520 has a straight 24-bit counter */
466
if (m_tod == m_alarm)
475
/*-------------------------------------------------
477
-------------------------------------------------*/
479
TIMER_CALLBACK( mos6526_device::clock_tod_callback )
481
mos6526_device *cia = reinterpret_cast<mos6526_device *>(ptr);
486
/*-------------------------------------------------
488
-------------------------------------------------*/
490
void mos6526_device::cnt_w(UINT8 state)
492
/* is this a rising edge? */
495
if (m_timer[0].m_mode & CIA_CRA_START)
497
/* does timer #0 bump on CNT? */
498
if (m_timer[0].m_mode & CIA_CRA_INMODE)
504
/* if the serial port is set to input, the CNT will shift the port */
505
if (!(m_timer[0].m_mode & CIA_CRA_SPMODE))
525
/* does timer #1 bump on CNT? */
526
if ((m_timer[1].m_mode & 0x61) == 0x21)
535
void mos6526_device::flag_w(UINT8 state)
538
if (m_flag && !state)
547
/*-------------------------------------------------
549
-------------------------------------------------*/
551
UINT8 mos6526_device::reg_r(UINT8 offset)
564
port = &m_port[offset & 1];
565
data = port->m_read(0);
566
data = ((data & ~port->m_ddr) | (port->m_latch & port->m_ddr)) & port->m_mask_value;
570
if (offset == CIA_PRB)
572
/* timer #0 can change PB6 */
573
if (m_timer[0].m_mode & 0x02)
575
m_timer[0].update(0, -1);
576
if (m_timer[0].m_count != 0)
586
/* timer #1 can change PB7 */
587
if (m_timer[1].m_mode & 0x02)
589
m_timer[1].update(1, -1);
590
if (m_timer[1].m_count != 0)
600
/* pulse /PC following the read */
605
/* port A/B direction */
608
port = &m_port[offset & 1];
612
/* timer A/B low byte */
615
timer = &m_timer[(offset >> 1) & 1];
616
data = timer->get_count() >> 0;
619
/* timer A/B high byte */
622
timer = &m_timer[(offset >> 1) & 1];
623
data = timer->get_count() >> 8;
631
if (type() == MOS8520)
633
if (offset == CIA_TOD2)
636
m_tod_latched = TRUE;
641
if (offset == CIA_TOD3)
644
m_tod_latched = TRUE;
647
if (offset == CIA_TOD0)
649
m_tod_latched = FALSE;
654
data = m_tod_latch >> ((offset - CIA_TOD0) * 8);
658
data = m_tod >> ((offset - CIA_TOD0) * 8);
662
/* serial data ready */
667
/* interrupt status/clear */
670
m_ics = 0; /* clear on read */
677
timer = &m_timer[offset & 1];
678
data = timer->m_mode;
685
/*-------------------------------------------------
687
-------------------------------------------------*/
689
void mos6526_device::reg_w(UINT8 offset, UINT8 data)
702
port = &m_port[offset & 1];
703
port->m_latch = data;
704
port->m_out = (data & port->m_ddr) | (port->m_in & ~port->m_ddr);
705
port->m_write(0, port->m_out);
707
/* pulse /PC following the write */
708
if (offset == CIA_PRB)
715
/* port A/B direction */
718
port = &m_port[offset & 1];
722
/* timer A/B latch low */
725
timer = &m_timer[(offset >> 1) & 1];
726
timer->m_latch = (timer->m_latch & 0xff00) | (data << 0);
729
/* timer A latch high */
732
timer = &m_timer[(offset >> 1) & 1];
733
timer->m_latch = (timer->m_latch & 0x00ff) | (data << 8);
735
/* if the timer is one-shot, then force a start on it */
736
if (timer->m_mode & 0x08)
739
timer->update((offset >> 1) & 1, timer->m_latch);
743
/* if the timer is off, update the count */
744
if (!(timer->m_mode & 0x01))
746
timer->update((offset >> 1) & 1, timer->m_latch);
751
/* time of day latches */
756
shift = 8 * ((offset - CIA_TOD0));
758
/* alarm setting mode? */
759
if (m_timer[1].m_mode & 0x80)
761
m_alarm = (m_alarm & ~(0xff << shift)) | (data << shift);
763
/* counter setting mode */
766
m_tod = (m_tod & ~(0xff << shift)) | (data << shift);
769
if (type() == MOS8520)
771
if (offset == CIA_TOD2)
773
m_tod_running = FALSE;
778
if (offset == CIA_TOD3)
780
m_tod_running = FALSE;
783
if (offset == CIA_TOD0)
785
m_tod_running = TRUE;
789
/* serial data ready */
792
if (m_timer[0].m_mode & 0x40)
798
/* interrupt control register */
802
m_icr |= data & 0x7f;
806
m_icr &= ~(data & 0x7f);
811
/* timer A/B modes */
814
timer = &m_timer[offset & 1];
815
timer->m_mode = data & 0xef;
820
timer->update(offset & 1, timer->m_latch);
824
timer->update(offset & 1, -1);
830
/*-------------------------------------------------
832
-------------------------------------------------*/
834
static int is_timer_active(emu_timer *timer)
836
attotime t = timer->expire();
837
return (t != attotime::never);
840
/*-------------------------------------------------
841
update - updates the count and emu_timer for
843
-------------------------------------------------*/
845
void mos6526_device::cia_timer::update(int which, INT32 new_count)
847
/* sanity check arguments */
848
assert((new_count >= -1) && (new_count <= 0xffff));
850
/* update the timer count, if necessary */
851
if ((new_count == -1) && is_timer_active(m_timer))
853
UINT16 current_count = (m_timer->elapsed() * m_clock).as_double();
854
m_count = m_count - MIN(m_count, current_count);
857
/* set the timer if we are instructed to */
863
/* now update the MAME timer */
864
if ((m_mode & 0x01) && ((m_mode & (which ? 0x60 : 0x20)) == 0x00))
866
/* timer is on and is connected to clock */
867
attotime period = attotime::from_hz(m_clock) * (m_count ? m_count : 0x10000);
868
m_timer->adjust(period, which);
872
/* timer is off or not connected to clock */
873
m_timer->adjust(attotime::never, which);
877
/*-------------------------------------------------
878
get_count - get the count for a given CIA
880
-------------------------------------------------*/
882
UINT16 mos6526_device::cia_timer::get_count()
886
if (is_timer_active(m_timer))
888
UINT16 current_count = (m_timer->elapsed() * m_clock).as_double();
889
count = m_count - MIN(m_count, current_count);
899
/***************************************************************************
901
***************************************************************************/
903
void cia_set_port_mask_value(device_t *device, int port, int data) { downcast<mos6526_device *>(device)->set_port_mask_value(port, data); }
905
READ8_DEVICE_HANDLER( mos6526_r ) { return downcast<mos6526_device *>(device)->reg_r(offset); }
906
WRITE8_DEVICE_HANDLER( mos6526_w ) { downcast<mos6526_device *>(device)->reg_w(offset, data); }
908
READ8_DEVICE_HANDLER( mos6526_pa_r ) { return downcast<mos6526_device *>(device)->pa_r(offset); }
909
READ8_DEVICE_HANDLER( mos6526_pb_r ) { return downcast<mos6526_device *>(device)->pb_r(offset); }
911
READ_LINE_DEVICE_HANDLER( mos6526_irq_r ) { return downcast<mos6526_device *>(device)->irq_r(); }
913
WRITE_LINE_DEVICE_HANDLER( mos6526_tod_w ) { downcast<mos6526_device *>(device)->tod_w(state); }
915
READ_LINE_DEVICE_HANDLER( mos6526_cnt_r ) { return downcast<mos6526_device *>(device)->cnt_r(); }
916
WRITE_LINE_DEVICE_HANDLER( mos6526_cnt_w ) { downcast<mos6526_device *>(device)->cnt_w(state); }
918
READ_LINE_DEVICE_HANDLER( mos6526_sp_r ) { return downcast<mos6526_device *>(device)->sp_r(); }
919
WRITE_LINE_DEVICE_HANDLER( mos6526_sp_w ) { downcast<mos6526_device *>(device)->sp_w(state); }
921
WRITE_LINE_DEVICE_HANDLER( mos6526_flag_w ) { downcast<mos6526_device *>(device)->flag_w(state); }