// license:BSD-3-Clause
// copyright-holders:R. Belmont
/**********************************************************************
General Instruments AY-5-3600 Keyboard Encoder emulation
*********************************************************************/
/*
TODO:
- scan timer clock frequency
- more accurate emulation of real chip
*/
#include "emu.h"
#include "kb3600.h"
//#define VERBOSE 1
#include "logmacro.h"
//**************************************************************************
// GLOBAL VARIABLES
//**************************************************************************
// devices
DEFINE_DEVICE_TYPE(AY3600, ay3600_device, "ay3600", "AY-5-3600 Keyboard Encoder")
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
//-------------------------------------------------
// ay3600_device - constructor
//-------------------------------------------------
ay3600_device::ay3600_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, AY3600, tag, owner, clock),
m_read_x0(*this),
m_read_x1(*this),
m_read_x2(*this),
m_read_x3(*this),
m_read_x4(*this),
m_read_x5(*this),
m_read_x6(*this),
m_read_x7(*this),
m_read_x8(*this),
m_read_shift(*this),
m_read_control(*this),
m_write_data_ready(*this),
m_write_ako(*this)
{
for (auto & elem : m_x_mask)
{
elem = 0;
}
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void ay3600_device::device_start()
{
// resolve callbacks
m_read_x0.resolve_safe(0);
m_read_x1.resolve_safe(0);
m_read_x2.resolve_safe(0);
m_read_x3.resolve_safe(0);
m_read_x4.resolve_safe(0);
m_read_x5.resolve_safe(0);
m_read_x6.resolve_safe(0);
m_read_x7.resolve_safe(0);
m_read_x8.resolve_safe(0);
m_read_shift.resolve_safe(0);
m_read_control.resolve_safe(0);
m_write_data_ready.resolve_safe();
m_write_ako.resolve_safe();
// allocate timers
m_scan_timer = timer_alloc();
m_scan_timer->adjust(attotime::from_hz(60), 0, attotime::from_hz(60));
m_ako = 0;
// state saving
save_item(NAME(m_b));
save_item(NAME(m_ako));
save_item(NAME(m_x_mask));
}
//-------------------------------------------------
// device_start - device-specific reset
//-------------------------------------------------
void ay3600_device::device_reset()
{
}
//-------------------------------------------------
// device_timer - handler timer events
//-------------------------------------------------
void ay3600_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
int ako = 0;
for (int x = 0; x < 9; x++)
{
uint16_t data = 0;
switch(x)
{
case 0: data = m_read_x0(); break;
case 1: data = m_read_x1(); break;
case 2: data = m_read_x2(); break;
case 3: data = m_read_x3(); break;
case 4: data = m_read_x4(); break;
case 5: data = m_read_x5(); break;
case 6: data = m_read_x6(); break;
case 7: data = m_read_x7(); break;
case 8: data = m_read_x8(); break;
}
for (int y = 0; y < 10; y++)
{
int b = (x * 10) + y;
if (b > 63)
{
b -= 64;
b = 0x100 | b;
}
b |= (m_read_shift() << 6);
b |= (m_read_control() << 7);
if (BIT(data, y))
{
ako = 1;
if (!(m_x_mask[x] & (1 << y)))
{
m_x_mask[x] |= (1 << y);
if (m_b != b)
{
m_b = b;
m_write_data_ready(1);
if (ako != m_ako)
{
m_write_ako(ako);
m_ako = ako;
}
return;
}
}
}
else // key released, unmark it from the "down" info
{
m_x_mask[x] &= ~(1 << y);
}
}
}
if (!ako)
{
m_b = -1;
}
if (ako != m_ako)
{
m_write_ako(ako);
m_ako = ako;
}
}
//-------------------------------------------------
// b_r -
//-------------------------------------------------
uint16_t ay3600_device::b_r()
{
uint16_t data = m_b;
m_write_data_ready(0);
return data;
}