mirror of
https://github.com/holub/mame
synced 2025-06-04 11:56:28 +03:00
989 lines
24 KiB
C
989 lines
24 KiB
C
/**********************************************************************
|
|
|
|
MOS 6526/8520 CIA interface and emulation
|
|
|
|
This function emulates all the functionality MOS6526 or
|
|
MOS8520 complex interface adapters.
|
|
|
|
**********************************************************************/
|
|
|
|
#include "driver.h"
|
|
#include "6526cia.h"
|
|
|
|
|
|
/***************************************************************************
|
|
CONSTANTS
|
|
***************************************************************************/
|
|
|
|
/* CIA registers */
|
|
#define CIA_PRA 0
|
|
#define CIA_PRB 1
|
|
#define CIA_DDRA 2
|
|
#define CIA_DDRB 3
|
|
#define CIA_TALO 4
|
|
#define CIA_TAHI 5
|
|
#define CIA_TBLO 6
|
|
#define CIA_TBHI 7
|
|
#define CIA_TOD0 8 /* 6526: 1/10 seconds 8520: bits 0- 7 */
|
|
#define CIA_TOD1 9 /* 6526: seconds 8520: bits 8-15 */
|
|
#define CIA_TOD2 10 /* 6526: minutes 8520: bits 16-23 */
|
|
#define CIA_TOD3 11 /* 6526: hours 8520: N/A */
|
|
#define CIA_SDR 12
|
|
#define CIA_ICR 13
|
|
#define CIA_CRA 14
|
|
#define CIA_CRB 15
|
|
|
|
|
|
/***************************************************************************
|
|
TYPE DEFINITIONS
|
|
***************************************************************************/
|
|
|
|
typedef struct _cia_timer cia_timer;
|
|
typedef struct _cia_port cia_port;
|
|
typedef struct _cia_state cia_state;
|
|
|
|
struct _cia_timer
|
|
{
|
|
UINT16 latch;
|
|
UINT16 count;
|
|
UINT8 mode;
|
|
UINT8 irq;
|
|
emu_timer *timer;
|
|
cia_state * cia;
|
|
};
|
|
|
|
struct _cia_port
|
|
{
|
|
UINT8 ddr;
|
|
UINT8 latch;
|
|
UINT8 in;
|
|
UINT8 out;
|
|
devcb_resolved_read8 read;
|
|
devcb_resolved_write8 write;
|
|
UINT8 mask_value; /* in READ operation the value can be forced by a extern electric circuit */
|
|
};
|
|
|
|
struct _cia_state
|
|
{
|
|
const device_config *device;
|
|
devcb_resolved_write_line irq_func;
|
|
devcb_resolved_write_line pc_func;
|
|
|
|
cia_port port[2];
|
|
cia_timer timer[2];
|
|
|
|
/* Time Of the Day clock (TOD) */
|
|
UINT32 tod;
|
|
UINT32 tod_latch;
|
|
UINT8 tod_latched;
|
|
UINT8 tod_running;
|
|
UINT32 alarm;
|
|
|
|
/* Interrupts */
|
|
UINT8 icr;
|
|
UINT8 ics;
|
|
UINT8 irq;
|
|
int flag;
|
|
|
|
/* Serial */
|
|
UINT8 loaded;
|
|
UINT8 sdr;
|
|
UINT8 sp;
|
|
UINT8 cnt;
|
|
UINT8 shift;
|
|
UINT8 serial;
|
|
};
|
|
|
|
|
|
/***************************************************************************
|
|
PROTOTYPES
|
|
***************************************************************************/
|
|
|
|
static TIMER_CALLBACK( cia_timer_proc );
|
|
static void cia_timer_underflow(const device_config *device, int timer);
|
|
static TIMER_CALLBACK( cia_clock_tod_callback );
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
INLINE FUNCTIONS
|
|
***************************************************************************/
|
|
|
|
INLINE cia_state *get_token(const device_config *device)
|
|
{
|
|
assert(device != NULL);
|
|
assert((device->type == CIA6526R1) || (device->type == CIA6526R2) || (device->type == CIA8520));
|
|
return (cia_state *) device->token;
|
|
}
|
|
|
|
|
|
INLINE const cia6526_interface *get_interface(const device_config *device)
|
|
{
|
|
assert(device != NULL);
|
|
assert((device->type == CIA6526R1) || (device->type == CIA6526R2) || (device->type == CIA8520));
|
|
return (cia6526_interface *) device->static_config;
|
|
}
|
|
|
|
|
|
/***************************************************************************
|
|
IMPLEMENTATION
|
|
***************************************************************************/
|
|
|
|
/*-------------------------------------------------
|
|
DEVICE_START( cia )
|
|
-------------------------------------------------*/
|
|
|
|
static DEVICE_START( cia )
|
|
{
|
|
int t, p;
|
|
cia_state *cia = get_token(device);
|
|
const cia6526_interface *intf = get_interface(device);
|
|
|
|
/* clear out CIA structure, and copy the interface */
|
|
memset(cia, 0, sizeof(*cia));
|
|
cia->device = device;
|
|
devcb_resolve_write_line(&cia->irq_func, &intf->irq_func, device);
|
|
devcb_resolve_write_line(&cia->pc_func, &intf->pc_func, device);
|
|
cia->flag = 1;
|
|
|
|
/* setup ports */
|
|
for (p = 0; p < (sizeof(cia->port) / sizeof(cia->port[0])); p++)
|
|
{
|
|
devcb_resolve_read8(&cia->port[p].read, &intf->port[p].read, device);
|
|
devcb_resolve_write8(&cia->port[p].write, &intf->port[p].write, device);
|
|
cia->port[p].mask_value = 0xff;
|
|
}
|
|
|
|
/* setup timers */
|
|
for (t = 0; t < (sizeof(cia->timer) / sizeof(cia->timer[0])); t++)
|
|
{
|
|
cia_timer *timer = &cia->timer[t];
|
|
timer->timer = timer_alloc(device->machine, cia_timer_proc, timer);
|
|
timer->cia = cia;
|
|
timer->irq = 0x01 << t;
|
|
}
|
|
|
|
/* setup TOD timer, if appropriate */
|
|
if (intf->tod_clock != 0)
|
|
timer_pulse(device->machine, ATTOTIME_IN_HZ(intf->tod_clock), (void *) device, 0, cia_clock_tod_callback);
|
|
|
|
/* state save support */
|
|
state_save_register_device_item(device, 0, cia->port[0].ddr);
|
|
state_save_register_device_item(device, 0, cia->port[0].latch);
|
|
state_save_register_device_item(device, 0, cia->port[0].in);
|
|
state_save_register_device_item(device, 0, cia->port[0].out);
|
|
state_save_register_device_item(device, 0, cia->port[0].mask_value);
|
|
state_save_register_device_item(device, 0, cia->port[1].ddr);
|
|
state_save_register_device_item(device, 0, cia->port[1].latch);
|
|
state_save_register_device_item(device, 0, cia->port[1].in);
|
|
state_save_register_device_item(device, 0, cia->port[1].out);
|
|
state_save_register_device_item(device, 0, cia->port[1].mask_value);
|
|
state_save_register_device_item(device, 0, cia->timer[0].latch);
|
|
state_save_register_device_item(device, 0, cia->timer[0].count);
|
|
state_save_register_device_item(device, 0, cia->timer[0].mode);
|
|
state_save_register_device_item(device, 0, cia->timer[0].irq);
|
|
state_save_register_device_item(device, 0, cia->timer[1].latch);
|
|
state_save_register_device_item(device, 0, cia->timer[1].count);
|
|
state_save_register_device_item(device, 0, cia->timer[1].mode);
|
|
state_save_register_device_item(device, 0, cia->timer[1].irq);
|
|
state_save_register_device_item(device, 0, cia->tod);
|
|
state_save_register_device_item(device, 0, cia->tod_latch);
|
|
state_save_register_device_item(device, 0, cia->tod_latched);
|
|
state_save_register_device_item(device, 0, cia->tod_running);
|
|
state_save_register_device_item(device, 0, cia->alarm);
|
|
state_save_register_device_item(device, 0, cia->icr);
|
|
state_save_register_device_item(device, 0, cia->ics);
|
|
state_save_register_device_item(device, 0, cia->irq);
|
|
state_save_register_device_item(device, 0, cia->flag);
|
|
state_save_register_device_item(device, 0, cia->loaded);
|
|
state_save_register_device_item(device, 0, cia->sdr);
|
|
state_save_register_device_item(device, 0, cia->sp);
|
|
state_save_register_device_item(device, 0, cia->cnt);
|
|
state_save_register_device_item(device, 0, cia->shift);
|
|
state_save_register_device_item(device, 0, cia->serial);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
cia_set_port_mask_value
|
|
-------------------------------------------------*/
|
|
|
|
void cia_set_port_mask_value(const device_config *device, int port, int data)
|
|
{
|
|
cia_state *cia = get_token(device);
|
|
cia->port[port].mask_value = data;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
DEVICE_RESET( cia )
|
|
-------------------------------------------------*/
|
|
|
|
static DEVICE_RESET( cia )
|
|
{
|
|
int t;
|
|
cia_state *cia = get_token(device);
|
|
|
|
/* clear things out */
|
|
cia->port[0].latch = 0x00;
|
|
cia->port[0].in = 0x00;
|
|
cia->port[0].out = 0x00;
|
|
cia->port[0].mask_value = 0xff;
|
|
cia->port[1].latch = 0x00;
|
|
cia->port[1].in = 0x00;
|
|
cia->port[1].out = 0x00;
|
|
cia->port[1].mask_value = 0xff;
|
|
cia->tod = 0;
|
|
cia->tod_latch = 0;
|
|
cia->alarm = 0;
|
|
cia->icr = 0x00;
|
|
cia->ics = 0x00;
|
|
cia->irq = 0;
|
|
|
|
/* initialize data direction registers */
|
|
cia->port[0].ddr = !strcmp(device->tag, "cia_0") ? 0x03 : 0xff;
|
|
cia->port[1].ddr = !strcmp(device->tag, "cia_0") ? 0x00 : 0xff;
|
|
|
|
/* TOD running by default */
|
|
cia->tod_running = TRUE;
|
|
|
|
/* initialize timers */
|
|
for (t = 0; t < 2; t++)
|
|
{
|
|
cia_timer *timer = &cia->timer[t];
|
|
|
|
timer->latch = 0xffff;
|
|
timer->count = 0x0000;
|
|
timer->mode = 0x00;
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
DEVICE_VALIDITY_CHECK( cia )
|
|
-------------------------------------------------*/
|
|
|
|
static DEVICE_VALIDITY_CHECK( cia )
|
|
{
|
|
int error = FALSE;
|
|
|
|
if (device->clock <= 0)
|
|
{
|
|
mame_printf_error("%s: %s has a cia with an invalid clock\n", driver->source_file, driver->name);
|
|
error = TRUE;
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
cia_update_pc - pulse /pc output
|
|
-------------------------------------------------*/
|
|
|
|
static void cia_update_pc(const device_config *device)
|
|
{
|
|
cia_state *cia = get_token(device);
|
|
|
|
/* this should really be one cycle long */
|
|
devcb_call_write_line(&cia->pc_func, 0);
|
|
devcb_call_write_line(&cia->pc_func, 1);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
cia_update_interrupts
|
|
-------------------------------------------------*/
|
|
|
|
static void cia_update_interrupts(const device_config *device)
|
|
{
|
|
UINT8 new_irq;
|
|
cia_state *cia = get_token(device);
|
|
|
|
/* always update the high bit of ICS */
|
|
if (cia->ics & 0x7f)
|
|
cia->ics |= 0x80;
|
|
else
|
|
cia->ics &= ~0x80;
|
|
|
|
/* based on what is enabled, set/clear the IRQ via the custom chip */
|
|
new_irq = (cia->ics & cia->icr) ? 1 : 0;
|
|
if (cia->irq != new_irq)
|
|
{
|
|
cia->irq = new_irq;
|
|
devcb_call_write_line(&cia->irq_func, cia->irq);
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
is_timer_active
|
|
-------------------------------------------------*/
|
|
|
|
static int is_timer_active(emu_timer *timer)
|
|
{
|
|
attotime t = timer_firetime(timer);
|
|
return attotime_compare(t, attotime_never) != 0;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
cia_timer_update - updates the count and
|
|
emu_timer for a given CIA timer
|
|
-------------------------------------------------*/
|
|
|
|
static void cia_timer_update(cia_timer *timer, INT32 new_count)
|
|
{
|
|
int which = timer - timer->cia->timer;
|
|
|
|
/* sanity check arguments */
|
|
assert((new_count >= -1) && (new_count <= 0xffff));
|
|
|
|
/* update the timer count, if necessary */
|
|
if ((new_count == -1) && is_timer_active(timer->timer))
|
|
{
|
|
UINT16 current_count = attotime_to_double(attotime_mul(timer_timeelapsed(timer->timer), timer->cia->device->clock));
|
|
timer->count = timer->count - MIN(timer->count, current_count);
|
|
}
|
|
|
|
/* set the timer if we are instructed to */
|
|
if (new_count != -1)
|
|
timer->count = new_count;
|
|
|
|
/* now update the MAME timer */
|
|
if ((timer->mode & 0x01) && ((timer->mode & (which ? 0x60 : 0x20)) == 0x00))
|
|
{
|
|
/* timer is on and is connected to clock */
|
|
attotime period = attotime_mul(ATTOTIME_IN_HZ(timer->cia->device->clock), (timer->count ? timer->count : 0x10000));
|
|
timer_adjust_oneshot(timer->timer, period, 0);
|
|
}
|
|
else
|
|
{
|
|
/* timer is off or not connected to clock */
|
|
timer_adjust_oneshot(timer->timer, attotime_never, 0);
|
|
}
|
|
}
|
|
|
|
/*-------------------------------------------------
|
|
cia_get_timer - get the count
|
|
for a given CIA timer
|
|
-------------------------------------------------*/
|
|
|
|
static UINT16 cia_get_timer(cia_timer *timer)
|
|
{
|
|
UINT16 count;
|
|
|
|
if (is_timer_active(timer->timer))
|
|
{
|
|
UINT16 current_count = attotime_to_double(attotime_mul(timer_timeelapsed(timer->timer), timer->cia->device->clock));
|
|
count = timer->count - MIN(timer->count, current_count);
|
|
}
|
|
else
|
|
count = timer->count;
|
|
|
|
return count;
|
|
}
|
|
|
|
/*-------------------------------------------------
|
|
cia_timer_bump
|
|
-------------------------------------------------*/
|
|
|
|
static void cia_timer_bump(const device_config *device, int timer)
|
|
{
|
|
cia_state *cia = get_token(device);
|
|
|
|
cia_timer_update(&cia->timer[timer], -1);
|
|
|
|
if (cia->timer[timer].count == 0x00)
|
|
cia_timer_underflow(device, timer);
|
|
else
|
|
cia_timer_update(&cia->timer[timer], cia->timer[timer].count - 1);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
cia_timer_underflow
|
|
-------------------------------------------------*/
|
|
|
|
static void cia_timer_underflow(const device_config *device, int timer)
|
|
{
|
|
cia_state *cia = get_token(device);
|
|
|
|
assert((timer == 0) || (timer == 1));
|
|
|
|
/* set the status and update interrupts */
|
|
cia->ics |= cia->timer[timer].irq;
|
|
cia_update_interrupts(device);
|
|
|
|
/* if one-shot mode, turn it off */
|
|
if (cia->timer[timer].mode & 0x08)
|
|
cia->timer[timer].mode &= 0xfe;
|
|
|
|
/* reload the timer */
|
|
cia_timer_update(&cia->timer[timer], cia->timer[timer].latch);
|
|
|
|
/* timer A has some interesting properties */
|
|
if (timer == 0)
|
|
{
|
|
/* such as cascading to timer B */
|
|
if ((cia->timer[1].mode & 0x41) == 0x41)
|
|
{
|
|
if (cia->cnt || !(cia->timer[1].mode & 0x20))
|
|
cia_timer_bump(device, 1);
|
|
}
|
|
|
|
/* also the serial line */
|
|
if ((cia->timer[timer].irq == 0x01) && (cia->timer[timer].mode & 0x40))
|
|
{
|
|
if (cia->shift || cia->loaded)
|
|
{
|
|
if (cia->cnt)
|
|
{
|
|
if (cia->shift == 0)
|
|
{
|
|
cia->loaded = 0;
|
|
cia->serial = cia->sdr;
|
|
}
|
|
cia->sp = (cia->serial & 0x80) ? 1 : 0;
|
|
cia->shift++;
|
|
cia->serial <<= 1;
|
|
cia->cnt = 0;
|
|
}
|
|
else
|
|
{
|
|
cia->cnt = 1;
|
|
if (cia->shift == 8)
|
|
{
|
|
cia->ics |= 0x08;
|
|
cia_update_interrupts(device);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
TIMER_CALLBACK( cia_timer_proc )
|
|
-------------------------------------------------*/
|
|
|
|
static TIMER_CALLBACK( cia_timer_proc )
|
|
{
|
|
cia_timer *timer = (cia_timer *)ptr;
|
|
cia_state *cia = timer->cia;
|
|
|
|
cia_timer_underflow(cia->device, timer - cia->timer);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
bcd_increment
|
|
-------------------------------------------------*/
|
|
|
|
static UINT8 bcd_increment(UINT8 value)
|
|
{
|
|
value++;
|
|
if ((value & 0x0f) >= 0x0a)
|
|
value += 0x10 - 0x0a;
|
|
return value;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
cia6526_increment
|
|
-------------------------------------------------*/
|
|
|
|
static void cia6526_increment(cia_state *cia)
|
|
{
|
|
/* break down TOD value into components */
|
|
UINT8 subsecond = (UINT8) (cia->tod >> 0);
|
|
UINT8 second = (UINT8) (cia->tod >> 8);
|
|
UINT8 minute = (UINT8) (cia->tod >> 16);
|
|
UINT8 hour = (UINT8) (cia->tod >> 24);
|
|
|
|
subsecond = bcd_increment(subsecond);
|
|
if (subsecond >= 0x10)
|
|
{
|
|
subsecond = 0x00;
|
|
second = bcd_increment(second);
|
|
if (second >= ((cia->timer[0].mode & 0x80) ? 0x50 : 0x60))
|
|
{
|
|
second = 0x00;
|
|
minute = bcd_increment(minute);
|
|
if (minute >= 0x60)
|
|
{
|
|
minute = 0x00;
|
|
if (hour == 0x91)
|
|
hour = 0x00;
|
|
else if (hour == 0x89)
|
|
hour = 0x90;
|
|
else if (hour == 0x11)
|
|
hour = 0x80;
|
|
else if (hour == 0x09)
|
|
hour = 0x10;
|
|
else
|
|
hour++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* update the TOD with new value */
|
|
cia->tod = (((UINT32) subsecond) << 0)
|
|
| (((UINT32) second) << 8)
|
|
| (((UINT32) minute) << 16)
|
|
| (((UINT32) hour) << 24);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
cia_clock_tod - Update TOD on CIA A
|
|
-------------------------------------------------*/
|
|
|
|
void cia_clock_tod(const device_config *device)
|
|
{
|
|
cia_state *cia;
|
|
|
|
cia = get_token(device);
|
|
|
|
if (cia->tod_running)
|
|
{
|
|
if ((device->type == CIA6526R1) || (device->type == CIA6526R2))
|
|
{
|
|
/* The 6526 split the value into hours, minutes, seconds and
|
|
* subseconds */
|
|
cia6526_increment(cia);
|
|
}
|
|
else if (device->type == CIA8520)
|
|
{
|
|
/* the 8520 has a straight 24-bit counter */
|
|
cia->tod++;
|
|
cia->tod &= 0xffffff;
|
|
}
|
|
|
|
if (cia->tod == cia->alarm)
|
|
{
|
|
cia->ics |= 0x04;
|
|
cia_update_interrupts(device);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
cia_clock_tod_callback
|
|
-------------------------------------------------*/
|
|
|
|
static TIMER_CALLBACK( cia_clock_tod_callback )
|
|
{
|
|
const device_config *device = (const device_config *)ptr;
|
|
cia_clock_tod(device);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
cia_issue_index
|
|
-------------------------------------------------*/
|
|
|
|
void cia_issue_index(const device_config *device)
|
|
{
|
|
cia_state *cia = get_token(device);
|
|
cia->ics |= 0x10;
|
|
cia_update_interrupts(device);
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
cia_set_input_sp
|
|
-------------------------------------------------*/
|
|
|
|
void cia_set_input_sp(const device_config *device, int data)
|
|
{
|
|
cia_state *cia = get_token(device);
|
|
cia->sp = data;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
cia_set_input_cnt
|
|
-------------------------------------------------*/
|
|
|
|
void cia_set_input_cnt(const device_config *device, int data)
|
|
{
|
|
cia_state *cia = get_token(device);
|
|
|
|
/* is this a rising edge? */
|
|
if (!cia->cnt && data)
|
|
{
|
|
/* does timer #0 bump on CNT? */
|
|
if ((cia->timer[0].mode & 0x21) == 0x21)
|
|
cia_timer_bump(device, 0);
|
|
|
|
/* does timer #1 bump on CNT? */
|
|
if ((cia->timer[1].mode & 0x61) == 0x21)
|
|
cia_timer_bump(device, 1);
|
|
|
|
/* if the serial port is set to output, the CNT will shift the port */
|
|
if (!(cia->timer[0].mode & 0x40))
|
|
{
|
|
cia->serial >>= 1;
|
|
if (cia->sp)
|
|
cia->serial |= 0x80;
|
|
|
|
if (++cia->shift == 8)
|
|
{
|
|
cia->sdr = cia->serial;
|
|
cia->serial = 0;
|
|
cia->shift = 0;
|
|
cia->ics |= 0x08;
|
|
cia_update_interrupts(device);
|
|
}
|
|
}
|
|
}
|
|
cia->cnt = data ? 1 : 0;
|
|
}
|
|
|
|
WRITE_LINE_DEVICE_HANDLER( mos6526_tod_w )
|
|
{
|
|
if (state) cia_clock_tod(device);
|
|
}
|
|
|
|
WRITE_LINE_DEVICE_HANDLER( mos6526_cnt_w )
|
|
{
|
|
cia_set_input_cnt(device, state);
|
|
}
|
|
|
|
WRITE_LINE_DEVICE_HANDLER( mos6526_sp_w )
|
|
{
|
|
cia_set_input_sp(device, state);
|
|
}
|
|
|
|
WRITE_LINE_DEVICE_HANDLER( mos6526_flag_w )
|
|
{
|
|
cia_state *cia = get_token(device);
|
|
|
|
if (cia->flag && !state)
|
|
{
|
|
cia_issue_index(device);
|
|
}
|
|
|
|
cia->flag = state;
|
|
}
|
|
|
|
/*-------------------------------------------------
|
|
cia_r
|
|
-------------------------------------------------*/
|
|
|
|
READ8_DEVICE_HANDLER( cia_r )
|
|
{
|
|
cia_timer *timer;
|
|
cia_state *cia;
|
|
cia_port *port;
|
|
UINT8 data = 0x00;
|
|
|
|
cia = get_token(device);
|
|
offset &= 0x0F;
|
|
|
|
switch(offset)
|
|
{
|
|
/* port A/B data */
|
|
case CIA_PRA:
|
|
case CIA_PRB:
|
|
port = &cia->port[offset & 1];
|
|
data = devcb_call_read8(&port->read, 0);
|
|
data = ((data & ~port->ddr) | (port->latch & port->ddr)) & port->mask_value;
|
|
|
|
port->in = data;
|
|
|
|
if (offset == CIA_PRB)
|
|
{
|
|
/* timer #0 can change PB6 */
|
|
if (cia->timer[0].mode & 0x02)
|
|
{
|
|
cia_timer_update(&cia->timer[0], -1);
|
|
if (cia->timer[0].count != 0)
|
|
data |= 0x40;
|
|
else
|
|
data &= ~0x40;
|
|
}
|
|
|
|
/* timer #1 can change PB7 */
|
|
if (cia->timer[1].mode & 0x02)
|
|
{
|
|
cia_timer_update(&cia->timer[1], -1);
|
|
if (cia->timer[1].count != 0)
|
|
data |= 0x80;
|
|
else
|
|
data &= ~0x80;
|
|
}
|
|
|
|
/* pulse /PC following the read */
|
|
cia_update_pc(device);
|
|
}
|
|
break;
|
|
|
|
/* port A/B direction */
|
|
case CIA_DDRA:
|
|
case CIA_DDRB:
|
|
port = &cia->port[offset & 1];
|
|
data = port->ddr;
|
|
break;
|
|
|
|
/* timer A/B low byte */
|
|
case CIA_TALO:
|
|
case CIA_TBLO:
|
|
timer = &cia->timer[(offset >> 1) & 1];
|
|
data = cia_get_timer(timer) >> 0;
|
|
break;
|
|
|
|
/* timer A/B high byte */
|
|
case CIA_TAHI:
|
|
case CIA_TBHI:
|
|
timer = &cia->timer[(offset >> 1) & 1];
|
|
data = cia_get_timer(timer) >> 8;
|
|
break;
|
|
|
|
/* TOD counter */
|
|
case CIA_TOD0:
|
|
case CIA_TOD1:
|
|
case CIA_TOD2:
|
|
case CIA_TOD3:
|
|
if (device->type == CIA8520)
|
|
{
|
|
if (offset == CIA_TOD2)
|
|
{
|
|
cia->tod_latch = cia->tod;
|
|
cia->tod_latched = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (offset == CIA_TOD3)
|
|
{
|
|
cia->tod_latch = cia->tod;
|
|
cia->tod_latched = TRUE;
|
|
}
|
|
}
|
|
if (offset == CIA_TOD0)
|
|
cia->tod_latched = FALSE;
|
|
|
|
if (cia->tod_latched)
|
|
data = cia->tod_latch >> ((offset - CIA_TOD0) * 8);
|
|
else
|
|
data = cia->tod >> ((offset - CIA_TOD0) * 8);
|
|
break;
|
|
|
|
/* serial data ready */
|
|
case CIA_SDR:
|
|
data = cia->sdr;
|
|
break;
|
|
|
|
/* interrupt status/clear */
|
|
case CIA_ICR:
|
|
data = cia->ics;
|
|
cia->ics = 0; /* clear on read */
|
|
cia_update_interrupts(device);
|
|
break;
|
|
|
|
/* timer A/B mode */
|
|
case CIA_CRA:
|
|
case CIA_CRB:
|
|
timer = &cia->timer[offset & 1];
|
|
data = timer->mode;
|
|
break;
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
cia_w
|
|
-------------------------------------------------*/
|
|
|
|
WRITE8_DEVICE_HANDLER( cia_w )
|
|
{
|
|
cia_timer *timer;
|
|
cia_state *cia;
|
|
cia_port *port;
|
|
int shift;
|
|
|
|
cia = get_token(device);
|
|
offset &= 0x0F;
|
|
|
|
switch(offset)
|
|
{
|
|
/* port A/B data */
|
|
case CIA_PRA:
|
|
case CIA_PRB:
|
|
port = &cia->port[offset & 1];
|
|
port->latch = data;
|
|
port->out = (data & port->ddr) | (port->in & ~port->ddr);
|
|
devcb_call_write8(&port->write, 0, port->out);
|
|
|
|
/* pulse /PC following the write */
|
|
if (offset == CIA_PRB)
|
|
cia_update_pc(device);
|
|
|
|
break;
|
|
|
|
/* port A/B direction */
|
|
case CIA_DDRA:
|
|
case CIA_DDRB:
|
|
port = &cia->port[offset & 1];
|
|
port->ddr = data;
|
|
break;
|
|
|
|
/* timer A/B latch low */
|
|
case CIA_TALO:
|
|
case CIA_TBLO:
|
|
timer = &cia->timer[(offset >> 1) & 1];
|
|
timer->latch = (timer->latch & 0xff00) | (data << 0);
|
|
break;
|
|
|
|
/* timer A latch high */
|
|
case CIA_TAHI:
|
|
case CIA_TBHI:
|
|
timer = &cia->timer[(offset >> 1) & 1];
|
|
timer->latch = (timer->latch & 0x00ff) | (data << 8);
|
|
|
|
/* if the timer is one-shot, then force a start on it */
|
|
if (timer->mode & 0x08)
|
|
{
|
|
timer->mode |= 1;
|
|
cia_timer_update(timer, timer->latch);
|
|
}
|
|
else
|
|
{
|
|
/* if the timer is off, update the count */
|
|
if (!(timer->mode & 0x01))
|
|
cia_timer_update(timer, timer->latch);
|
|
}
|
|
break;
|
|
|
|
/* time of day latches */
|
|
case CIA_TOD0:
|
|
case CIA_TOD1:
|
|
case CIA_TOD2:
|
|
case CIA_TOD3:
|
|
shift = 8 * ((offset - CIA_TOD0));
|
|
|
|
/* alarm setting mode? */
|
|
if (cia->timer[1].mode & 0x80)
|
|
cia->alarm = (cia->alarm & ~(0xff << shift)) | (data << shift);
|
|
/* counter setting mode */
|
|
else
|
|
cia->tod = (cia->tod & ~(0xff << shift)) | (data << shift);
|
|
|
|
if (device->type == CIA8520)
|
|
{
|
|
if (offset == CIA_TOD2)
|
|
cia->tod_running = FALSE;
|
|
}
|
|
else
|
|
{
|
|
if (offset == CIA_TOD3)
|
|
cia->tod_running = FALSE;
|
|
}
|
|
if (offset == CIA_TOD0)
|
|
cia->tod_running = TRUE;
|
|
break;
|
|
|
|
/* serial data ready */
|
|
case CIA_SDR:
|
|
cia->sdr = data;
|
|
if (cia->timer[0].mode & 0x40)
|
|
cia->loaded = 1;
|
|
break;
|
|
|
|
/* interrupt control register */
|
|
case CIA_ICR:
|
|
if (data & 0x80)
|
|
cia->icr |= data & 0x7f;
|
|
else
|
|
cia->icr &= ~(data & 0x7f);
|
|
cia_update_interrupts(device);
|
|
break;
|
|
|
|
/* timer A/B modes */
|
|
case CIA_CRA:
|
|
case CIA_CRB:
|
|
timer = &cia->timer[offset & 1];
|
|
timer->mode = data & 0xef;
|
|
|
|
/* force load? */
|
|
if (data & 0x10)
|
|
cia_timer_update(timer, timer->latch);
|
|
else
|
|
cia_timer_update(timer, -1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
UINT8 cia_get_output_a(const device_config *device) { return (get_token(device)->port[0].latch | ~get_token(device)->port[0].ddr); }
|
|
UINT8 cia_get_output_b(const device_config *device) { return (get_token(device)->port[1].latch | ~get_token(device)->port[1].ddr); }
|
|
int cia_get_irq(const device_config *device) { return get_token(device)->irq; }
|
|
|
|
|
|
/*-------------------------------------------------
|
|
DEVICE_GET_INFO( cia6526r1 )
|
|
-------------------------------------------------*/
|
|
|
|
DEVICE_GET_INFO(cia6526r1)
|
|
{
|
|
switch (state)
|
|
{
|
|
/* --- the following bits of info are returned as 64-bit signed integers --- */
|
|
case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(cia_state); break;
|
|
case DEVINFO_INT_INLINE_CONFIG_BYTES: info->i = 0; break;
|
|
case DEVINFO_INT_CLASS: info->i = DEVICE_CLASS_PERIPHERAL; break;
|
|
|
|
/* --- the following bits of info are returned as pointers to data or functions --- */
|
|
case DEVINFO_FCT_START: info->start = DEVICE_START_NAME(cia); break;
|
|
case DEVINFO_FCT_STOP: /* Nothing */ break;
|
|
case DEVINFO_FCT_RESET: info->reset = DEVICE_RESET_NAME(cia); break;
|
|
case DEVINFO_FCT_VALIDITY_CHECK: info->validity_check = DEVICE_VALIDITY_CHECK_NAME(cia); break;
|
|
|
|
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
|
case DEVINFO_STR_NAME: strcpy(info->s, "6526 CIA rev1"); break;
|
|
case DEVINFO_STR_FAMILY: strcpy(info->s, "6526 CIA"); break;
|
|
case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
|
|
case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
|
|
case DEVINFO_STR_CREDITS: /* Nothing */ break;
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
DEVICE_GET_INFO( cia8520 )
|
|
-------------------------------------------------*/
|
|
|
|
DEVICE_GET_INFO(cia6526r2)
|
|
{
|
|
switch (state)
|
|
{
|
|
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
|
case DEVINFO_STR_NAME: strcpy(info->s, "6526 CIA rev2"); break;
|
|
default: DEVICE_GET_INFO_CALL(cia6526r1); break;
|
|
}
|
|
}
|
|
|
|
|
|
/*-------------------------------------------------
|
|
DEVICE_GET_INFO( cia8520 )
|
|
-------------------------------------------------*/
|
|
|
|
DEVICE_GET_INFO(cia8520)
|
|
{
|
|
switch (state)
|
|
{
|
|
/* --- the following bits of info are returned as NULL-terminated strings --- */
|
|
case DEVINFO_STR_NAME: strcpy(info->s, "8520 CIA"); break;
|
|
default: DEVICE_GET_INFO_CALL(cia6526r1); break;
|
|
}
|
|
}
|
|
|
|
|