// license:BSD-3-Clause
// copyright-holders:Gordon Jefferyes
/******************************************************************************
uPD7002 Analogue to Digital Converter
MESS Driver By:
Gordon Jefferyes
mess_bbc@gjeffery.dircon.co.uk
******************************************************************************/
#include "emu.h"
#include "upd7002.h"
/* Device Interface */
DEFINE_DEVICE_TYPE(UPD7002, upd7002_device, "upd7002", "uPD7002 ADC")
upd7002_device::upd7002_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, UPD7002, tag, owner, clock)
, m_status(0)
, m_data1(0)
, m_data0(0)
, m_digitalvalue(0)
, m_conversion_counter(0)
, m_get_analogue_cb(*this)
, m_eoc_cb(*this)
{
}
//-------------------------------------------------
// device_start - device-specific startup
//-------------------------------------------------
void upd7002_device::device_start()
{
m_get_analogue_cb.resolve();
m_eoc_cb.resolve();
// register for state saving
save_item(NAME(m_status));
save_item(NAME(m_data1));
save_item(NAME(m_data0));
save_item(NAME(m_digitalvalue));
save_item(NAME(m_conversion_counter));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void upd7002_device::device_reset()
{
m_status = 0;
m_data1 = 0;
m_data0 = 0;
m_digitalvalue = 0;
m_conversion_counter = 0;
}
/*****************************************************************************
Implementation
*****************************************************************************/
READ_LINE_MEMBER( upd7002_device::eoc_r )
{
return BIT(m_status, 7);
}
void upd7002_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
switch (id)
{
case TIMER_CONVERSION_COMPLETE:
{
int counter_value = param;
if (counter_value == m_conversion_counter)
{
// this really always does a 12 bit conversion
m_data1 = m_digitalvalue >> 8;
m_data0 = m_digitalvalue & 0xf0;
// set the status register with top 2 MSB, not busy and conversion complete
m_status = (m_status & 0x0f) | ((m_data1 & 0xc0) >> 2) | 0x40;
// call the EOC function with EOC from status
// eoc_r(0) this has just been set to 0
if (!m_eoc_cb.isnull()) m_eoc_cb(0);
m_conversion_counter=0;
}
}
break;
default:
throw emu_fatalerror("Unknown id in upd7002_device::device_timer");
}
}
uint8_t upd7002_device::read(offs_t offset)
{
switch (offset & 0x03)
{
case 0:
return m_status;
case 1:
return m_data1;
case 2:
case 3:
return m_data0;
}
return 0;
}
void upd7002_device::write(offs_t offset, uint8_t data)
{
/* logerror("write to uPD7002 $%02X = $%02X\n",offset,data); */
switch (offset & 0x03)
{
case 0:
/*
Data Latch/AD start
D0 and D1 together define which one of the four input channels is selected
D2 flag input, normally set to 0????
D3 defines whether an 8 (0) or 12 (1) bit resolution conversion should occur
D4 to D7 not used.
an 8 bit conversion typically takes 4ms
an 12 bit conversion typically takes 10ms
writing to this register will initiate a conversion.
*/
/* set D6=0 busy ,D7=1 conversion not complete */
m_status=(data & 0x0f) | 0x80;
// call the EOC function with EOC from status
// eoc_r(0) this has just been set to 1
if (!m_eoc_cb.isnull()) m_eoc_cb(1);
/* the uPD7002 works by sampling the analogue value at the start of the conversion
so it is read hear and stored until the end of the A to D conversion */
// this function should return a 16 bit value.
m_digitalvalue = m_get_analogue_cb(m_status & 0x03);
m_conversion_counter++;
// call a timer to start the conversion
if (m_status & 0x08)
{
// 12 bit conversion takes 10ms
timer_set(attotime::from_msec(10), TIMER_CONVERSION_COMPLETE, m_conversion_counter);
}
else
{
// 8 bit conversion takes 4ms
timer_set(attotime::from_msec(4), TIMER_CONVERSION_COMPLETE, m_conversion_counter);
}
break;
case 1:
case 2:
/* Nothing */
break;
case 3:
/* Test Mode: Used for inspecting the device, The data input-output terminals assume an input
state and are connected to the A/D counter. Therefore, the A/D conversion data
read out after this is meaningless.
*/
break;
}
}