adc0808: Rewrite and make it work

This commit is contained in:
Dirk Best 2018-04-02 11:51:02 +02:00
parent 7079fa8fa6
commit 0086d473b7
4 changed files with 254 additions and 270 deletions

View File

@ -75,18 +75,17 @@ MACHINE_CONFIG_START(newbrain_eim_device::device_add_mconfig)
MCFG_Z80CTC_ZC2_CB(WRITELINE(newbrain_eim_device, ctc_z2_w))
MCFG_TIMER_DRIVER_ADD_PERIODIC("z80ctc_c2", newbrain_eim_device, ctc_c2_tick, attotime::from_hz(XTAL(16'000'000)/4/13))
MCFG_DEVICE_ADD(ADC0809_TAG, ADC0808, 500000)
MCFG_ADC0808_OUT_EOC_CB(WRITELINE(newbrain_eim_device, adc_eoc_w))
MCFG_ADC0808_IN_VREF_POS_CB(newbrain_eim_device, adc_vref_pos_r)
MCFG_ADC0808_IN_VREF_NEG_CB(newbrain_eim_device, adc_vref_neg_r)
MCFG_ADC0808_IN_IN_0_CB(newbrain_eim_device, adc_input_r)
MCFG_ADC0808_IN_IN_1_CB(newbrain_eim_device, adc_input_r)
MCFG_ADC0808_IN_IN_2_CB(newbrain_eim_device, adc_input_r)
MCFG_ADC0808_IN_IN_3_CB(newbrain_eim_device, adc_input_r)
MCFG_ADC0808_IN_IN_4_CB(newbrain_eim_device, adc_input_r)
MCFG_ADC0808_IN_IN_5_CB(newbrain_eim_device, adc_input_r)
MCFG_ADC0808_IN_IN_6_CB(newbrain_eim_device, adc_input_r)
MCFG_ADC0808_IN_IN_7_CB(newbrain_eim_device, adc_input_r)
MCFG_DEVICE_ADD(ADC0809_TAG, ADC0809, 500000)
MCFG_ADC0808_EOC_CB(WRITELINE(newbrain_eim_device, adc_eoc_w))
MCFG_ADC0808_IN0_CB(GND)
MCFG_ADC0808_IN1_CB(GND)
MCFG_ADC0808_IN2_CB(GND)
MCFG_ADC0808_IN3_CB(GND)
MCFG_ADC0808_IN4_CB(GND)
MCFG_ADC0808_IN5_CB(GND)
MCFG_ADC0808_IN6_CB(GND)
MCFG_ADC0808_IN7_CB(GND)
MCFG_DEVICE_ADD(MC6850_TAG, ACIA6850, 0)
MCFG_ACIA6850_IRQ_HANDLER(WRITELINE(newbrain_eim_device, acia_interrupt))
@ -229,36 +228,6 @@ WRITE_LINE_MEMBER( newbrain_eim_device::adc_eoc_w )
}
//-------------------------------------------------
// adc_vref_pos_r -
//-------------------------------------------------
ADC0808_ANALOG_READ_CB( newbrain_eim_device::adc_vref_pos_r )
{
return 5.0;
}
//-------------------------------------------------
// adc_vref_neg_r -
//-------------------------------------------------
ADC0808_ANALOG_READ_CB( newbrain_eim_device::adc_vref_neg_r )
{
return 0.0;
}
//-------------------------------------------------
// adc_input_r -
//-------------------------------------------------
ADC0808_ANALOG_READ_CB( newbrain_eim_device::adc_input_r )
{
return 0.0;
}
//-------------------------------------------------
// acia_interrupt -
//-------------------------------------------------
@ -282,7 +251,7 @@ WRITE_LINE_MEMBER( newbrain_eim_device::ctc_z2_w )
//-------------------------------------------------
// adc_input_r -
// ctc_c2_tick -
//-------------------------------------------------
TIMER_DEVICE_CALLBACK_MEMBER(newbrain_eim_device::ctc_c2_tick)

View File

@ -58,10 +58,6 @@ private:
DECLARE_WRITE_LINE_MEMBER( ctc_z2_w );
DECLARE_WRITE_LINE_MEMBER( adc_eoc_w );
ADC0808_ANALOG_READ_CB(adc_vref_pos_r);
ADC0808_ANALOG_READ_CB(adc_vref_neg_r);
ADC0808_ANALOG_READ_CB(adc_input_r);
TIMER_DEVICE_CALLBACK_MEMBER(ctc_c2_tick);
required_device<z80ctc_device> m_ctc;

View File

@ -1,42 +1,74 @@
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/**********************************************************************
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************
National Semiconductor ADC0808/ADC0809 8-Bit A/D Converter emulation
ADC0808/ADC0809
The only difference between ADC0808 and ADC0809 is that the latter
chip allows twice as much adjusted error. Mitsubishi parts M58990P
and M58990P-1 are equivalent to ADC0808 and ADC0809.
A/D Converter with 8 Channel-Multiplexer
**********************************************************************/
***************************************************************************/
#include "emu.h"
#include "adc0808.h"
//**************************************************************************
// CONSTANTS/MACROS
//**************************************************************************
#define VERBOSE 0
//**************************************************************************
// DEVICE DEFINITIONS
//**************************************************************************
DEFINE_DEVICE_TYPE(ADC0808, adc0808_device, "adc0808", "ADC0808 A/D Converter")
DEFINE_DEVICE_TYPE(ADC0809, adc0809_device, "adc0809", "ADC0809 A/D Converter")
DEFINE_DEVICE_TYPE(M58990P, m58990p_device, "m58990p", "M58990P A/D Converter")
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
// device type definition
DEFINE_DEVICE_TYPE(ADC0808, adc0808_device, "adc0808", "ADC0808")
// permit our enum to be saved
ALLOW_SAVE_TYPE(adc0808_device::state);
//-------------------------------------------------
// adc0808_device - constructor
//-------------------------------------------------
adc0808_device::adc0808_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, ADC0808, tag, owner, clock),
m_out_eoc_cb(*this),
m_address(0),
m_start(0),
m_eoc(0),
m_next_eoc(0), m_sar(0),
m_cycle(0),
m_bit(0),
m_cycle_timer(nullptr)
adc0808_device::adc0808_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
device_t(mconfig, type, tag, owner, clock),
m_eoc_cb(*this), m_eoc_ff_cb(*this),
m_in_cb{ {*this}, {*this}, {*this}, {*this}, {*this}, {*this}, {*this}, {*this} },
m_state(STATE_IDLE),
m_cycle_timer(nullptr),
m_start(0), m_cycle(0), m_step(0), m_address(0), m_sar(0xff), m_eoc_pending(false)
{
}
adc0808_device::adc0808_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
adc0808_device(mconfig, ADC0808, tag, owner, clock)
{
}
//-------------------------------------------------
// adc0809_device - constructor
//-------------------------------------------------
adc0809_device::adc0809_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
adc0808_device(mconfig, ADC0809, tag, owner, clock)
{
}
//-------------------------------------------------
// m58990p_device - constructor
//-------------------------------------------------
m58990p_device::m58990p_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
adc0808_device(mconfig, M58990P, tag, owner, clock)
{
}
@ -47,146 +79,117 @@ adc0808_device::adc0808_device(const machine_config &mconfig, const char *tag, d
void adc0808_device::device_start()
{
// resolve callbacks
m_out_eoc_cb.resolve_safe();
m_in_vref_pos_cb.bind_relative_to(*owner());
m_in_vref_neg_cb.bind_relative_to(*owner());
m_in_in_0_cb.bind_relative_to(*owner());
m_in_in_1_cb.bind_relative_to(*owner());
m_in_in_2_cb.bind_relative_to(*owner());
m_in_in_3_cb.bind_relative_to(*owner());
m_in_in_4_cb.bind_relative_to(*owner());
m_in_in_5_cb.bind_relative_to(*owner());
m_in_in_6_cb.bind_relative_to(*owner());
m_in_in_7_cb.bind_relative_to(*owner());
m_eoc_cb.resolve_safe();
m_eoc_ff_cb.resolve_safe();
for (int i = 0; i < 8; i++)
m_in_cb[i].resolve_safe(0xff);
// allocate timers
m_cycle_timer = timer_alloc();
m_cycle_timer->adjust(attotime::zero, 0, attotime::from_hz(clock()));
// register for state saving
save_item(NAME(m_address));
// register for save states
save_item(NAME(m_state));
save_item(NAME(m_start));
save_item(NAME(m_eoc));
save_item(NAME(m_next_eoc));
save_item(NAME(m_sar));
save_item(NAME(m_cycle));
save_item(NAME(m_bit));
save_item(NAME(m_step));
save_item(NAME(m_address));
save_item(NAME(m_sar));
save_item(NAME(m_eoc_pending));
}
//-------------------------------------------------
// device_timer - handler timer events
//-------------------------------------------------
void adc0808_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
if (!m_start)
// eoc is delayed one cycle
if (m_eoc_pending)
{
if (m_cycle == 7)
m_eoc_cb(1);
m_eoc_ff_cb(1);
m_eoc_pending = false;
}
// start of conversion cycle
if (m_cycle == 0 && m_state == STATE_CONVERSION_START)
m_state = STATE_CONVERSION_RUNNING;
// end of conversion cycle
if (m_cycle == 7 && m_state == STATE_CONVERSION_RUNNING)
{
// the conversion takes 8 steps every 8 cycles
if (m_step++ == 7)
{
m_bit++;
m_step = 0;
m_sar = m_in_cb[m_address](0);
m_eoc_pending = true;
m_state = STATE_IDLE;
if (m_bit == 8)
{
/* sample input */
double vref_pos = m_in_vref_pos_cb();
double vref_neg = m_in_vref_neg_cb();
double input = 0;
switch (m_address)
{
case 0:
input = m_in_in_0_cb();
break;
case 1:
input = m_in_in_1_cb();
break;
case 2:
input = m_in_in_2_cb();
break;
case 3:
input = m_in_in_3_cb();
break;
case 4:
input = m_in_in_4_cb();
break;
case 5:
input = m_in_in_5_cb();
break;
case 6:
input = m_in_in_6_cb();
break;
case 7:
input = m_in_in_7_cb();
break;
}
m_sar = (255 * (input - vref_neg)) / (vref_pos - vref_neg);
/* trigger end of conversion */
m_next_eoc = 1;
}
if (VERBOSE)
logerror("Conversion finished, result %02x\n", m_sar);
}
}
if (m_cycle == 0)
{
/* set end of conversion pin */
if (m_next_eoc != m_eoc)
{
m_out_eoc_cb(m_next_eoc);
m_eoc = m_next_eoc;
}
}
m_cycle++;
if (m_cycle == 8)
{
m_cycle = 0;
}
// next cycle
m_cycle = (m_cycle + 1) & 7;
}
//-------------------------------------------------
// data_r - data read
//-------------------------------------------------
//**************************************************************************
// INTERFACE
//**************************************************************************
READ8_MEMBER( adc0808_device::data_r )
{
if (VERBOSE)
logerror("data_r: %02x\n", m_sar);
// oe connected to flip-flop clear
m_eoc_ff_cb(0);
return m_sar;
}
//-------------------------------------------------
// ale_w - address write
//-------------------------------------------------
WRITE8_MEMBER( adc0808_device::ale_w )
WRITE8_MEMBER( adc0808_device::address_w )
{
m_address = data;
m_address = data & 7;
}
//-------------------------------------------------
// start_w - start conversion
//-------------------------------------------------
WRITE_LINE_MEMBER( adc0808_device::start_w )
{
if (!m_start && state) // rising edge
if (m_start == 1 && state == 0)
{
// reset registers
m_sar = 0;
m_bit = 0;
m_state = STATE_CONVERSION_START;
}
else if (m_start && !state) // falling edge
else if (m_start == 0 && state == 1)
{
// start conversion
m_next_eoc = 0;
m_sar = 0;
m_eoc_cb(0);
m_eoc_pending = false;
}
m_start = state;
}
WRITE8_MEMBER( adc0808_device::address_offset_start_w )
{
if (VERBOSE)
logerror("address_offset_start_w %02x %02x\n", offset, data);
start_w(1);
address_w(space, 0, offset);
start_w(0);
}
WRITE8_MEMBER( adc0808_device::address_data_start_w )
{
if (VERBOSE)
logerror("address_data_start_w %02x %02x\n", offset, data);
start_w(1);
address_w(space, 0, data);
start_w(0);
}

View File

@ -1,138 +1,154 @@
// license:BSD-3-Clause
// copyright-holders:Curt Coder
/**********************************************************************
// license: BSD-3-Clause
// copyright-holders: Dirk Best
/***************************************************************************
National Semiconductor ADC0808/ADC0809 8-Bit A/D Converter emulation
ADC0808/ADC0809
**********************************************************************
_____ _____
IN3 1 |* \_/ | 28 IN2
IN4 2 | | 27 IN1
IN5 3 | | 26 IN0
IN6 4 | | 25 ADD A
IN7 5 | | 24 ADD B
START 6 | | 23 ADD C
EOC 7 | ADC0808 | 22 ALE
2-5 8 | ADC0809 | 21 2-1 MSB
OUTPUT ENABLE 9 | | 20 2-2
CLOCK 10 | | 19 2-3
Vcc 11 | | 18 2-4
Vref+ 12 | | 17 2-8 LSB
GND 13 | | 16 Vref-
2-7 14 |_____________| 15 2-6
A/D Converter with 8 Channel-Multiplexer
**********************************************************************/
___ ___
IN3 1 |* u | 28 IN2
IN4 2 | | 27 IN1
IN5 3 | | 26 IN0
IN6 4 | | 25 ADD A
IN7 5 | | 24 ADD B
START 6 | | 23 ADD C
EOC 7 | | 22 ALE
D4 8 | | 21 D0
OE 9 | | 20 D1
CLOCK 10 | | 19 D2
VCC 11 | | 18 D3
VREF+ 12 | | 17 D7
DND 13 | | 16 VREF-
D6 14 |_______| 15 D5
#ifndef MAME_MACHINE_ADC0808_H
#define MAME_MACHINE_ADC0808_H
Notes:
* The difference between the two devices is the total adjusted
error: ADC0808 ±½ LSB, ADC0809 ±1 LSB
* MM74C949 and M58990P are equivalent to ADC0808
* MM74C949-1 and M58990P-1 are equivalent to ADC0809
* ADC0816 and ADC0817 are 16 channel equivalents
***************************************************************************/
#ifndef MAME_DEVICES_MACHINE_ADC0808_H
#define MAME_DEVICES_MACHINE_ADC0808_H
#pragma once
//**************************************************************************
// INTERFACE CONFIGURATION MACROS
//**************************************************************************
#define MCFG_ADC0808_EOC_CB(_devcb) \
devcb = &adc0808_device::set_eoc_callback(*device, DEVCB_##_devcb);
// common hookup where the eoc output is connected to a flip-flop
#define MCFG_ADC0808_EOC_FF_CB(_devcb) \
devcb = &adc0808_device::set_eoc_ff_callback(*device, DEVCB_##_devcb);
#define MCFG_ADC0808_IN0_CB(_devcb) \
devcb = &adc0808_device::set_in_callback(*device, DEVCB_##_devcb, 0);
#define MCFG_ADC0808_IN1_CB(_devcb) \
devcb = &adc0808_device::set_in_callback(*device, DEVCB_##_devcb, 1);
#define MCFG_ADC0808_IN2_CB(_devcb) \
devcb = &adc0808_device::set_in_callback(*device, DEVCB_##_devcb, 2);
#define MCFG_ADC0808_IN3_CB(_devcb) \
devcb = &adc0808_device::set_in_callback(*device, DEVCB_##_devcb, 3);
#define MCFG_ADC0808_IN4_CB(_devcb) \
devcb = &adc0808_device::set_in_callback(*device, DEVCB_##_devcb, 4);
#define MCFG_ADC0808_IN5_CB(_devcb) \
devcb = &adc0808_device::set_in_callback(*device, DEVCB_##_devcb, 5);
#define MCFG_ADC0808_IN6_CB(_devcb) \
devcb = &adc0808_device::set_in_callback(*device, DEVCB_##_devcb, 6);
#define MCFG_ADC0808_IN7_CB(_devcb) \
devcb = &adc0808_device::set_in_callback(*device, DEVCB_##_devcb, 7);
//**************************************************************************
// TYPE DEFINITIONS
//**************************************************************************
// ======================> adc0808_analog_read
#define ADC0808_ANALOG_READ_CB(name) double name()
#define MCFG_ADC0808_OUT_EOC_CB(_devcb) \
devcb = &downcast<adc0808_device &>(*device).set_out_eoc_callback(DEVCB_##_devcb);
#define MCFG_ADC0808_IN_VREF_POS_CB(_class, _method) \
downcast<adc0808_device &>(*device).set_in_vref_pos_callback(adc0808_device::analog_read_delegate(&_class::_method, #_class "::" #_method, this));
#define MCFG_ADC0808_IN_VREF_NEG_CB(_class, _method) \
downcast<adc0808_device &>(*device).set_in_vref_neg_callback(adc0808_device::analog_read_delegate(&_class::_method, #_class "::" #_method, this));
#define MCFG_ADC0808_IN_IN_0_CB(_class, _method) \
downcast<adc0808_device &>(*device).set_in_in_0_callback(adc0808_device::analog_read_delegate(&_class::_method, #_class "::" #_method, this));
#define MCFG_ADC0808_IN_IN_1_CB(_class, _method) \
downcast<adc0808_device &>(*device).set_in_in_1_callback(adc0808_device::analog_read_delegate(&_class::_method, #_class "::" #_method, this));
#define MCFG_ADC0808_IN_IN_2_CB(_class, _method) \
downcast<adc0808_device &>(*device).set_in_in_2_callback(adc0808_device::analog_read_delegate(&_class::_method, #_class "::" #_method, this));
#define MCFG_ADC0808_IN_IN_3_CB(_class, _method) \
downcast<adc0808_device &>(*device).set_in_in_3_callback(adc0808_device::analog_read_delegate(&_class::_method, #_class "::" #_method, this));
#define MCFG_ADC0808_IN_IN_4_CB(_class, _method) \
downcast<adc0808_device &>(*device).set_in_in_4_callback(adc0808_device::analog_read_delegate(&_class::_method, #_class "::" #_method, this));
#define MCFG_ADC0808_IN_IN_5_CB(_class, _method) \
downcast<adc0808_device &>(*device).set_in_in_5_callback(adc0808_device::analog_read_delegate(&_class::_method, #_class "::" #_method, this));
#define MCFG_ADC0808_IN_IN_6_CB(_class, _method) \
downcast<adc0808_device &>(*device).set_in_in_6_callback(adc0808_device::analog_read_delegate(&_class::_method, #_class "::" #_method, this));
#define MCFG_ADC0808_IN_IN_7_CB(_class, _method) \
downcast<adc0808_device &>(*device).set_in_in_7_callback(adc0808_device::analog_read_delegate(&_class::_method, #_class "::" #_method, this));
// ======================> adc0808_device
class adc0808_device : public device_t
class adc0808_device : public device_t
{
public:
typedef device_delegate<double ()> analog_read_delegate;
// construction/destruction
adc0808_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
template <class Object> devcb_base &set_out_eoc_callback(Object &&cb) { return m_out_eoc_cb.set_callback(std::forward<Object>(cb)); }
template <typename Object> void set_in_vref_pos_callback(Object &&cb) { m_in_vref_pos_cb = std::forward<Object>(cb); }
template <typename Object> void set_in_vref_neg_callback(Object &&cb) { m_in_vref_neg_cb = std::forward<Object>(cb); }
template <typename Object> void set_in_in_0_callback(Object &&cb) { m_in_in_0_cb = std::forward<Object>(cb); }
template <typename Object> void set_in_in_1_callback(Object &&cb) { m_in_in_1_cb = std::forward<Object>(cb); }
template <typename Object> void set_in_in_2_callback(Object &&cb) { m_in_in_2_cb = std::forward<Object>(cb); }
template <typename Object> void set_in_in_3_callback(Object &&cb) { m_in_in_3_cb = std::forward<Object>(cb); }
template <typename Object> void set_in_in_4_callback(Object &&cb) { m_in_in_4_cb = std::forward<Object>(cb); }
template <typename Object> void set_in_in_5_callback(Object &&cb) { m_in_in_5_cb = std::forward<Object>(cb); }
template <typename Object> void set_in_in_6_callback(Object &&cb) { m_in_in_6_cb = std::forward<Object>(cb); }
template <typename Object> void set_in_in_7_callback(Object &&cb) { m_in_in_7_cb = std::forward<Object>(cb); }
// configuration
template <class Object> static devcb_base &set_eoc_callback(device_t &device, Object &&cb)
{ return downcast<adc0808_device &>(device).m_eoc_cb.set_callback(std::forward<Object>(cb)); }
DECLARE_READ8_MEMBER( data_r );
DECLARE_WRITE8_MEMBER( ale_w );
template <class Object> static devcb_base &set_eoc_ff_callback(device_t &device, Object &&cb)
{ return downcast<adc0808_device &>(device).m_eoc_ff_cb.set_callback(std::forward<Object>(cb)); }
DECLARE_WRITE_LINE_MEMBER( start_w );
template <class Object> static devcb_base &set_in_callback(device_t &device, Object &&cb, int index)
{ return downcast<adc0808_device &>(device).m_in_cb[index].set_callback(std::forward<Object>(cb)); }
DECLARE_READ8_MEMBER(data_r);
DECLARE_WRITE8_MEMBER(address_w);
DECLARE_WRITE_LINE_MEMBER(start_w);
// common hookups
DECLARE_WRITE8_MEMBER(address_offset_start_w); // start and ale connected, address to the address bus
DECLARE_WRITE8_MEMBER(address_data_start_w); // start and ale connected, address to the data bus
protected:
adc0808_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock);
// device-level overrides
virtual void device_start() override;
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
private:
devcb_write_line m_out_eoc_cb;
analog_read_delegate m_in_vref_pos_cb;
analog_read_delegate m_in_vref_neg_cb;
analog_read_delegate m_in_in_0_cb;
analog_read_delegate m_in_in_1_cb;
analog_read_delegate m_in_in_2_cb;
analog_read_delegate m_in_in_3_cb;
analog_read_delegate m_in_in_4_cb;
analog_read_delegate m_in_in_5_cb;
analog_read_delegate m_in_in_6_cb;
analog_read_delegate m_in_in_7_cb;
// callbacks
devcb_write_line m_eoc_cb;
devcb_write_line m_eoc_ff_cb;
devcb_read8 m_in_cb[8];
int m_address; // analog channel address
int m_start; // start conversion pin
int m_eoc; // end of conversion pin
int m_next_eoc; // next value end of conversion pin
enum state : int
{
STATE_IDLE,
STATE_CONVERSION_START,
STATE_CONVERSION_RUNNING
};
state m_state;
uint8_t m_sar; // successive approximation register
int m_cycle; // clock cycle counter
int m_bit; // bit counter
// timers
emu_timer *m_cycle_timer;
// state
int m_start;
int m_cycle;
int m_step;
int m_address;
uint8_t m_sar;
bool m_eoc_pending;
};
class adc0809_device : public adc0808_device
{
public:
adc0809_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
class m58990p_device : public adc0808_device
{
public:
m58990p_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
};
// device type definition
DECLARE_DEVICE_TYPE(ADC0808, adc0808_device)
DECLARE_DEVICE_TYPE(ADC0809, adc0809_device)
DECLARE_DEVICE_TYPE(M58990P, m58990p_device)
#endif // MAME_MACHINE_ADC0808_H
#endif // MAME_DEVICES_MACHINE_ADC0808_H