mirror of
https://github.com/holub/mame
synced 2025-10-05 08:41:31 +03:00
435 lines
10 KiB
C
435 lines
10 KiB
C
/*
|
|
* nlbase.c
|
|
*
|
|
*/
|
|
|
|
#include "nl_base.h"
|
|
#include "devices/nld_system.h"
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// netlist_base_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
netlist_base_t::netlist_base_t()
|
|
: m_mainclock(NULL),
|
|
m_time_ps(netlist_time::zero),
|
|
m_rem(0),
|
|
m_div(NETLIST_DIV)
|
|
{
|
|
}
|
|
|
|
netlist_base_t::~netlist_base_t()
|
|
{
|
|
}
|
|
|
|
ATTR_COLD void netlist_base_t::set_mainclock_dev(NETLIB_NAME(netdev_mainclock) *dev)
|
|
{
|
|
m_mainclock = dev;
|
|
}
|
|
|
|
ATTR_COLD void netlist_base_t::reset()
|
|
{
|
|
m_time_ps = netlist_time::zero;
|
|
m_rem = 0;
|
|
m_queue.clear();
|
|
if (m_mainclock != NULL)
|
|
m_mainclock->m_Q.set_time(netlist_time::zero);
|
|
}
|
|
|
|
|
|
void netlist_base_t::set_clock_freq(UINT64 clockfreq)
|
|
{
|
|
m_div = netlist_time::from_hz(clockfreq).as_raw();
|
|
m_rem = 0;
|
|
assert_always(m_div == NETLIST_DIV, "netlist: illegal clock!");
|
|
NL_VERBOSE_OUT(("Setting clock %" I64FMT "d and divisor %d\n", clockfreq, m_div));
|
|
}
|
|
|
|
ATTR_HOT ATTR_ALIGN inline void netlist_base_t::update_time(const netlist_time t, INT32 &atime)
|
|
{
|
|
if (NETLIST_DIV_BITS == 0)
|
|
{
|
|
const netlist_time delta = t - m_time_ps;
|
|
m_time_ps = t;
|
|
atime -= delta.as_raw();
|
|
} else {
|
|
const netlist_time delta = t - m_time_ps + netlist_time::from_raw(m_rem);
|
|
m_time_ps = t;
|
|
m_rem = delta.as_raw() & NETLIST_MASK;
|
|
atime -= (delta.as_raw() >> NETLIST_DIV_BITS);
|
|
|
|
// The folling is suitable for non-power of 2 m_divs ...
|
|
// atime -= divu_64x32_rem(delta.as_raw(), m_div, &m_rem);
|
|
}
|
|
}
|
|
|
|
ATTR_HOT ATTR_ALIGN void netlist_base_t::process_queue(INT32 &atime)
|
|
{
|
|
if (m_mainclock == NULL)
|
|
{
|
|
while ( (atime > 0) && (m_queue.is_not_empty()))
|
|
{
|
|
const queue_t::entry_t e = m_queue.pop();
|
|
update_time(e.time(), atime);
|
|
|
|
if (FATAL_ERROR_AFTER_NS)
|
|
printf("%s\n", e.object().netdev()->name().cstr());
|
|
|
|
e.object().update_devs();
|
|
|
|
add_to_stat(m_perf_out_processed, 1);
|
|
|
|
if (FATAL_ERROR_AFTER_NS)
|
|
if (time() > NLTIME_FROM_NS(FATAL_ERROR_AFTER_NS))
|
|
fatalerror("Stopped");
|
|
}
|
|
|
|
if (atime > 0)
|
|
{
|
|
m_time_ps += netlist_time::from_raw(atime * m_div);
|
|
atime = 0;
|
|
}
|
|
} else {
|
|
net_output_t &mcQ = m_mainclock->m_Q;
|
|
const netlist_time inc = m_mainclock->m_inc;
|
|
|
|
while (atime > 0)
|
|
{
|
|
if (m_queue.is_not_empty())
|
|
{
|
|
while (m_queue.peek().time() > mcQ.time())
|
|
{
|
|
update_time(mcQ.time(), atime);
|
|
|
|
NETLIB_NAME(netdev_mainclock)::mc_update(mcQ, time() + inc);
|
|
|
|
}
|
|
const queue_t::entry_t e = m_queue.pop();
|
|
|
|
update_time(e.time(), atime);
|
|
|
|
e.object().update_devs();
|
|
|
|
} else {
|
|
update_time(mcQ.time(), atime);
|
|
|
|
NETLIB_NAME(netdev_mainclock)::mc_update(mcQ, time() + inc);
|
|
}
|
|
if (FATAL_ERROR_AFTER_NS)
|
|
if (time() > NLTIME_FROM_NS(FATAL_ERROR_AFTER_NS))
|
|
fatalerror("Stopped");
|
|
|
|
add_to_stat(m_perf_out_processed, 1);
|
|
}
|
|
|
|
if (atime > 0)
|
|
{
|
|
m_time_ps += netlist_time::from_raw(atime * m_div);
|
|
atime = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// Default netlist elements ...
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// net_core_device_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
netlist_core_device_t::netlist_core_device_t()
|
|
: net_object_t(DEVICE)
|
|
{
|
|
}
|
|
|
|
ATTR_COLD void netlist_core_device_t::init(netlist_setup_t &setup, const astring &name)
|
|
{
|
|
m_netlist = &setup.netlist();
|
|
m_name = name;
|
|
|
|
#if USE_DELEGATES
|
|
#if USE_PMFDELEGATES
|
|
void (netlist_core_device_t::* pFunc)() = &netlist_core_device_t::update;
|
|
static_update = reinterpret_cast<net_update_delegate>((this->*pFunc));
|
|
#else
|
|
static_update = net_update_delegate(&netlist_core_device_t::update, "update", this);
|
|
// get the pointer to the member function
|
|
#endif
|
|
#endif
|
|
|
|
}
|
|
|
|
ATTR_COLD void net_device_t::init(netlist_setup_t &setup, const astring &name)
|
|
{
|
|
netlist_core_device_t::init(setup, name);
|
|
m_setup = &setup;
|
|
start();
|
|
}
|
|
|
|
|
|
netlist_core_device_t::~netlist_core_device_t()
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// net_device_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
ATTR_HOT ATTR_ALIGN const netlist_sig_t netlist_core_device_t::INPLOGIC_PASSIVE(logic_input_t &inp)
|
|
{
|
|
if (inp.state() == net_input_t::INP_STATE_PASSIVE)
|
|
{
|
|
inp.activate();
|
|
const netlist_sig_t ret = inp.Q();
|
|
inp.inactivate();
|
|
return ret;
|
|
}
|
|
else
|
|
return inp.Q();
|
|
|
|
}
|
|
|
|
net_device_t::net_device_t()
|
|
: netlist_core_device_t(),
|
|
m_inputs(20),
|
|
m_setup(NULL),
|
|
m_variable_input_count(false)
|
|
{
|
|
}
|
|
|
|
net_device_t::~net_device_t()
|
|
{
|
|
//printf("~net_device_t\n");
|
|
}
|
|
|
|
ATTR_COLD void net_device_t::register_sub(netlist_core_device_t &dev, const astring &name)
|
|
{
|
|
dev.init(*m_setup, name);
|
|
}
|
|
|
|
void net_device_t::register_output(netlist_core_device_t &dev, const astring &name, net_output_t &port)
|
|
{
|
|
m_setup->register_output(*this, dev, name, port);
|
|
}
|
|
|
|
void net_device_t::register_output(const astring &name, net_output_t &port)
|
|
{
|
|
m_setup->register_output(*this,*this,name, port);
|
|
}
|
|
|
|
void net_device_t::register_input(netlist_core_device_t &dev, const astring &name, net_input_t &inp, net_input_t::net_input_state type)
|
|
{
|
|
m_setup->register_input(*this, dev, name, inp, type);
|
|
}
|
|
|
|
void net_device_t::register_input(const astring &name, net_input_t &inp, net_input_t::net_input_state type)
|
|
{
|
|
register_input(*this, name, inp, type);
|
|
}
|
|
|
|
void net_device_t::register_link_internal(netlist_core_device_t &dev, net_input_t &in, net_output_t &out, net_input_t::net_input_state aState)
|
|
{
|
|
in.set_output(out);
|
|
in.init_input(&dev, aState);
|
|
//if (in.state() != net_input_t::INP_STATE_PASSIVE)
|
|
out.register_con(in);
|
|
}
|
|
|
|
void net_device_t::register_link_internal(net_input_t &in, net_output_t &out, net_input_t::net_input_state aState)
|
|
{
|
|
register_link_internal(*this, in, out, aState);
|
|
}
|
|
|
|
void net_device_t::register_param(netlist_core_device_t &dev, const astring &name, net_param_t ¶m, double initialVal)
|
|
{
|
|
param.set_netdev(dev);
|
|
param.initial(initialVal);
|
|
m_setup->register_param(name, ¶m);
|
|
}
|
|
|
|
void net_device_t::register_param(const astring &name, net_param_t ¶m, double initialVal)
|
|
{
|
|
register_param(*this,name, param, initialVal);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// net_terminal_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
ATTR_COLD void net_terminal_t::init_terminal(netlist_core_device_t *dev)
|
|
{
|
|
m_netdev = dev;
|
|
m_netlist = dev->netlist();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// net_input_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
ATTR_COLD void net_input_t::init_input(netlist_core_device_t *dev, net_input_state astate)
|
|
{
|
|
init_terminal(dev);
|
|
m_state = astate;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// net_output_t
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
net_output_t::net_output_t(int atype)
|
|
: net_terminal_t(atype)
|
|
, m_low_V(0.0)
|
|
, m_high_V(0.0)
|
|
, m_last_Q(0)
|
|
, m_Q(0)
|
|
, m_new_Q(0)
|
|
, m_Q_analog(0.0)
|
|
, m_new_Q_analog(0.0)
|
|
, m_num_cons(0)
|
|
, m_time(netlist_time::zero)
|
|
, m_active(0)
|
|
, m_in_queue(2)
|
|
{
|
|
//m_cons = global_alloc_array(net_input_t *, OUTPUT_MAX_CONNECTIONS);
|
|
}
|
|
|
|
ATTR_HOT inline void net_output_t::update_dev(const net_input_t *inp, const UINT32 mask)
|
|
{
|
|
if ((inp->state() & mask) != 0)
|
|
{
|
|
ATTR_UNUSED netlist_core_device_t *netdev = inp->netdev();
|
|
begin_timing(netdev->total_time);
|
|
inc_stat(netdev->stat_count);
|
|
netdev->update_dev();
|
|
end_timing(netdev()->total_time);
|
|
}
|
|
}
|
|
|
|
ATTR_HOT inline void net_output_t::update_devs()
|
|
{
|
|
assert(m_num_cons != 0);
|
|
|
|
const UINT32 masks[4] = { 1, 5, 3, 1 };
|
|
m_Q = m_new_Q;
|
|
m_Q_analog = m_new_Q_analog;
|
|
m_in_queue = 2; /* mark as taken ... */
|
|
|
|
const UINT32 mask = masks[ (m_last_Q << 1) | m_Q ];
|
|
|
|
switch (m_num_cons)
|
|
{
|
|
case 2:
|
|
update_dev(m_cons[1], mask);
|
|
case 1:
|
|
update_dev(m_cons[0], mask);
|
|
break;
|
|
default:
|
|
{
|
|
for (int i=0; i < m_num_cons; i++)
|
|
update_dev(m_cons[i], mask);
|
|
}
|
|
break;
|
|
}
|
|
|
|
m_last_Q = m_Q;
|
|
}
|
|
|
|
ATTR_COLD void net_output_t::register_con(net_input_t &input)
|
|
{
|
|
int i;
|
|
if (m_num_cons >= OUTPUT_MAX_CONNECTIONS)
|
|
fatalerror("Connections exceeded for %s\n", netdev()->name().cstr());
|
|
|
|
/* keep similar devices together */
|
|
for (i = 0; i < m_num_cons; i++)
|
|
if (m_cons[i]->netdev() == input.netdev())
|
|
break;
|
|
|
|
for (int j = m_num_cons; j > i; j--)
|
|
m_cons[j] = m_cons[j - 1];
|
|
|
|
m_cons[i] = &input;
|
|
m_num_cons++;
|
|
if (input.state() != net_input_t::INP_STATE_PASSIVE)
|
|
m_active++;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// netdev_const
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
NETLIB_START(netdev_ttl_const)
|
|
{
|
|
register_output("Q", m_Q);
|
|
register_param("CONST", m_const, 0.0);
|
|
}
|
|
|
|
NETLIB_UPDATE(netdev_ttl_const)
|
|
{
|
|
}
|
|
|
|
NETLIB_UPDATE_PARAM(netdev_ttl_const)
|
|
{
|
|
OUTLOGIC(m_Q, m_const.ValueInt(), NLTIME_IMMEDIATE);
|
|
}
|
|
|
|
NETLIB_START(netdev_analog_const)
|
|
{
|
|
register_output("Q", m_Q);
|
|
register_param("CONST", m_const, 0.0);
|
|
}
|
|
|
|
NETLIB_UPDATE(netdev_analog_const)
|
|
{
|
|
}
|
|
|
|
NETLIB_UPDATE_PARAM(netdev_analog_const)
|
|
{
|
|
m_Q.initial(m_const.Value());
|
|
}
|
|
|
|
NETLIB_UPDATE(netdev_analog_callback)
|
|
{
|
|
// FIXME: Remove after device cleanup
|
|
if (!m_callback.isnull())
|
|
m_callback(INPANALOG(m_in));
|
|
}
|
|
|
|
// license:GPL-2.0+
|
|
// copyright-holders:Couriersud
|
|
// ----------------------------------------------------------------------------------------
|
|
// netdev_mainclock
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
ATTR_HOT inline void NETLIB_NAME(netdev_mainclock)::mc_update(net_output_t &Q, const netlist_time curtime)
|
|
{
|
|
Q.m_new_Q = !Q.m_new_Q;
|
|
Q.set_time(curtime);
|
|
Q.update_devs();
|
|
}
|
|
|
|
ATTR_COLD NETLIB_START(netdev_mainclock)
|
|
{
|
|
register_output("Q", m_Q);
|
|
|
|
register_param("FREQ", m_freq, 7159000.0 * 5);
|
|
m_inc = netlist_time::from_hz(m_freq.Value()*2);
|
|
|
|
}
|
|
|
|
ATTR_HOT NETLIB_UPDATE_PARAM(netdev_mainclock)
|
|
{
|
|
m_inc = netlist_time::from_hz(m_freq.Value()*2);
|
|
}
|
|
|
|
ATTR_HOT NETLIB_UPDATE(netdev_mainclock)
|
|
{
|
|
// this is only called during setup ...
|
|
m_Q.m_new_Q = !m_Q.m_new_Q;
|
|
m_Q.set_time(m_netlist->time() + m_inc);
|
|
}
|