// license:BSD-3-Clause
// copyright-holders:Peter Trauner, Mathis Rosenhauer
/**********************************************************************
Rockwell 6522 VIA interface and emulation
This is based on the M6821 emulation in MAME.
To do:
T2 pulse counting mode
Pulse mode handshake output
**********************************************************************/
/*
1999-Dec-22 PeT
vc20 random number generation only partly working
(reads (uninitialized) timer 1 and timer 2 counter)
timer init, reset, read changed
2017-Feb-15 Edstrom
Fixed shift registers to be more accurate, eg 50/50 duty cycle, latching
on correct flanks and leading and trailing flanks added + logging.
*/
#include "emu.h"
#include "6522via.h"
/***************************************************************************
PARAMETERS
***************************************************************************/
#define LOG_SETUP (1U << 1)
#define LOG_SHIFT (1U << 2)
#define LOG_READ (1U << 3)
#define LOG_INT (1U << 4)
//#define VERBOSE (LOG_SHIFT|LOG_INT|LOG_SETUP)
//#define LOG_OUTPUT_FUNC printf
#include "logmacro.h"
#define LOGSETUP(...) LOGMASKED(LOG_SETUP, __VA_ARGS__)
#define LOGSHIFT(...) LOGMASKED(LOG_SHIFT, __VA_ARGS__)
#define LOGR(...) LOGMASKED(LOG_READ, __VA_ARGS__)
#define LOGINT(...) LOGMASKED(LOG_INT, __VA_ARGS__)
/***************************************************************************
MACROS
***************************************************************************/
/* Macros for PCR */
#define CA1_LOW_TO_HIGH(c) (c & 0x01)
#define CA1_HIGH_TO_LOW(c) (!(c & 0x01))
#define CB1_LOW_TO_HIGH(c) (c & 0x10)
#define CB1_HIGH_TO_LOW(c) (!(c & 0x10))
#define CA2_INPUT(c) (!(c & 0x08))
#define CA2_LOW_TO_HIGH(c) ((c & 0x0c) == 0x04)
#define CA2_HIGH_TO_LOW(c) ((c & 0x0c) == 0x00)
#define CA2_IND_IRQ(c) ((c & 0x0a) == 0x02)
#define CA2_OUTPUT(c) (c & 0x08)
#define CA2_AUTO_HS(c) ((c & 0x0c) == 0x08)
#define CA2_HS_OUTPUT(c) ((c & 0x0e) == 0x08)
#define CA2_PULSE_OUTPUT(c) ((c & 0x0e) == 0x0a)
#define CA2_FIX_OUTPUT(c) ((c & 0x0c) == 0x0c)
#define CA2_OUTPUT_LEVEL(c) ((c & 0x02) >> 1)
#define CB2_INPUT(c) (!(c & 0x80))
#define CB2_LOW_TO_HIGH(c) ((c & 0xc0) == 0x40)
#define CB2_HIGH_TO_LOW(c) ((c & 0xc0) == 0x00)
#define CB2_IND_IRQ(c) ((c & 0xa0) == 0x20)
#define CB2_OUTPUT(c) (c & 0x80)
#define CB2_AUTO_HS(c) ((c & 0xc0) == 0x80)
#define CB2_HS_OUTPUT(c) ((c & 0xe0) == 0x80)
#define CB2_PULSE_OUTPUT(c) ((c & 0xe0) == 0xa0)
#define CB2_FIX_OUTPUT(c) ((c & 0xc0) == 0xc0)
#define CB2_OUTPUT_LEVEL(c) ((c & 0x20) >> 5)
/* Macros for ACR */
#define PA_LATCH_ENABLE(c) (c & 0x01)
#define PB_LATCH_ENABLE(c) (c & 0x02)
#define SR_DISABLED(c) (!(c & 0x1c))
#define SI_T2_CONTROL(c) ((c & 0x1c) == 0x04)
#define SI_O2_CONTROL(c) ((c & 0x1c) == 0x08)
#define SI_EXT_CONTROL(c) ((c & 0x1c) == 0x0c)
#define SO_T2_RATE(c) ((c & 0x1c) == 0x10)
#define SO_T2_CONTROL(c) ((c & 0x1c) == 0x14)
#define SO_O2_CONTROL(c) ((c & 0x1c) == 0x18)
#define SO_EXT_CONTROL(c) ((c & 0x1c) == 0x1c)
#define T1_SET_PB7(c) (c & 0x80)
#define T1_CONTINUOUS(c) (c & 0x40)
#define T2_COUNT_PB6(c) (c & 0x20)
/* Interrupt flags */
#define INT_CA2 0x01
#define INT_CA1 0x02
#define INT_SR 0x04
#define INT_CB2 0x08
#define INT_CB1 0x10
#define INT_T2 0x20
#define INT_T1 0x40
#define INT_ANY 0x80
#define CLR_PA_INT() clear_int(INT_CA1 | ((!CA2_IND_IRQ(m_pcr)) ? INT_CA2: 0))
#define CLR_PB_INT() clear_int(INT_CB1 | ((!CB2_IND_IRQ(m_pcr)) ? INT_CB2: 0))
#define IFR_DELAY 3
#define TIMER1_VALUE (m_t1ll+(m_t1lh<<8))
#define TIMER2_VALUE (m_t2ll+(m_t2lh<<8))
/***************************************************************************
INLINE FUNCTIONS
***************************************************************************/
uint16_t via6522_device::get_counter1_value()
{
uint16_t val;
if(m_t1_active)
{
val = attotime_to_clocks(m_t1->remaining()) - IFR_DELAY;
}
else
{
val = 0xffff - attotime_to_clocks(machine().time() - m_time1);
}
return val;
}
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
// device type definition
DEFINE_DEVICE_TYPE(VIA6522, via6522_device, "via6522", "6522 VIA")
void via6522_device::map(address_map &map)
{
map(0x00, 0x0f).rw(FUNC(via6522_device::read), FUNC(via6522_device::write));
}
//-------------------------------------------------
// via6522_device - constructor
//-------------------------------------------------
via6522_device::via6522_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, VIA6522, tag, owner, clock),
m_in_a_handler(*this),
m_in_b_handler(*this),
m_out_a_handler(*this),
m_out_b_handler(*this),
m_ca2_handler(*this),
m_cb1_handler(*this),
m_cb2_handler(*this),
m_irq_handler(*this),
m_in_a(0xff),
m_in_ca1(0),
m_in_ca2(0),
m_out_ca2(0),
m_in_b(0),
m_in_cb1(0),
m_in_cb2(0),
m_pcr(0),
m_acr(0),
m_ier(0),
m_ifr(0)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void via6522_device::device_start()
{
m_in_a_handler.resolve();
m_in_b_handler.resolve();
m_out_a_handler.resolve_safe();
m_out_b_handler.resolve_safe();
m_cb1_handler.resolve_safe();
m_ca2_handler.resolve_safe();
m_cb2_handler.resolve_safe();
m_irq_handler.resolve_safe();
m_t1ll = 0xf3; /* via at 0x9110 in vic20 show these values */
m_t1lh = 0xb5; /* ports are not written by kernel! */
m_t2ll = 0xff; /* taken from vice */
m_t2lh = 0xff;
m_sr = 0;
m_time2 = m_time1 = machine().time();
m_t1 = timer_alloc(TIMER_T1);
m_t2 = timer_alloc(TIMER_T2);
m_ca2_timer = timer_alloc(TIMER_CA2);
m_shift_timer = timer_alloc(TIMER_SHIFT);
m_shift_irq_timer = timer_alloc(TIMER_SHIFT_IRQ);
/* save state register */
save_item(NAME(m_in_a));
save_item(NAME(m_in_ca1));
save_item(NAME(m_in_ca2));
save_item(NAME(m_out_a));
save_item(NAME(m_out_ca2));
save_item(NAME(m_ddr_a));
save_item(NAME(m_latch_a));
save_item(NAME(m_in_b));
save_item(NAME(m_in_cb1));
save_item(NAME(m_in_cb2));
save_item(NAME(m_out_b));
save_item(NAME(m_out_cb1));
save_item(NAME(m_out_cb2));
save_item(NAME(m_ddr_b));
save_item(NAME(m_latch_b));
save_item(NAME(m_t1cl));
save_item(NAME(m_t1ch));
save_item(NAME(m_t1ll));
save_item(NAME(m_t1lh));
save_item(NAME(m_t2cl));
save_item(NAME(m_t2ch));
save_item(NAME(m_t2ll));
save_item(NAME(m_t2lh));
save_item(NAME(m_sr));
save_item(NAME(m_pcr));
save_item(NAME(m_acr));
save_item(NAME(m_ier));
save_item(NAME(m_ifr));
save_item(NAME(m_time1));
save_item(NAME(m_t1_active));
save_item(NAME(m_t1_pb7));
save_item(NAME(m_time2));
save_item(NAME(m_t2_active));
save_item(NAME(m_shift_counter));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void via6522_device::device_reset()
{
m_out_a = 0;
m_out_ca2 = 1;
m_ddr_a = 0;
m_latch_a = 0;
m_out_b = 0;
m_out_cb1 = 1;
m_out_cb2 = 1;
m_ddr_b = 0;
m_latch_b = 0;
m_t1cl = 0;
m_t1ch = 0;
m_t2cl = 0;
m_t2ch = 0;
m_pcr = 0;
m_acr = 0;
m_ier = 0;
m_ifr = 0;
m_t1_active = 0;
m_t1_pb7 = 1;
m_t2_active = 0;
m_shift_counter = 0;
output_pa();
output_pb();
m_ca2_handler(m_out_ca2);
m_cb1_handler(m_out_cb1);
m_cb2_handler(m_out_cb2);
}
void via6522_device::output_irq()
{
if (m_ier & m_ifr & 0x7f)
{
if ((m_ifr & INT_ANY) == 0)
{
LOGINT("INT asserted\n");
m_ifr |= INT_ANY;
m_irq_handler(ASSERT_LINE);
}
}
else
{
if (m_ifr & INT_ANY)
{
LOGINT("INT cleared\n");
m_ifr &= ~INT_ANY;
m_irq_handler(CLEAR_LINE);
}
}
}
/*-------------------------------------------------
via_set_int - external interrupt check
-------------------------------------------------*/
void via6522_device::set_int(int data)
{
if (!(m_ifr & data))
{
m_ifr |= data;
output_irq();
LOGINT("granted\n");
LOG("%s:6522VIA chip %s: IFR = %02X\n", machine().describe_context(), tag(), m_ifr);
}
else
{
LOGINT("denied\n");
}
}
/*-------------------------------------------------
via_clear_int - external interrupt check
-------------------------------------------------*/
void via6522_device::clear_int(int data)
{
if (m_ifr & data)
{
LOGINT("cleared\n");
m_ifr &= ~data;
output_irq();
LOG("%s:6522VIA chip %s: IFR = %02X\n", machine().describe_context(), tag(), m_ifr);
}
else
{
LOGINT("not cleared\n");
}
}
/*-------------------------------------------------
via_shift
-------------------------------------------------*/
void via6522_device::shift_out()
{
// Only shift out msb on falling flank
if (m_shift_counter & 1)
{
LOGSHIFT(" %s shift Out SR: %02x->", tag(), m_sr);
m_out_cb2 = (m_sr >> 7) & 1;
m_sr = (m_sr << 1) | m_out_cb2;
LOGSHIFT("%02x CB2: %d\n", m_sr, m_out_cb2);
m_cb2_handler(m_out_cb2);
if (m_shift_counter == 1 && SO_EXT_CONTROL(m_acr))
{
LOGINT("SHIFT EXT out INT request ");
set_int(INT_SR); // IRQ on last falling flank for external clock (mode 7)
}
}
else // Check for INT condition, eg the last and raising flank of the 15-0 falling/raising flanks
{
if (!SO_T2_RATE(m_acr)) // The T2 continous shifter doesn't do interrupts (mode 4)
{
if (m_shift_counter == 0 && (SO_O2_CONTROL(m_acr) || SO_T2_CONTROL(m_acr)))
{
LOGINT("SHIFT O2/T2 out INT request ");
set_int(INT_SR); // IRQ on last raising flank for internal clock (mode 5-6)
}
}
}
m_shift_counter = (m_shift_counter - 1) & 0x0f; // Count all flanks
}
void via6522_device::shift_in()
{
// Only shift in data on raising flank
if ( !(m_shift_counter & 1) )
{
LOGSHIFT("%s shift In SR: %02x->", tag(), m_sr);
m_sr = (m_sr << 1) | (m_in_cb2 & 1);
LOGSHIFT("%02x\n", m_sr);
if (m_shift_counter == 0)
{
LOGINT("SHIFT in INT request ");
// set_int(INT_SR);// TODO: this interrupt is 1-2 clock cycles too early
m_shift_irq_timer->adjust(clocks_to_attotime(2)/2); // Delay IRQ 2 flanks for all shift INs (mode 1-3)
}
}
m_shift_counter = (m_shift_counter - 1) & 0x0f; // Count all flanks
}
void via6522_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
switch (id)
{
case TIMER_SHIFT_IRQ: // This timer event is a delayed IRQ for improved cycle accuracy
set_int(INT_SR); // triggered from shift_in or shift_out on the last rising flank
m_shift_irq_timer->adjust(attotime::never); // Not needed really...
break;
case TIMER_SHIFT:
LOGSHIFT("SHIFT timer event CB1 %s edge, %d\n", m_out_cb1 & 1 ? "falling" : "raising", m_shift_counter);
m_out_cb1 ^= 1;
m_cb1_handler(m_out_cb1);
// we call shift methods for all flanks
if (SO_T2_RATE(m_acr) || SO_T2_CONTROL(m_acr) || SO_O2_CONTROL(m_acr))
{
shift_out();
}
else if (SI_T2_CONTROL(m_acr) || SI_O2_CONTROL(m_acr))
{
shift_in();
}
// If in continous mode or the shifter is still shifting we re-arm the timer
if (SO_T2_RATE(m_acr) || (m_shift_counter != 0x0f))
{
if (SI_O2_CONTROL(m_acr) || SO_O2_CONTROL(m_acr))
{
m_shift_timer->adjust(clocks_to_attotime(1) / 2);
}
else if (SO_T2_RATE(m_acr) || SO_T2_CONTROL(m_acr) || SI_T2_CONTROL(m_acr))
{
m_shift_timer->adjust(clocks_to_attotime(m_t2ll + 2) / 2);
}
else // otherwise we stop it
{
m_shift_timer->adjust(attotime::never);
}
}
break;
case TIMER_T1:
if (T1_CONTINUOUS (m_acr))
{
m_t1_pb7 = !m_t1_pb7;
m_t1->adjust(clocks_to_attotime(TIMER1_VALUE + IFR_DELAY));
}
else
{
m_t1_pb7 = 1;
m_t1_active = 0;
m_time1 = machine().time();
}
if (T1_SET_PB7(m_acr))
{
output_pb();
}
LOGINT("T1 INT request ");
set_int(INT_T1);
break;
case TIMER_T2:
m_t2_active = 0;
m_time2 = machine().time();
LOGINT("T2 INT request ");
set_int(INT_T2);
break;
case TIMER_CA2:
m_out_ca2 = 1;
m_ca2_handler(m_out_ca2);
break;
}
}
uint8_t via6522_device::input_pa()
{
/// TODO: REMOVE THIS
if (!m_in_a_handler.isnull())
{
if (m_ddr_a != 0xff)
m_in_a = m_in_a_handler(0);
return (m_out_a & m_ddr_a) + (m_in_a & ~m_ddr_a);
}
return m_in_a & (m_out_a | ~m_ddr_a);
}
void via6522_device::output_pa()
{
uint8_t pa = (m_out_a & m_ddr_a) | ~m_ddr_a;
m_out_a_handler(pa);
}
uint8_t via6522_device::input_pb()
{
/// TODO: REMOVE THIS
if (m_ddr_b != 0xff && !m_in_b_handler.isnull())
{
m_in_b = m_in_b_handler(0);
}
uint8_t pb = (m_out_b & m_ddr_b) + (m_in_b & ~m_ddr_b);
if (T1_SET_PB7(m_acr))
pb = (pb & 0x7f) | (m_t1_pb7 << 7);
return pb;
}
void via6522_device::output_pb()
{
uint8_t pb = (m_out_b & m_ddr_b) | ~m_ddr_b;
if (T1_SET_PB7(m_acr))
pb = (pb & 0x7f) | (m_t1_pb7 << 7);
m_out_b_handler(pb);
}
/*-------------------------------------------------
via_r - CPU interface for VIA read
-------------------------------------------------*/
READ8_MEMBER( via6522_device::read )
{
int val = 0;
if (machine().side_effects_disabled())
return 0;
offset &= 0xf;
switch (offset)
{
case VIA_PB:
/* update the input */
if (PB_LATCH_ENABLE(m_acr) == 0)
{
val = input_pb();
}
else
{
val = m_latch_b;
}
LOGINT("PB INT ");
CLR_PB_INT();
break;
case VIA_PA:
/* update the input */
if (PA_LATCH_ENABLE(m_acr) == 0)
{
val = input_pa();
}
else
{
val = m_latch_a;
}
LOGINT("PA INT ");
CLR_PA_INT();
if (m_out_ca2 && (CA2_PULSE_OUTPUT(m_pcr) || CA2_AUTO_HS(m_pcr)))
{
m_out_ca2 = 0;
m_ca2_handler(m_out_ca2);
}
if (CA2_PULSE_OUTPUT(m_pcr))
m_ca2_timer->adjust(clocks_to_attotime(1));
break;
case VIA_PANH:
/* update the input */
if (PA_LATCH_ENABLE(m_acr) == 0)
{
val = input_pa();
}
else
{
val = m_latch_a;
}
break;
case VIA_DDRB:
val = m_ddr_b;
break;
case VIA_DDRA:
val = m_ddr_a;
break;
case VIA_T1CL:
LOGINT("T1CL INT ");
clear_int(INT_T1);
val = get_counter1_value() & 0xFF;
break;
case VIA_T1CH:
val = get_counter1_value() >> 8;
break;
case VIA_T1LL:
val = m_t1ll;
break;
case VIA_T1LH:
val = m_t1lh;
break;
case VIA_T2CL:
LOGINT("T2CL INT ");
clear_int(INT_T2);
if (m_t2_active)
{
val = attotime_to_clocks(m_t2->remaining()) & 0xff;
}
else
{
if (T2_COUNT_PB6(m_acr))
{
val = m_t2cl;
}
else
{
val = (0x10000 - (attotime_to_clocks(machine().time() - m_time2) & 0xffff) - 1) & 0xff;
}
}
break;
case VIA_T2CH:
if (m_t2_active)
{
val = attotime_to_clocks(m_t2->remaining()) >> 8;
}
else
{
if (T2_COUNT_PB6(m_acr))
{
val = m_t2ch;
}
else
{
val = (0x10000 - (attotime_to_clocks(machine().time() - m_time2) & 0xffff) - 1) >> 8;
}
}
break;
case VIA_SR:
LOGSHIFT("Read SR: %02x ", m_sr);
val = m_sr;
m_out_cb1 = 1;
m_cb1_handler(m_out_cb1);
m_shift_counter = 0x0f;
LOGINT("SR INT ");
clear_int(INT_SR);
LOGSHIFT(" - ACR: %02x ", m_acr);
if (SI_O2_CONTROL(m_acr) || SO_O2_CONTROL(m_acr))
{
m_shift_timer->adjust(clocks_to_attotime(8) / 2); // 8 flanks to start shifter from a read
LOGSHIFT(" - read SR starts O2 timer ");
}
else if (SI_T2_CONTROL(m_acr) || SO_T2_CONTROL(m_acr))
{
m_shift_timer->adjust(clocks_to_attotime(m_t2ll + 2) / 2);
LOGSHIFT(" - read SR starts T2 timer ");
}
else if (! SO_T2_RATE(m_acr))
{
m_shift_timer->adjust(attotime::never);
LOGSHIFT("Timer stops");
}
LOGSHIFT("\n");
break;
case VIA_PCR:
val = m_pcr;
break;
case VIA_ACR:
val = m_acr;
break;
case VIA_IER:
val = m_ier | 0x80;
break;
case VIA_IFR:
val = m_ifr;
break;
}
LOGR(" * %s Reg %02x -> %02x - %s\n", tag(), offset, val, std::array<char const *, 16>
{{"IRB", "IRA", "DDRB", "DDRA", "T1CL","T1CH","T1LL","T1LH","T2CL","T2CH","SR","ACR","PCR","IFR","IER","IRA (nh)"}}[offset]);
return val;
}
/*-------------------------------------------------
via_w - CPU interface for VIA write
-------------------------------------------------*/
WRITE8_MEMBER( via6522_device::write )
{
offset &=0x0f;
LOGSETUP(" * %s Reg %02x <- %02x - %s\n", tag(), offset, data, std::array<char const *, 16>
{{"ORB", "ORA", "DDRB", "DDRA", "T1CL","T1CH","T1LL","T1LH","T2CL","T2CH","SR","ACR","PCR","IFR","IER","ORA (nh)"}}[offset]);
switch (offset)
{
case VIA_PB:
m_out_b = data;
if (m_ddr_b != 0)
{
output_pb();
}
LOGINT("PB INT ");
CLR_PB_INT();
if (m_out_cb2 && CB2_AUTO_HS(m_pcr))
{
m_out_cb2 = 0;
m_cb2_handler(m_out_cb2);
}
break;
case VIA_PA:
m_out_a = data;
if (m_ddr_a != 0)
{
output_pa();
}
LOGINT("PA INT ");
CLR_PA_INT();
if (m_out_ca2 && (CA2_PULSE_OUTPUT(m_pcr) || CA2_AUTO_HS(m_pcr)))
{
m_out_ca2 = 0;
m_ca2_handler(m_out_ca2);
}
if (CA2_PULSE_OUTPUT(m_pcr))
m_ca2_timer->adjust(clocks_to_attotime(1));
break;
case VIA_PANH:
m_out_a = data;
if (m_ddr_a != 0)
{
output_pa();
}
break;
case VIA_DDRB:
if ( data != m_ddr_b )
{
m_ddr_b = data;
output_pb();
}
break;
case VIA_DDRA:
if (m_ddr_a != data)
{
m_ddr_a = data;
output_pa();
}
break;
case VIA_T1CL:
case VIA_T1LL:
m_t1ll = data;
break;
case VIA_T1LH:
m_t1lh = data;
LOGINT("T1LH INT ");
clear_int(INT_T1);
break;
case VIA_T1CH:
m_t1ch = m_t1lh = data;
m_t1cl = m_t1ll;
LOGINT("T1CH INT ");
clear_int(INT_T1);
m_t1_pb7 = 0;
if (T1_SET_PB7(m_acr))
{
output_pb();
}
m_t1->adjust(clocks_to_attotime(TIMER1_VALUE + IFR_DELAY));
m_t1_active = 1;
break;
case VIA_T2CL:
m_t2ll = data;
break;
case VIA_T2CH:
m_t2ch = m_t2lh = data;
m_t2cl = m_t2ll;
LOGINT("T2 INT ");
clear_int(INT_T2);
if (!T2_COUNT_PB6(m_acr))
{
m_t2->adjust(clocks_to_attotime(TIMER2_VALUE + IFR_DELAY));
m_t2_active = 1;
}
else
{
m_t2->adjust(clocks_to_attotime(TIMER2_VALUE));
m_t2_active = 1;
m_time2 = machine().time();
}
break;
case VIA_SR:
m_sr = data;
LOGSHIFT("Write SR: %02x\n", m_sr);
// make sure CB1 is high - this should not be needed though
if (m_out_cb1 != 1)
{
logerror("VIA: CB1 is low starting shifter\n");
m_out_cb1 = 1;
m_cb1_handler(m_out_cb1);
}
m_shift_counter = 0x0f;
LOGINT("SR INT ");
clear_int(INT_SR);
LOGSHIFT(" - ACR is: %02x ", m_acr);
if (SO_O2_CONTROL(m_acr) || SI_O2_CONTROL(m_acr))
{
m_shift_timer->adjust(clocks_to_attotime(8) / 2); // 8 flanks to start shifter from a write
LOGSHIFT(" - write SR starts O2 timer");
}
else if (SO_T2_RATE(m_acr) || SO_T2_CONTROL(m_acr) || SI_T2_CONTROL(m_acr))
{
m_shift_timer->adjust(clocks_to_attotime(m_t2ll + 2) / 2);
LOGSHIFT(" - write starts T2 timer");
}
else
{
m_shift_timer->adjust(attotime::never); // In case we change mode before counter expire
LOGSHIFT(" - timer stops");
}
LOGSHIFT("\n");
break;
case VIA_PCR:
m_pcr = data;
LOG("%s:6522VIA chip %s: PCR = %02X\n", machine().describe_context(), tag(), data);
if (CA2_FIX_OUTPUT(data) && m_out_ca2 != CA2_OUTPUT_LEVEL(data))
{
m_out_ca2 = CA2_OUTPUT_LEVEL(data);
m_ca2_handler(m_out_ca2);
}
if (CB2_FIX_OUTPUT(data) && m_out_cb2 != CB2_OUTPUT_LEVEL(data))
{
m_out_cb2 = CB2_OUTPUT_LEVEL(data);
m_cb2_handler(m_out_cb2);
}
break;
case VIA_ACR:
{
uint16_t counter1 = get_counter1_value();
m_acr = data;
LOGSHIFT("Write ACR: %02x ", m_acr);
output_pb();
LOGSHIFT("Shift mode [%02x]: ", (m_acr >> 2) & 7);
if (SR_DISABLED(m_acr)) LOGSHIFT("Disabled");
if (SI_T2_CONTROL(m_acr)) LOGSHIFT("IN on T2");
if (SI_O2_CONTROL(m_acr)) LOGSHIFT("IN on O2");
if (SI_EXT_CONTROL(m_acr)) LOGSHIFT("IN on EXT");
if (SO_T2_RATE(m_acr)) LOGSHIFT("OUT on continous T2");
if (SO_T2_CONTROL(m_acr)) LOGSHIFT("OUT on T2");
if (SO_O2_CONTROL(m_acr)) LOGSHIFT("OUT on O2");
if (SO_EXT_CONTROL(m_acr)) LOGSHIFT("OUT on EXT");
if (SR_DISABLED(m_acr) || SI_EXT_CONTROL(m_acr) || SO_EXT_CONTROL(m_acr))
{
m_shift_timer->adjust(attotime::never);
LOGSHIFT(" Timer stops");
}
if (T1_CONTINUOUS(m_acr))
{
m_t1->adjust(clocks_to_attotime(counter1 + IFR_DELAY));
m_t1_active = 1;
}
LOGSHIFT("\n");
}
break;
case VIA_IER:
if (data & 0x80)
{
m_ier |= data & 0x7f;
}
else
{
m_ier &= ~(data & 0x7f);
}
output_irq();
break;
case VIA_IFR:
if (data & INT_ANY)
{
data = 0x7f;
}
LOGINT("IFR INT ");
clear_int(data);
break;
}
}
void via6522_device::write_pa(int line, int state)
{
if (state)
m_in_a |= (1 << line);
else
m_in_a &= ~(1 << line);
}
WRITE8_MEMBER( via6522_device::write_pa )
{
m_in_a = data;
}
/*-------------------------------------------------
ca1_w - interface setting VIA port CA1 input
-------------------------------------------------*/
WRITE_LINE_MEMBER( via6522_device::write_ca1 )
{
if (m_in_ca1 != state)
{
m_in_ca1 = state;
LOG("%s:6522VIA chip %s: CA1 = %02X\n", machine().describe_context(), tag(), m_in_ca1);
if ((m_in_ca1 && CA1_LOW_TO_HIGH(m_pcr)) || (!m_in_ca1 && CA1_HIGH_TO_LOW(m_pcr)))
{
if (PA_LATCH_ENABLE(m_acr))
{
m_latch_a = input_pa();
}
LOGINT("CA1 INT request ");
set_int(INT_CA1);
if (!m_out_ca2 && CA2_AUTO_HS(m_pcr))
{
m_out_ca2 = 1;
m_ca2_handler(m_out_ca2);
}
}
}
}
/*-------------------------------------------------
ca2_w - interface setting VIA port CA2 input
-------------------------------------------------*/
WRITE_LINE_MEMBER( via6522_device::write_ca2 )
{
if (m_in_ca2 != state)
{
m_in_ca2 = state;
if (CA2_INPUT(m_pcr))
{
if ((m_in_ca2 && CA2_LOW_TO_HIGH(m_pcr)) || (!m_in_ca2 && CA2_HIGH_TO_LOW(m_pcr)))
{
LOGINT("CA2 INT request ");
set_int(INT_CA2);
}
}
}
}
void via6522_device::write_pb(int line, int state)
{
if (state)
m_in_b |= (1 << line);
else
m_in_b &= ~(1 << line);
}
WRITE8_MEMBER( via6522_device::write_pb )
{
m_in_b = data;
}
/*-------------------------------------------------
write_cb1 - interface setting VIA port CB1 input
-------------------------------------------------*/
WRITE_LINE_MEMBER( via6522_device::write_cb1 )
{
if (m_in_cb1 != state)
{
m_in_cb1 = state;
if ((m_in_cb1 && CB1_LOW_TO_HIGH(m_pcr)) || (!m_in_cb1 && CB1_HIGH_TO_LOW(m_pcr)))
{
if (PB_LATCH_ENABLE(m_acr))
{
m_latch_b = input_pb();
}
LOGINT("CB1 INT request ");
set_int(INT_CB1);
if (!m_out_cb2 && CB2_AUTO_HS(m_pcr))
{
m_out_cb2 = 1;
m_cb2_handler(1);
}
}
// The shifter shift is not controlled by PCR
if (SO_EXT_CONTROL(m_acr))
{
LOGSHIFT("SHIFT OUT EXT/CB1 falling edge, %d\n", m_shift_counter);
shift_out();
}
else if (SI_EXT_CONTROL(m_acr))
{
LOGSHIFT("SHIFT IN EXT/CB1 raising edge, %d\n", m_shift_counter);
shift_in();
}
}
}
/*-------------------------------------------------
write_cb2 - interface setting VIA port CB2 input
-------------------------------------------------*/
WRITE_LINE_MEMBER( via6522_device::write_cb2 )
{
if (m_in_cb2 != state)
{
m_in_cb2 = state;
LOGSHIFT("CB2 IN: %d\n", m_in_cb2);
if (CB2_INPUT(m_pcr))
{
if ((m_in_cb2 && CB2_LOW_TO_HIGH(m_pcr)) || (!m_in_cb2 && CB2_HIGH_TO_LOW(m_pcr)))
{
LOGINT("CB2 INT request ");
set_int(INT_CB2);
}
}
}
}