Implemented a generic implementation to model discrete net lists. [Couriersud]

- Based on timeslot modelling, the implementation models gate delays in logic chips.
- Design ready to be split into a generic and a MAME implementation part. 
- Design prepared to merge in discrete components from discrete.*
- Supports code based as well as external net list parsing.

Ultimately, net lists and discrete emulation should share one code base. The class design was set up to accomplish this. There is no point in having multiple 555, 7474 implementations around. 
Most of the code will be moved to lib/netlist going forward to allow usage in other projects.
This commit is contained in:
Couriersud 2012-07-22 11:23:53 +00:00
parent 90f7a66a1b
commit 6fb7943200
6 changed files with 2902 additions and 0 deletions

4
.gitattributes vendored
View File

@ -946,6 +946,10 @@ src/emu/machine/msm6242.c svneol=native#text/plain
src/emu/machine/msm6242.h svneol=native#text/plain
src/emu/machine/ncr539x.c svneol=native#text/plain
src/emu/machine/ncr539x.h svneol=native#text/plain
src/emu/machine/net_lib.c svneol=native#text/plain
src/emu/machine/net_lib.h svneol=native#text/plain
src/emu/machine/netlist.c svneol=native#text/plain
src/emu/machine/netlist.h svneol=native#text/plain
src/emu/machine/nmc9306.c svneol=native#text/plain
src/emu/machine/nmc9306.h svneol=native#text/plain
src/emu/machine/nscsi_bus.c svneol=native#text/plain

View File

@ -220,6 +220,8 @@ EMUMACHINEOBJS = \
$(EMUMACHINE)/msm58321.o \
$(EMUMACHINE)/msm6242.o \
$(EMUMACHINE)/ncr539x.o \
$(EMUMACHINE)/netlist.o \
$(EMUMACHINE)/net_lib.o \
$(EMUMACHINE)/nmc9306.o \
$(EMUMACHINE)/nscsi_bus.o \
$(EMUMACHINE)/nscsi_cd.o \

757
src/emu/machine/net_lib.c Normal file
View File

@ -0,0 +1,757 @@
/***************************************************************************
netlib.c
Discrete netlist implementation.
****************************************************************************
Couriersud reserves the right to license the code under a less restrictive
license going forward.
Copyright Nicola Salmoria and the MAME team
All rights reserved.
Redistribution and use of this code or any derivative works are permitted
provided that the following conditions are met:
* Redistributions may not be sold, nor may they be used in a commercial
product or activity.
* Redistributions that are modified from the original source must include the
complete source code, including the source code for all components used by a
binary built from the modified sources. However, as a special exception, the
source code distributed need not include anything that is normally distributed
(in either source or binary form) with the major components (compiler, kernel,
and so on) of the operating system on which the executable runs, unless that
component itself accompanies the executable.
* Redistributions must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
****************************************************************************/
#include "net_lib.h"
NETLIB_START(netdev_const)
{
register_output("Q", &m_Q);
register_param("CONST", &m_const, 0.0);
}
NETLIB_UPDATE(netdev_const)
{
}
NETLIB_UPDATE_PARAM(netdev_const)
{
m_Q.setTo(m_const.ValueInt());
}
NETLIB_START(netdev_input)
{
register_output("Q", &m_Q);
}
NETLIB_UPDATE(netdev_input)
{
}
NETLIB_START(netdev_delay_lh)
{
register_input("CLK", &m_clk);
register_input("D", &m_D, NET_INP_TYPE_PASSIVE);
register_output("Q", &m_Q);
m_lastclk = nst_LOW;
m_Q.initial(1);
}
NETLIB_UPDATE(netdev_delay_lh)
{
UINT8 oclk = m_lastclk;
m_lastclk = INPVAL(m_clk);
if (!oclk && m_lastclk)
m_Q.setTo(INPVAL(m_D));
}
NETLIB_START(nicMultiSwitch)
{
static const char *sIN[8] = { "i1", "i2", "i3", "i4", "i5", "i6", "i7", "i8" };
int i;
m_position = 0;
m_low.initial(0);
for (i=0; i<8; i++)
{
register_input(sIN[i], &m_I[i]);
m_I[i].o = GETINPPTR(m_low);
}
register_param("POS", &m_POS);
register_output("Q", &m_Q);
m_variable_input_count = true;
}
NETLIB_UPDATE(nicMultiSwitch)
{
m_Q.setTo(INPVAL(m_I[m_position]));
}
NETLIB_UPDATE_PARAM(nicMultiSwitch)
{
m_position = m_POS.ValueInt();
update();
}
NETLIB_START(nicRSFF)
{
register_input("S", &m_S);
register_input("R", &m_R);
register_output("Q", &m_Q);
register_output("QQ", &m_QQ);
}
NETLIB_UPDATE(nicRSFF)
{
if (INPVAL(m_S))
{
m_Q.set();
m_QQ.clear();
}
else if (INPVAL(m_R))
{
m_Q.clear();
m_QQ.set();
}
}
NETLIB_START(nicNE555N_MSTABLE)
{
register_input("TRIG", &m_trigger);
register_output("Q", &m_Q);
register_param("R", &m_R);
register_param("C", &m_C);
register_param("VS", &m_VS, 5.0);
register_param("VL", &m_VL, 0.0 *5.0);
register_param("VT", &m_VT, 0.67*5.0);
m_Q.initial(0);
m_timer = m_setup->netlist().alloc_timer(this, 0);
}
NETLIB_UPDATE_PARAM(nicNE555N_MSTABLE)
{
double vt = m_VT.Value();
double vl = m_VL.Value();
if (vt > m_VS.Value()-0.7)
vt = m_VS.Value()-0.7;
if (vt < 1.4)
vt = 1.4;
if (vt<vl)
m_time = 0;
else
m_time = - log((m_VS.Value()-vt)/(m_VS.Value()-vl)) * m_R.Value() * m_C.Value();
}
NETLIB_UPDATE(nicNE555N_MSTABLE)
{
if (!m_Q.Q())
{
if (m_last && !INPVAL(m_trigger))
{
m_fired = 0;
m_timer->adjust_timer(m_time);
m_Q.set();
}
}
else if (m_fired)
{
if (INPVAL(m_trigger))
m_Q.clear();
}
m_last = INPVAL(m_trigger);
}
NETLIB_TIMER_CALLBACK(nicNE555N_MSTABLE)
{
if (INPVAL(m_trigger))
m_Q.clear();
m_fired = 1;
}
NETLIB_UPDATE(nic7400)
{
UINT8 t = (INPVAL(m_i[0]) & INPVAL(m_i[1])) ^ 1;
m_Q.setTo(t, t ? 22 : 15);
}
NETLIB_UPDATE(nic7402)
{
UINT8 t = (INPVAL(m_i[0]) | INPVAL(m_i[1])) ^ 1;
m_Q.setTo(t, t ? 22 : 15);
}
NETLIB_UPDATE(nic7404)
{
UINT8 t = (INPVAL(m_i[0])) ^ 1;
m_Q.setTo(t, t ? 22 : 15);
}
NETLIB_UPDATE(nic7410)
{
UINT8 t = (INPVAL(m_i[0]) & INPVAL(m_i[1]) & INPVAL(m_i[2])) ^ 1;
m_Q.setTo(t, t ? 22 : 15);
}
NETLIB_UPDATE(nic7420)
{
UINT8 t = (INPVAL(m_i[0]) & INPVAL(m_i[1]) & INPVAL(m_i[2]) & INPVAL(m_i[3])) ^ 1;
m_Q.setTo(t, t ? 22 : 15);
}
NETLIB_UPDATE(nic7425)
{
UINT8 t = (INPVAL(m_i[0]) | INPVAL(m_i[1]) | INPVAL(m_i[2]) | INPVAL(m_i[3])) ^ 1;
m_Q.setTo(t, t ? 22 : 15);
}
NETLIB_UPDATE(nic7427)
{
UINT8 t = (INPVAL(m_i[0]) | INPVAL(m_i[1]) | INPVAL(m_i[2])) ^ 1;
m_Q.setTo(t, t ? 22 : 15);
}
NETLIB_UPDATE(nic7430)
{
UINT8 t = (INPVAL(m_i[0]) & INPVAL(m_i[1]) & INPVAL(m_i[2]) & INPVAL(m_i[3]) & INPVAL(m_i[4]) & INPVAL(m_i[5]) & INPVAL(m_i[6]) & INPVAL(m_i[7])) ^ 1;
m_Q.setTo(t, t ? 22 : 15);
}
NETLIB_UPDATE(nic7486)
{
UINT8 t = INPVAL(m_i[0]) ^ INPVAL(m_i[1]);
m_Q.setTo(t, t ? 22 : 15 );
}
NETLIB_START(nic7448)
{
register_input("A0", &m_A0);
register_input("A1", &m_A1);
register_input("A2", &m_A2);
register_input("A3", &m_A3);
register_input("LTQ", &m_LTQ);
register_input("BIQ", &m_BIQ);
register_input("RBIQ",&m_RBIQ);
register_output("a", &m_a);
register_output("b", &m_b);
register_output("c", &m_c);
register_output("d", &m_d);
register_output("e", &m_e);
register_output("f", &m_f);
register_output("g", &m_g);
}
NETLIB_UPDATE(nic7448)
{
UINT8 v;
if (INPVAL(m_BIQ) && !INPVAL(m_LTQ))
v = 8;
else
{
v = (INPVAL(m_A0) << 0) | (INPVAL(m_A1) << 1) | (INPVAL(m_A2) << 2) | (INPVAL(m_A3) << 3);
if (!INPVAL(m_BIQ) || (!INPVAL(m_RBIQ) && (v==0)))
v = 15;
}
if (v != m_state)
{
m_a.setTo(tab7448[v][0]);
m_b.setTo(tab7448[v][1]);
m_c.setTo(tab7448[v][2]);
m_d.setTo(tab7448[v][3]);
m_e.setTo(tab7448[v][4]);
m_f.setTo(tab7448[v][5]);
m_g.setTo(tab7448[v][6]);
m_state = v;
}
}
const UINT8 nic7448::tab7448[16][7] =
{
{ 1, 1, 1, 1, 1, 1, 0 }, /* 00 - not blanked ! */
{ 0, 1, 1, 0, 0, 0, 0 }, /* 01 */
{ 1, 1, 0, 1, 1, 0, 1 }, /* 02 */
{ 1, 1, 1, 1, 0, 0, 1 }, /* 03 */
{ 0, 1, 1, 0, 0, 1, 1 }, /* 04 */
{ 1, 0, 1, 1, 0, 1, 1 }, /* 05 */
{ 0, 0, 1, 1, 1, 1, 1 }, /* 06 */
{ 1, 1, 1, 0, 0, 0, 0 }, /* 07 */
{ 1, 1, 1, 1, 1, 1, 1 }, /* 08 */
{ 1, 1, 1, 0, 0, 1, 1 }, /* 09 */
{ 0, 0, 0, 1, 1, 0, 1 }, /* 10 */
{ 0, 0, 1, 1, 0, 0, 1 }, /* 11 */
{ 0, 1, 0, 0, 0, 1, 1 }, /* 12 */
{ 1, 0, 0, 1, 0, 1, 1 }, /* 13 */
{ 0, 0, 0, 1, 1, 1, 1 }, /* 14 */
{ 0, 0, 0, 0, 0, 0, 0 }, /* 15 */
};
NETLIB_UPDATE(nic7450)
{
UINT8 t1 = INPVAL(m_i[0]) & INPVAL(m_i[1]);
UINT8 t2 = INPVAL(m_i[2]) & INPVAL(m_i[3]);
UINT8 t = (t1 | t2) ^ 1;
m_Q.setTo(t, t ? 22 : 15);
}
INLINE void nic7474_newstate(UINT8 state, net_output_t &Q, net_output_t &QQ)
{
if (state != Q.Q())
{
Q.setToNoCheck(state, state ? 40 : 25);
QQ.setToNoCheck(!state, !state ? 40 : 25);
}
}
NETLIB_UPDATE(nic7474)
{
net_sig_t old_clk = m_lastclk;
m_lastclk = INPVAL(m_clk);
if (UNEXPECTED(!INPVAL(m_preQ)))
nic7474_newstate(1, m_Q, m_QQ);
else if (UNEXPECTED(!INPVAL(m_clrQ)))
nic7474_newstate(0, m_Q, m_QQ);
else if (!old_clk && m_lastclk)
nic7474_newstate(INPVAL(m_D), m_Q, m_QQ);
}
NETLIB_START(nic7474)
{
m_lastclk = 0;
register_input("CLK", &m_clk);
register_input("D", &m_D, NET_INP_TYPE_PASSIVE);
register_input("CLRQ", &m_clrQ);
register_input("PREQ", &m_preQ);
register_output("Q", &m_Q);
register_output("QQ", &m_QQ);
m_Q.initial(0);
m_QQ.initial(1);
}
NETLIB_START(nic7483)
{
m_lastr = 0;
register_input("A1", &m_A1);
register_input("A2", &m_A2);
register_input("A3", &m_A3);
register_input("A4", &m_A4);
register_input("B1", &m_B1);
register_input("B2", &m_B2);
register_input("B3", &m_B3);
register_input("B4", &m_B4);
register_input("CI", &m_CI);
register_output("SA", &m_SA);
register_output("SB", &m_SB);
register_output("SC", &m_SC);
register_output("SD", &m_SD);
register_output("CO", &m_CO);
}
NETLIB_UPDATE(nic7483)
{
UINT8 a = (INPVAL(m_A1) << 0) | (INPVAL(m_A2) << 1) | (INPVAL(m_A3) << 2) | (INPVAL(m_A4) << 3);
UINT8 b = (INPVAL(m_B1) << 0) | (INPVAL(m_B2) << 1) | (INPVAL(m_B3) << 2) | (INPVAL(m_B4) << 3);
UINT8 r = a + b + INPVAL(m_CI);
if (r != m_lastr)
{
m_lastr = r;
m_SA.setTo((r >> 0) & 1);
m_SB.setTo((r >> 1) & 1);
m_SC.setTo((r >> 2) & 1);
m_SD.setTo((r >> 3) & 1);
m_CO.setTo((r >> 4) & 1);
}
}
NETLIB_START(nic7490)
{
m_lastclk = 0;
m_cnt = 0;
register_input("CLK", &m_clk);
register_input("R1", &m_R1);
register_input("R2", &m_R2);
register_input("R91", &m_R91);
register_input("R92", &m_R92);
register_output("QA", &m_QA);
register_output("QB", &m_QB);
register_output("QC", &m_QC);
register_output("QD", &m_QD);
}
NETLIB_UPDATE(nic7490)
{
UINT8 old_clk = m_lastclk;
m_lastclk = INPVAL(m_clk);
if (UNEXPECTED(INPVAL(m_R91) & INPVAL(m_R92)))
{
m_cnt = 9;
update_outputs();
}
else if (UNEXPECTED(INPVAL(m_R1) & INPVAL(m_R2)))
{
m_cnt = 0;
update_outputs();
}
else if (old_clk & !m_lastclk)
{
m_cnt++;
if (m_cnt >= 10)
m_cnt = 0;
update_outputs();
}
}
NETLIB_FUNC_VOID(nic7490, update_outputs)
{
m_QA.setTo((m_cnt >> 0) & 1);
m_QB.setTo((m_cnt >> 1) & 1);
m_QC.setTo((m_cnt >> 2) & 1);
m_QD.setTo((m_cnt >> 3) & 1);
}
NETLIB_START(nic7493)
{
m_lastclk = 0;
m_cnt = 0;
register_input("CLK", &m_clk);
register_input("R1", &m_R1);
register_input("R2", &m_R2);
register_output("QA", &m_QA);
register_output("QB", &m_QB);
register_output("QC", &m_QC);
register_output("QD", &m_QD);
}
NETLIB_UPDATE(nic7493)
{
UINT8 old_clk = m_lastclk;
m_lastclk = INPVAL(m_clk);
if (UNEXPECTED(INPVAL(m_R1) & INPVAL(m_R2)))
{
if (EXPECTED(m_cnt > 0))
{
m_cnt = 0;
m_QA.setTo(0, 40);
m_QB.setTo(0, 40);
m_QC.setTo(0, 40);
m_QD.setTo(0, 40);
}
}
else if (EXPECTED(old_clk & !m_lastclk))
{
m_cnt++;
m_cnt &= 0x0f;
update_outputs();
}
}
NETLIB_FUNC_VOID(nic7493, update_outputs)
{
if (m_cnt & 1)
m_QA.setToNoCheck(1, 16);
else
{
m_QA.setToNoCheck(0, 16);
switch (m_cnt)
{
case 0x00:
m_QB.setToNoCheck(0, 34);
m_QC.setToNoCheck(0, 48);
m_QD.setToNoCheck(0, 70);
break;
case 0x02:
case 0x06:
case 0x0A:
case 0x0E:
m_QB.setToNoCheck(1, 34);
break;
case 0x04:
case 0x0C:
m_QB.setToNoCheck(0, 34);
m_QC.setToNoCheck(1, 48);
break;
case 0x08:
m_QB.setToNoCheck(0, 34);
m_QC.setToNoCheck(0, 48);
m_QD.setToNoCheck(1, 70);
break;
}
}
}
NETLIB_START(nic74107A)
{
register_input("CLK", &m_clk);
register_input("J", &m_J, NET_INP_TYPE_PASSIVE);
register_input("K", &m_K, NET_INP_TYPE_PASSIVE);
register_input("CLRQ", &m_clrQ);
register_output("Q", &m_Q);
register_output("QQ", &m_QQ);
m_lastclk = 0;
m_Q.initial(0);
m_QQ.initial(1);
}
INLINE void nic74107A_newstate(UINT8 state, net_output_t &Q, net_output_t &QQ)
{
if (state != Q.Q())
{
Q.setToNoCheck(state, state ? 40 : 25);
QQ.setToNoCheck(!state, state ? 25 : 40);
}
}
NETLIB_UPDATE(nic74107A)
{
UINT8 oclk = m_lastclk;
m_lastclk = INPVAL(m_clk);
if (!INPVAL(m_clrQ))
nic74107A_newstate(0, m_Q, m_QQ);
else if (oclk & !m_lastclk)
{
if (EXPECTED(INPVAL(m_J) & INPVAL(m_K)))
nic74107A_newstate(!m_Q.Q(), m_Q, m_QQ);
else if (!INPVAL(m_J) & INPVAL(m_K))
nic74107A_newstate(0, m_Q, m_QQ);
else if (INPVAL(m_J) & !INPVAL(m_K))
nic74107A_newstate(1, m_Q, m_QQ);
}
}
NETLIB_START(nic74153)
{
register_input("A1", &m_I[0]);
register_input("A2", &m_I[1]);
register_input("A3", &m_I[2]);
register_input("A4", &m_I[3]);
register_input("A", &m_A);
register_input("B", &m_B);
register_input("GA", &m_GA);
register_output("AY", &m_AY);
}
NETLIB_UPDATE(nic74153)
{
if (!INPVAL(m_GA))
{
UINT8 chan = (INPVAL(m_A) | (INPVAL(m_B)<<1));
m_AY.setTo(INPVAL(m_I[chan]));
}
else
m_AY.clear();
}
NETLIB_START(nic9316)
{
m_lastclk = 0;
m_cnt = 0;
register_input("CLK", &m_clk);
register_input("ENP", &m_ENP, NET_INP_TYPE_PASSIVE);
register_input("ENT", &m_ENT);
register_input("CLRQ", &m_CLRQ);
register_input("LOADQ", &m_LOADQ, NET_INP_TYPE_PASSIVE);
register_input("A", &m_A, NET_INP_TYPE_PASSIVE);
register_input("B", &m_B, NET_INP_TYPE_PASSIVE);
register_input("C", &m_C, NET_INP_TYPE_PASSIVE);
register_input("D", &m_D, NET_INP_TYPE_PASSIVE);
register_output("QA", &m_QA);
register_output("QB", &m_QB);
register_output("QC", &m_QC);
register_output("QD", &m_QD);
register_output("RC", &m_RC);
}
NETLIB_UPDATE(nic9316)
{
UINT8 old_clk = m_lastclk;
m_lastclk = INPVAL(m_clk);
if (EXPECTED(INPVAL(m_CLRQ)))
{
if (EXPECTED(!old_clk & m_lastclk))
{
if (EXPECTED(INPVAL(m_LOADQ)))
{
if (EXPECTED(INPVAL(m_ENP) & INPVAL(m_ENT)))
{
m_cnt = ( m_cnt + 1) & 0x0f;
update_outputs();
}
}
else
{
m_cnt = (INPVAL(m_D) << 3) | (INPVAL(m_C) << 2) | (INPVAL(m_B) << 1) | (INPVAL(m_A) << 0);
update_outputs_all();
}
}
m_RC.setTo((INPVAL(m_ENT) & (m_cnt == 0x0f)), 20);
}
else if (m_cnt>0)
{
m_cnt = 0;
update_outputs();
m_RC.setTo(0, 20);
}
}
NETLIB_FUNC_VOID(nic9316, update_outputs_all)
{
m_QA.setTo((m_cnt >> 0) & 1, 20);
m_QB.setTo((m_cnt >> 1) & 1, 20);
m_QC.setTo((m_cnt >> 2) & 1, 20);
m_QD.setTo((m_cnt >> 3) & 1, 20);
}
NETLIB_FUNC_VOID(nic9316, update_outputs)
{
#if 0
m_QA.setTo((m_cnt >> 0) & 1, 20);
m_QB.setTo((m_cnt >> 1) & 1, 20);
m_QC.setTo((m_cnt >> 2) & 1, 20);
m_QD.setTo((m_cnt >> 3) & 1, 20);
#else
if (m_cnt & 1)
m_QA.setToNoCheck(1, 20);
else
{
m_QA.setToNoCheck(0, 20);
switch (m_cnt)
{
case 0x00:
m_QB.setToNoCheck(0, 20);
m_QC.setToNoCheck(0, 20);
m_QD.setToNoCheck(0, 20);
break;
case 0x02:
case 0x06:
case 0x0A:
case 0x0E:
m_QB.setToNoCheck(1, 20);
break;
case 0x04:
case 0x0C:
m_QB.setToNoCheck(0, 20);
m_QC.setToNoCheck(1, 20);
break;
case 0x08:
m_QB.setToNoCheck(0, 20);
m_QC.setToNoCheck(0, 20);
m_QD.setToNoCheck(1, 20);
break;
}
}
#endif
}
#define ENTRY(_nic, _name) new net_dev_t_factory< _nic >( # _name, # _nic ),
static net_dev_t_base_factory *netregistry[] =
{
ENTRY(netdev_const, NETDEV_CONST)
ENTRY(netdev_input, NETDEV_INPUT)
ENTRY(netdev_callback, NETDEV_CALLBACK)
ENTRY(nicMultiSwitch, NETDEV_SWITCH2)
ENTRY(netdev_delay_lh, NETDEV_DELAY_RAISE)
ENTRY(nicRSFF, NETDEV_RSFF)
ENTRY(nic7400, TTL_7400_NAND)
ENTRY(nic7402, TTL_7402_NOR)
ENTRY(nic7404, TTL_7404_INVERT)
ENTRY(nic7410, TTL_7410_NAND)
ENTRY(nic7420, TTL_7420_NAND)
ENTRY(nic7425, TTL_7425_NOR)
ENTRY(nic7427, TTL_7427_NOR)
ENTRY(nic7430, TTL_7430_NAND)
ENTRY(nic7450, TTL_7450_ANDORINVERT)
ENTRY(nic7486, TTL_7486_XOR)
ENTRY(nic7448, TTL_7448)
ENTRY(nic7474, TTL_7474)
ENTRY(nic7483, TTL_7483)
ENTRY(nic7490, TTL_7490)
ENTRY(nic7493, TTL_7493)
ENTRY(nic74107, TTL_74107)
ENTRY(nic74107A, TTL_74107A)
ENTRY(nic74153, TTL_74153)
ENTRY(nic9316, TTL_9316)
ENTRY(nicNE555N_MSTABLE, NE555N_MSTABLE)
NULL
};
net_dev_t *net_create_device_by_classname(const char *classname, netlist_setup_t *setup, const char *icname)
{
net_dev_t_base_factory **p = &netregistry[0];
while (p != NULL)
{
if (strcmp((*p)->classname(), classname) == 0)
return (*p)->Create(setup, icname);
p++;
}
fatalerror("Class %s required for IC %s not found!", classname, icname);
}
net_dev_t *net_create_device_by_name(const char *name, netlist_setup_t *setup, const char *icname)
{
net_dev_t_base_factory **p = &netregistry[0];
while (p != NULL)
{
if (strcmp((*p)->name(), name) == 0)
return (*p)->Create(setup, icname);
p++;
}
fatalerror("Class %s required for IC %s not found!", name, icname);
}

471
src/emu/machine/net_lib.h Normal file
View File

@ -0,0 +1,471 @@
/***************************************************************************
net_lib.h
Discrete netlist implementation.
****************************************************************************
Couriersud reserves the right to license the code under a less restrictive
license going forward.
Copyright Nicola Salmoria and the MAME team
All rights reserved.
Redistribution and use of this code or any derivative works are permitted
provided that the following conditions are met:
* Redistributions may not be sold, nor may they be used in a commercial
product or activity.
* Redistributions that are modified from the original source must include the
complete source code, including the source code for all components used by a
binary built from the modified sources. However, as a special exception, the
source code distributed need not include anything that is normally distributed
(in either source or binary form) with the major components (compiler, kernel,
and so on) of the operating system on which the executable runs, unless that
component itself accompanies the executable.
* Redistributions must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
****************************************************************************/
#ifndef NET_LIB_H
#define NET_LIB_H
#include "netlist.h"
// ----------------------------------------------------------------------------------------
// Special chips
// ----------------------------------------------------------------------------------------
#define NETDEV_CONST(_name, _v) \
NET_REGISTER_DEV(netdev_const, _name) \
NETDEV_PARAM(_name.CONST, _v) \
#define NETDEV_INPUT(_name) \
NET_REGISTER_DEV(netdev_input, _name) \
#define NETDEV_CALLBACK(_name, _IN) \
NET_REGISTER_DEV(netdev_callback, _name) \
NET_CONNECT(_name, IN, _IN) \
#define NETDEV_SWITCH2(_name, _i1, _i2) \
NET_REGISTER_DEV(nicMultiSwitch, _name) \
NET_CONNECT(_name, i1, _i1) \
NET_CONNECT(_name, i2, _i2) \
#define NETDEV_DELAY_RISE(_name, _CLK, _D) \
NET_REGISTER_DEV(netdev_delay_lh, _name) \
NET_CONNECT(_name, CLK, _CLK) \
NET_CONNECT(_name, D, _D) \
#define NETDEV_RSFF(_name, _S, _R) \
NET_REGISTER_DEV(nicRSFF, _name) \
NET_CONNECT(_name, S, _S) \
NET_CONNECT(_name, R, _R) \
// ----------------------------------------------------------------------------------------
// TTL Logic chips
// ----------------------------------------------------------------------------------------
#define TTL_7400_NAND(_name, _I1, _I2) \
NET_REGISTER_DEV(nic7400, _name) \
NET_CONNECT(_name, I1, _I1) \
NET_CONNECT(_name, I2, _I2) \
#define TTL_7402_NOR(_name, _I1, _I2) \
NET_REGISTER_DEV(nic7402, _name) \
NET_CONNECT(_name, I1, _I1) \
NET_CONNECT(_name, I2, _I2) \
#define TTL_7404_INVERT(_name, _I1) \
NET_REGISTER_DEV(nic7404, _name) \
NET_CONNECT(_name, I1, _I1) \
#define TTL_7410_NAND(_name, _I1, _I2, _I3) \
NET_REGISTER_DEV(nic7410, _name) \
NET_CONNECT(_name, I1, _I1) \
NET_CONNECT(_name, I2, _I2) \
NET_CONNECT(_name, I3, _I3) \
#define TTL_7420_NAND(_name, _I1, _I2, _I3, _I4) \
NET_REGISTER_DEV(nic7420, _name) \
NET_CONNECT(_name, I1, _I1) \
NET_CONNECT(_name, I2, _I2) \
NET_CONNECT(_name, I3, _I3) \
NET_CONNECT(_name, I4, _I4) \
#define TTL_7425_NOR(_name, _I1, _I2, _I3, _I4) \
NET_REGISTER_DEV(nic7425, _name) \
NET_CONNECT(_name, I1, _I1) \
NET_CONNECT(_name, I2, _I2) \
NET_CONNECT(_name, I3, _I3) \
NET_CONNECT(_name, I4, _I4) \
#define TTL_7427_NOR(_name, _I1, _I2, _I3) \
NET_REGISTER_DEV(nic7427, _name) \
NET_CONNECT(_name, I1, _I1) \
NET_CONNECT(_name, I2, _I2) \
NET_CONNECT(_name, I3, _I3) \
#define TTL_7430_NAND(_name, _I1, _I2, _I3, _I4, _I5, _I6, _I7, _I8) \
NET_REGISTER_DEV(nic7430, _name) \
NET_CONNECT(_name, I1, _I1) \
NET_CONNECT(_name, I2, _I2) \
NET_CONNECT(_name, I3, _I3) \
NET_CONNECT(_name, I4, _I4) \
NET_CONNECT(_name, I5, _I5) \
NET_CONNECT(_name, I6, _I6) \
NET_CONNECT(_name, I7, _I7) \
NET_CONNECT(_name, I8, _I8) \
#define TTL_7450_ANDORINVERT(_name, _I1, _I2, _I3, _I4) \
NET_REGISTER_DEV(nic7450, _name) \
NET_CONNECT(_name, I1, _I1) \
NET_CONNECT(_name, I2, _I2) \
NET_CONNECT(_name, I3, _I3) \
NET_CONNECT(_name, I4, _I4) \
#define TTL_7486_XOR(_name, _I1, _I2) \
NET_REGISTER_DEV(nic7486, _name) \
NET_CONNECT(_name, I1, _I1) \
NET_CONNECT(_name, I2, _I2) \
#define TTL_7448(_name, _A0, _A1, _A2, _A3, _LTQ, _BIQ, _RBIQ) \
NET_REGISTER_DEV(nic7448, _name) \
NET_CONNECT(_name, A0, _A0) \
NET_CONNECT(_name, A1, _A1) \
NET_CONNECT(_name, A2, _A2) \
NET_CONNECT(_name, A3, _A3) \
NET_CONNECT(_name, LTQ, _LTQ) \
NET_CONNECT(_name, BIQ, _BIQ) \
NET_CONNECT(_name, RBIQ, _RBIQ) \
#define TTL_7474(_name, _CLK, _D, _CLRQ, _PREQ) \
NET_REGISTER_DEV(nic7474, _name) \
NET_CONNECT(_name, CLK, _CLK) \
NET_CONNECT(_name, D, _D) \
NET_CONNECT(_name, CLRQ, _CLRQ) \
NET_CONNECT(_name, PREQ, _PREQ) \
#define TTL_7483(_name, _A1, _A2, _A3, _A4, _B1, _B2, _B3, _B4, _CI) \
NET_REGISTER_DEV(nic7483, _name) \
NET_CONNECT(_name, A1, _A1) \
NET_CONNECT(_name, A2, _A2) \
NET_CONNECT(_name, A3, _A3) \
NET_CONNECT(_name, A4, _A4) \
NET_CONNECT(_name, B1, _B1) \
NET_CONNECT(_name, B2, _B2) \
NET_CONNECT(_name, B3, _B3) \
NET_CONNECT(_name, B4, _B4) \
NET_CONNECT(_name, CI, _CI) \
#define TTL_7490(_name, _CLK, _R1, _R2, _R91, _R92) \
NET_REGISTER_DEV(nic7490, _name) \
NET_CONNECT(_name, CLK, _CLK) \
NET_CONNECT(_name, R1, _R1) \
NET_CONNECT(_name, R2, _R2) \
NET_CONNECT(_name, R91, _R91) \
NET_CONNECT(_name, R92, _R92) \
#define TTL_7493(_name, _CLK, _R1, _R2) \
NET_REGISTER_DEV(nic7493, _name) \
NET_CONNECT(_name, CLK, _CLK) \
NET_CONNECT(_name, R1, _R1) \
NET_CONNECT(_name, R2, _R2) \
#define TTL_74107A(_name, _CLK, _J, _K, _CLRQ) \
NET_REGISTER_DEV(nic74107A, _name) \
NET_CONNECT(_name, CLK, _CLK) \
NET_CONNECT(_name, J, _J) \
NET_CONNECT(_name, K, _K) \
NET_CONNECT(_name, CLRQ, _CLRQ) \
#define TTL_74107(_name, _CLK, _J, _K, _CLRQ) \
TTL_74107A(_name, _CLK, _J, _K, _CLRQ)
#define TTL_74153(_name, _A1, _A2, _A3, _A4, _A, _B, _GA) \
NET_REGISTER_DEV(nic74153, _name) \
NET_CONNECT(_name, A1, _A1) \
NET_CONNECT(_name, A2, _A2) \
NET_CONNECT(_name, A3, _A3) \
NET_CONNECT(_name, A4, _A4) \
NET_CONNECT(_name, A, _A) \
NET_CONNECT(_name, B, _B) \
NET_CONNECT(_name, GA, _GA) \
#define TTL_9316(_name, _CLK, _ENP, _ENT, _CLRQ, _LOADQ, _A, _B, _C, _D) \
NET_REGISTER_DEV(nic9316, _name) \
NET_CONNECT(_name, CLK, _CLK) \
NET_CONNECT(_name, ENP, _ENP) \
NET_CONNECT(_name, ENT, _ENT) \
NET_CONNECT(_name, CLRQ, _CLRQ) \
NET_CONNECT(_name, LOADQ,_LOADQ) \
NET_CONNECT(_name, A, _A) \
NET_CONNECT(_name, B, _B) \
NET_CONNECT(_name, C, _C) \
NET_CONNECT(_name, D, _D) \
#define NE555N_MSTABLE(_name, _TRIG) \
NET_REGISTER_DEV(nicNE555N_MSTABLE, _name) \
NET_CONNECT(_name, TRIG, _TRIG) \
// ----------------------------------------------------------------------------------------
// Special support devices ...
// ----------------------------------------------------------------------------------------
NETLIB_DEVICE_WITH_PARAMS(netdev_const,
net_output_t m_Q;
net_param_t m_const;
);
NETLIB_DEVICE(netdev_input,
net_output_t m_Q;
);
// ----------------------------------------------------------------------------------------
// Special devices ...
// ----------------------------------------------------------------------------------------
/* This class is an artificial delay circuit delaying a signal until the next low to high transition
* this is needed by pong to model delays in the hblank circuit
*/
NETLIB_DEVICE(netdev_delay_lh,
net_input_t m_clk;
net_input_t m_D;
net_sig_t m_lastclk;
net_output_t m_Q;
);
NETLIB_DEVICE_WITH_PARAMS(nicMultiSwitch,
net_input_t m_I[8];
net_output_t m_Q;
net_output_t m_low;
net_param_t m_POS;
int m_position;
);
NETLIB_DEVICE(nicRSFF,
net_input_t m_S;
net_input_t m_R;
net_output_t m_Q;
net_output_t m_QQ;
);
// ----------------------------------------------------------------------------------------
// Standard devices ...
// ----------------------------------------------------------------------------------------
NETLIB_DEVICE_WITH_PARAMS(nicNE555N_MSTABLE,
ATTR_HOT void timer_cb(INT32 timer_id);
net_input_t m_trigger;
UINT8 m_last;
UINT8 m_fired;
double m_time;
net_output_t m_Q;
netlist_base_timer_t *m_timer;
net_param_t m_R;
net_param_t m_C;
net_param_t m_VS;
net_param_t m_VL;
net_param_t m_VT;
);
NETLIB_SIGNAL(nic7400, 2)
NETLIB_SIGNAL(nic7402, 2)
NETLIB_SIGNAL(nic7404, 1)
NETLIB_SIGNAL(nic7410, 3)
NETLIB_SIGNAL(nic7420, 4)
NETLIB_SIGNAL(nic7425, 4)
NETLIB_SIGNAL(nic7427, 3)
NETLIB_SIGNAL(nic7430, 8)
NETLIB_SIGNAL(nic7450, 4)
NETLIB_DEVICE(nic7474,
net_input_t m_clk;
net_input_t m_D;
net_input_t m_clrQ;
net_input_t m_preQ;
net_sig_t m_lastclk;
net_output_t m_Q;
net_output_t m_QQ;
);
NETLIB_SIGNAL(nic7486, 2)
/* 74107 does latch data during high !
* For modelling purposes, we assume 74107 and 74107A are the same
*/
NETLIB_DEVICE(nic74107A,
net_input_t m_clk;
net_input_t m_J;
net_input_t m_K;
net_input_t m_clrQ;
net_sig_t m_lastclk;
net_output_t m_Q;
net_output_t m_QQ;
);
class nic74107 : public nic74107A
{
public:
nic74107(netlist_setup_t *parent, const char *name)
: nic74107A(parent, name) {}
};
NETLIB_DEVICE(nic7493,
ATTR_HOT void update_outputs();
net_input_t m_R1;
net_input_t m_R2;
net_input_t m_clk;
UINT8 m_lastclk;
UINT8 m_cnt;
net_output_t m_QA;
net_output_t m_QB;
net_output_t m_QC;
net_output_t m_QD;
);
NETLIB_DEVICE(nic7490,
ATTR_HOT void update_outputs();
net_input_t m_R1;
net_input_t m_R2;
net_input_t m_R91;
net_input_t m_R92;
net_input_t m_clk;
net_sig_t m_lastclk;
UINT8 m_cnt;
net_output_t m_QA;
net_output_t m_QB;
net_output_t m_QC;
net_output_t m_QD;
);
/* ripple-carry counter on low-high clock transition */
NETLIB_DEVICE(nic9316,
ATTR_HOT void update_outputs_all();
ATTR_HOT void update_outputs();
net_input_t m_clk;
net_input_t m_ENP;
net_input_t m_ENT;
net_input_t m_CLRQ;
net_input_t m_LOADQ;
net_input_t m_A;
net_input_t m_B;
net_input_t m_C;
net_input_t m_D;
UINT8 m_lastclk;
UINT8 m_cnt;
net_output_t m_QA;
net_output_t m_QB;
net_output_t m_QC;
net_output_t m_QD;
net_output_t m_RC;
);
NETLIB_DEVICE(nic7483,
net_input_t m_CI;
net_input_t m_A1;
net_input_t m_A2;
net_input_t m_A3;
net_input_t m_A4;
net_input_t m_B1;
net_input_t m_B2;
net_input_t m_B3;
net_input_t m_B4;
net_input_t m_clk;
UINT8 m_lastr;
net_output_t m_SA;
net_output_t m_SB;
net_output_t m_SC;
net_output_t m_SD;
net_output_t m_CO;
);
/* one half of a nic74153 */
NETLIB_DEVICE(nic74153,
net_input_t m_I[4];
net_input_t m_A;
net_input_t m_B;
net_input_t m_GA;
net_output_t m_AY;
);
NETLIB_DEVICE(nic7448,
static const UINT8 tab7448[16][7];
net_input_t m_A0;
net_input_t m_A1;
net_input_t m_A2;
net_input_t m_A3;
net_input_t m_LTQ;
net_input_t m_RBIQ;
net_input_t m_BIQ;
UINT8 m_state;
net_output_t m_a;
net_output_t m_b;
net_output_t m_c;
net_output_t m_d;
net_output_t m_e;
net_output_t m_f;
net_output_t m_g;
);
#endif

798
src/emu/machine/netlist.c Normal file
View File

@ -0,0 +1,798 @@
/***************************************************************************
netlist.c
Discrete netlist implementation.
****************************************************************************
Couriersud reserves the right to license the code under a less restrictive
license going forward.
Copyright Nicola Salmoria and the MAME team
All rights reserved.
Redistribution and use of this code or any derivative works are permitted
provided that the following conditions are met:
* Redistributions may not be sold, nor may they be used in a commercial
product or activity.
* Redistributions that are modified from the original source must include the
complete source code, including the source code for all components used by a
binary built from the modified sources. However, as a special exception, the
source code distributed need not include anything that is normally distributed
(in either source or binary form) with the major components (compiler, kernel,
and so on) of the operating system on which the executable runs, unless that
component itself accompanies the executable.
* Redistributions must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
****************************************************************************/
#include "netlist.h"
//============================================================
// DEBUGGING
//============================================================
#define VERBOSE (0)
#define KEEP_STATISTICS (0)
#if KEEP_STATISTICS && USE_DELEGATES
#error "Statistics only work without delegates!"
#endif
#if (VERBOSE)
#define VERBOSE_OUT(x) printf x
#else
#define VERBOSE_OUT(x)
#endif
//============================================================
// MACROS
//============================================================
#if KEEP_STATISTICS
#define add_to_stat(v,x) do { atomic_add32((v), (x)); } while (0)
#define inc_stat(v) add_to_stat(v, 1)
#define begin_timing(v) do { (v) -= get_profile_ticks(); } while (0)
#define end_timing(v) do { (v) += get_profile_ticks(); } while (0)
#else
#define add_to_stat(v,x) do { } while (0)
#define inc_stat(v) add_to_stat(v, 1)
#define begin_timing(v) do { } while (0)
#define end_timing(v) do { } while (0)
#endif
// ----------------------------------------------------------------------------------------
// A netlist parser
// ----------------------------------------------------------------------------------------
class netlist_parser
{
public:
netlist_parser(netlist_setup_t &setup)
: m_setup(setup) {}
void parse(char *buf)
{
m_p = buf;
while (*m_p)
{
char *n;
skipws();
if (!*m_p) break;
n = getname('(');
VERBOSE_OUT(("Parser: Device: %s\n", n));
if (strcmp(n,"NET_ALIAS") == 0)
{
char *alias;
char *out;
m_p++;
skipws();
alias = getname(',');
m_p++;
skipws();
out = getname(')');
m_p++;
VERBOSE_OUT(("Parser: Alias: %s %s\n", alias, out));
m_setup.register_alias(alias, out);
}
else if (strcmp(n,"NETDEV_PARAM") == 0)
{
char *param;
double val;
m_p++;
skipws();
param = getname(',');
m_p++;
skipws();
val = eval_param();
m_p++;
VERBOSE_OUT(("Parser: Param: %s %f\n", param, val));
m_setup.find_param(param)->setTo(val);
}
else if (strcmp(n,"NETDEV_CONST") == 0)
{
char *devname;
net_dev_t *dev;
char paramfq[30];
double val;
m_p++;
skipws();
devname = getname(',');
dev = net_create_device_by_name(n, &m_setup, devname);
m_setup.register_dev(dev);
m_p++;
skipws();
val = eval_param();
m_p++;
strcpy(paramfq, devname);
strcat(paramfq, ".CONST");
VERBOSE_OUT(("Parser: Const: %s %f\n", devname, val));
m_setup.find_param(paramfq)->setTo(val);
}
else
{
char *devname;
net_dev_t *dev;
int cnt;
m_p++;
skipws();
devname = getname2(',', ')');
dev = net_create_device_by_name(n, &m_setup, devname);
m_setup.register_dev(dev);
skipws();
VERBOSE_OUT(("Parser: IC: %s\n", n));
cnt = 0;
while (*m_p != ')')
{
m_p++;
skipws();
n = getname2(',', ')');
VERBOSE_OUT(("Parser: ID: %s %s\n", n, dev->m_inputs.item(cnt)));
m_setup.register_link(dev->m_inputs.item(cnt), n);
skipws();
cnt++;
//return 0;
}
if (cnt != dev->m_inputs.count() && !dev->variable_input_count())
fatalerror("netlist: input count mismatch for %s - expected %d found %d\n", devname, dev->m_inputs.count(), cnt);
m_p++;
}
}
}
private:
void skipeol()
{
while (*m_p)
{
if (*m_p == 10)
{
m_p++;
return;
}
m_p++;
}
}
void skipws()
{
while (*m_p)
{
switch (*m_p)
{
case ' ':
case 9:
case 10:
case 13:
m_p++;
break;
case '/':
if (*(m_p+1) == '/')
skipeol();
break;
default:
return;
}
}
}
char *getname(char sep)
{
static char buf[30];
char *p1 = buf;
while (*m_p != sep)
*p1++ = *m_p++;
*p1 = 0;
return core_strdup(buf);
}
char *getname2(char sep1, char sep2)
{
static char buf[30];
char *p1 = buf;
while ((*m_p != sep1) && (*m_p != sep2))
*p1++ = *m_p++;
*p1 = 0;
return core_strdup(buf);
}
double eval_param()
{
static const char *macs[6] = {"", "RES_K(", "RES_M(", "CAP_U(", "CAP_N(", "CAP_P("};
static double facs[6] = {1, 1e3, 1e6, 1e-6, 1e-9, 1e-12};
int i;
int f=0;
char *e;
double ret;
char *s = m_p;
for (i=1; i<6;i++)
if (strncmp(s, macs[i], strlen(macs[i])) == 0)
f = i;
ret = strtod(s+strlen(macs[f]), &e);
if ((f>0) && (*e != ')'))
exit(0);
if (f>0)
e++;
m_p = e;
return ret * facs[f];
}
char * m_p;
netlist_setup_t &m_setup;
};
// ----------------------------------------------------------------------------------------
// netlist_base_t
// ----------------------------------------------------------------------------------------
netlist_base_t::netlist_base_t(bool sub_cycle_exact)
//m_output_list(ttl_list_t<output_t *>(2048)),
: m_current(0), m_divisor(32), m_sub_cycle_exact(sub_cycle_exact), m_gatedelay(100), m_clockfreq(1000000)
{
m_netlist_mask = NET_LIST_MASK;
reset_lists();
}
netlist_base_t::~netlist_base_t()
{
}
void netlist_base_t::set_clock_freq(int clockfreq)
{
m_clockfreq = clockfreq;
m_divisor = 1000000000L * 100L / (m_clockfreq) / m_gatedelay;
VERBOSE_OUT(("Divisor %d\n", m_divisor));
}
void netlist_base_t::set_gatedelay(int gatedelay)
{
m_gatedelay = gatedelay;
m_divisor = 1000000000L * 100L / (m_clockfreq) / m_gatedelay;
VERBOSE_OUT(("Divisor %d\n", m_divisor));
}
ATTR_HOT inline void netlist_base_t::process_list(void)
{
net_list_t<net_output_t *, 512> &list = m_output_list[m_sub_cycle_exact ? m_current : 0];
#if 0
net_output_t * RESTRICT * RESTRICT first;
net_output_t * RESTRICT * RESTRICT last;
first = list.first();
last = list.last();
while (first <= last)
{
net_output_t * RESTRICT * RESTRICT out = first;
while (out <= last)
{
(*out)->update_out();
(*out)->update_devs();
out++;
}
first = last + 1;
last = list.last();
}
#else
net_output_t * RESTRICT * RESTRICT out;
out = list.first();
while (out <= list.last())
{
(*out)->update_out();
(*out)->update_devs();
out++;
}
#endif
reset_list();
if (m_sub_cycle_exact)
m_current = (m_current + 1) & m_netlist_mask;
}
// ----------------------------------------------------------------------------------------
// netlist_setup_t
// ----------------------------------------------------------------------------------------
netlist_setup_t::netlist_setup_t(netlist_base_t &netlist)
//m_output_list(ttl_list_t<output_t *>(2048)),
: m_netlist(netlist)
{
}
netlist_setup_t::~netlist_setup_t()
{
}
net_dev_t *netlist_setup_t::register_dev(net_dev_t *dev)
{
if (!(m_devices.add(dev->name(), dev, false)==TMERR_NONE))
fatalerror("Error adding %s to device list\n", dev->name());
dev->start();
return dev;
}
template <class T>
static void remove_start_with(T &hm, astring &sw)
{
typename T::entry_t *entry = hm.first();
while (entry != NULL)
{
typename T::entry_t *next = hm.next(entry);
if (sw.cmpsubstr(entry->tag(), 0, sw.len()) == 0)
{
VERBOSE_OUT(("removing %s\n", entry->tag().cstr()));
hm.remove(entry->object());
}
entry = next;
}
}
void netlist_setup_t::remove_dev(const char *name)
{
net_dev_t *dev = m_devices.find(name);
astring temp = name;
if (dev == NULL)
fatalerror("Device %s does not exist\n", name);
temp.cat(".");
remove_start_with<tagmap_input_t>(m_inputs, temp);
remove_start_with<tagmap_output_t>(m_outputs, temp);
remove_start_with<tagmap_param_t>(m_params, temp);
remove_start_with<tagmap_astring_t>(m_links, temp);
m_devices.remove(name);
}
void netlist_setup_t::register_callback(const char *devname, net_output_delegate delegate)
{
netdev_callback *dev = (netdev_callback *) m_devices.find(devname);
if (dev == NULL)
fatalerror("did not find device %s\n", devname);
dev->register_callback(delegate);
}
void netlist_setup_t::register_alias(const char *alias, const char *out)
{
if (!(m_alias.add(alias, new astring(out), false)==TMERR_NONE))
fatalerror("Error adding alias %s to alias list\n", alias);
}
void netlist_setup_t::register_output(const char *name, net_output_t *out)
{
VERBOSE_OUT(("out %s\n", name));
if (!(m_outputs.add(name, out, false)==TMERR_NONE))
fatalerror("Error adding output %s to output list\n", name);
}
void netlist_setup_t::register_input(const char *name, net_input_t *inp, net_input_type type)
{
net_input_setup_t *setup_inp = new net_input_setup_t(inp, type);
VERBOSE_OUT(("input %s\n", name));
if (!(m_inputs.add(name, setup_inp, false) == TMERR_NONE))
fatalerror("Error adding input %s to input list\n", name);
}
void netlist_setup_t::register_link(const char *sin, const char *sout)
{
VERBOSE_OUT(("link %s <== %s\n", sin, sout));
if (!(m_links.add(sin, new astring(sout), false)==TMERR_NONE))
fatalerror("Error adding link %s<==%s to link list\n", sin, sout);
}
void netlist_setup_t::register_param(const char *sname, net_param_t *param)
{
astring temp = param->ttl_dev()->name();
temp.cat(".");
temp.cat(sname);
if (!(m_params.add(temp, param, false)==TMERR_NONE))
fatalerror("Error adding parameter %s to parameter list\n", sname);
}
const char *netlist_setup_t::resolve_alias(const char *name) const
{
const astring *ret = m_alias.find(name);
if (ret != NULL)
return ret->cstr();
return name;
}
net_output_t *netlist_setup_t::find_output_exact(const char *outname_in)
{
net_output_t *ret = m_outputs.find(outname_in);
return ret;
}
net_output_t *netlist_setup_t::find_output(const char *outname_in)
{
const char *outname = resolve_alias(outname_in);
net_output_t *ret;
ret = find_output_exact(outname);
/* look for default */
if (ret == NULL)
{
/* look for ".Q" std output */
astring s = outname;
s.cat(".Q");
ret = find_output_exact(s);
}
if (ret == NULL)
fatalerror("output %s(%s) not found!", outname_in, outname);
VERBOSE_OUT(("Found input %s\n", outname));
return ret;
}
net_param_t *netlist_setup_t::find_param(const char *param_in)
{
const char *outname = resolve_alias(param_in);
net_param_t *ret;
ret = m_params.find(outname);
if (ret == NULL)
fatalerror("parameter %s(%s) not found!", param_in, outname);
VERBOSE_OUT(("Found parameter %s\n", outname));
return ret;
}
void netlist_setup_t::resolve_inputs(void)
{
VERBOSE_OUT(("Resolving ...\n"));
for (tagmap_astring_t::entry_t *entry = m_links.first(); entry != NULL; entry = m_links.next(entry))
{
const astring *sout = entry->object();
astring sin = entry->tag();
net_input_setup_t *in = m_inputs.find(sin);
int p = sin.find(".");
const char *devname = sin.substr(0, p);
net_dev_t *dev = m_devices.find(devname);
net_output_t *out = find_output(sout->cstr() );
(*in->inp()).o = GETINPPTR(*out);
//in->inp()->v = out->Q_ptr();
if (in->type() == NET_INP_TYPE_ACTIVE)
out->register_con(dev);
}
step_all_devices();
//reset_list();
//process_list();
}
void netlist_setup_t::step_all_devices()
{
m_netlist.reset_list();
for (tagmap_devices_t::entry_t *entry = m_devices.first(); entry != NULL; entry = m_devices.next(entry))
{
net_dev_t &dev = *entry->object();
dev.update_param();
dev.update();
for (tagmap_output_t::entry_t *entry = m_outputs.first(); entry != NULL; entry = m_outputs.next(entry))
{
net_output_t &out = *entry->object();
out.update_out();
}
m_netlist.reset_lists();
}
}
void netlist_setup_t::parse(char *buf)
{
netlist_parser parser(*this);
parser.parse(buf);
}
void netlist_setup_t::print_stats()
{
if (KEEP_STATISTICS)
{
for (netlist_setup_t::tagmap_devices_t::entry_t *entry = m_devices.first(); entry != NULL; entry = m_devices.next(entry))
{
printf("Device %20s : %12d %15ld\n", entry->object()->name(), entry->object()->stat_count, (long int) entry->object()->total_time / (entry->object()->stat_count + 1));
}
}
}
// ----------------------------------------------------------------------------------------
// net_dev_t
// ----------------------------------------------------------------------------------------
net_dev_t::~net_dev_t()
{
}
ATTR_HOT void net_dev_t::update_timed()
{
inc_stat(&stat_count);
begin_timing(total_time);
update();
end_timing(total_time);
}
void net_dev_t::register_output(const char *name, net_output_t *port)
{
astring temp = this->name();
temp.cat(".");
temp.cat(name);
port->set_ttl_dev(this);
m_setup->register_output(temp, port);
}
void net_dev_t::register_input(const char *name, net_input_t *inp, net_input_type type)
{
astring temp = this->name();
temp.cat(".");
temp.cat(name);
m_inputs.add(core_strdup(temp.cstr()));
m_setup->register_input(temp, inp, type);
}
void net_dev_t::register_param(const char *name, net_param_t *param, double initialVal)
{
param->set_ttl_dev(this);
param->initial(initialVal);
m_setup->register_param(name, param);
}
// ----------------------------------------------------------------------------------------
// net_output_t
// ----------------------------------------------------------------------------------------
net_output_t::net_output_t()
{
#if USE_DELEGATES
//m_cons = global_alloc_array_clear(net_update_delegate, 32);
#else
// m_cons = global_alloc_array_clear(net_dev_t *, 32);
#endif
//m_Q = parent.alloc_sig();
//m_new_Q = parent.alloc_sig();
m_num_cons = 0;
m_Q = 0;
m_new_Q = m_Q;
}
ATTR_COLD void net_output_t::set_ttl_dev(net_dev_t *dev)
{
m_ttldev = dev;
m_netlist = &dev->setup()->netlist();
}
ATTR_COLD void net_output_t::register_con(net_dev_t *dev)
{
assert(m_num_cons<32);
#if USE_DELEGATES
net_update_delegate aDelegate = net_update_delegate(&net_dev_t::update, "update", dev);
for (int i=0; i < m_num_cons; i++)
if (m_cons[i] == aDelegate)
return;
m_cons[m_num_cons++] = aDelegate;
#else
for (int i=0; i < m_num_cons; i++)
if (m_cons[i] == dev)
return;
m_cons[m_num_cons++] = dev;
#endif
}
ATTR_HOT inline void net_output_t::update_devs()
{
#if USE_DELEGATES
net_update_delegate *s = m_cons;
net_update_delegate *e = s + m_num_cons;
while (s<e)
{
(*s++)();
}
#else
net_dev_t **s = m_cons;
net_dev_t **e = s + m_num_cons;
#if KEEP_STATISTICS
while (s<e)
(*s++)->update_timed();
#else
while (s<e)
(*s++)->update();
#endif
#endif
}
NETLIB_UPDATE(netdev_callback)
{
// FIXME: Remove after device cleanup
if (!m_callback.isnull())
m_callback(INPVAL(m_in));
}
// ----------------------------------------------------------------------------------------
// netlist_mame_device
// ----------------------------------------------------------------------------------------
const device_type NETLIST = &device_creator<netlist_mame_device>;
netlist_mame_device::netlist_mame_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
: device_t(mconfig, NETLIST, "netlist", tag, owner, clock),
device_execute_interface(mconfig, *this)
{
m_clock = clock;
}
void netlist_mame_device::static_set_subcycles(device_t &device, int subcycles)
{
netlist_mame_device &netlist = downcast<netlist_mame_device &>(device);
assert((subcycles & 1) == 0);
assert( subcycles > 0);
netlist.m_subcycles = subcycles;
}
void netlist_mame_device::static_set_constructor(device_t &device, void (*setup_func)(netlist_setup_t &))
{
netlist_mame_device &netlist = downcast<netlist_mame_device &>(device);
netlist.m_setup_func = setup_func;
}
void netlist_mame_device::device_config_complete()
{
}
void netlist_mame_device::device_start()
{
//double dt = clocks_to_attotime(1).as_double();
m_netlist = global_alloc_clear(netlist_t(*this, m_subcycles > 2));
m_netlist->set_clock_freq(m_clock * m_subcycles);
m_setup = global_alloc_clear(netlist_setup_t(*m_netlist));
m_setup_func(*m_setup);
bool allok = true;
for (on_device_start **ods = m_device_start_list.first(); ods <= m_device_start_list.last(); ods++)
allok &= (*ods)->OnDeviceStart();
if (!allok)
fatalerror("required elements not found\n");
m_setup->resolve_inputs();
//m_clockcnt = 0;
m_clock_input = m_setup->find_output("clk");
//save_item(NAME(m_clockcnt));
save_state();
/* TODO: we have to save the round robin queue as well */
// set our instruction counter
m_icountptr = &m_icount;
m_ss = SubCycles() / 2;
m_clk = 0;
}
void netlist_mame_device::device_reset()
{
}
void netlist_mame_device::device_stop()
{
m_setup->print_stats();
}
void netlist_mame_device::device_post_load()
{
}
void netlist_mame_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
}
void netlist_mame_device::save_state()
{
for (netlist_setup_t::tagmap_output_t::entry_t *entry = m_setup->m_outputs.first(); entry != NULL; entry = m_setup->m_outputs.next(entry))
{
save_item(*entry->object()->Q_ptr(), entry->tag().cstr(), 0);
save_item(*entry->object()->new_Q_ptr(), entry->tag().cstr(), 1);
}
}
UINT64 netlist_mame_device::execute_clocks_to_cycles(UINT64 clocks) const
{
return clocks * SubCycles();
}
UINT64 netlist_mame_device::execute_cycles_to_clocks(UINT64 cycles) const
{
return cycles / SubCycles();
}
ATTR_HOT void netlist_mame_device::execute_run()
{
//bool check_debugger = ((device_t::machine().debug_flags & DEBUG_FLAG_ENABLED) != 0);
osd_ticks_t a = -osd_ticks();
UINT8 ssdiv2 = (SubCycles() / 2);
//int p = m_icount;
do
{
// debugging
//m_ppc = m_pc; // copy PC to previous PC
//if (check_debugger)
// debugger_instruction_hook(this, 0); //m_pc);
if (--m_ss == 0)
{
m_ss = ssdiv2;
m_clk = !m_clk;
m_clock_input->setTo(m_clk);
}
m_netlist->process_list();
m_icount--;
} while (m_icount > 0);
a+=osd_ticks();
//printf("%ld %d %ld\n", (long) a, p, (long) (a * 1000 / p));
}

870
src/emu/machine/netlist.h Normal file
View File

@ -0,0 +1,870 @@
/***************************************************************************
netlist.h
Discrete netlist implementation.
****************************************************************************
Couriersud reserves the right to license the code under a less restrictive
license going forward.
Copyright Nicola Salmoria and the MAME team
All rights reserved.
Redistribution and use of this code or any derivative works are permitted
provided that the following conditions are met:
* Redistributions may not be sold, nor may they be used in a commercial
product or activity.
* Redistributions that are modified from the original source must include the
complete source code, including the source code for all components used by a
binary built from the modified sources. However, as a special exception, the
source code distributed need not include anything that is normally distributed
(in either source or binary form) with the major components (compiler, kernel,
and so on) of the operating system on which the executable runs, unless that
component itself accompanies the executable.
* Redistributions must reproduce the above copyright notice, this list of
conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
****************************************************************************/
#ifndef NETLIST_H
#define NETLIST_H
#include "emu.h"
#include "tagmap.h"
//============================================================
// SETUP
//============================================================
#define USE_DELEGATES (1)
#define USE_DOUBLE (0)
#define NET_LIST_SIZE (32)
#define NET_LIST_MASK (0x1f)
//============================================================
// MACROS / inline netlist definitions
//============================================================
#define NET_ALIAS(_alias, _name) \
netlist.register_alias(# _alias, # _name); \
#define NET_NEW(_type , _name) net_create_device_by_classname(# _type, &netlist, # _name)
#define NET_REGISTER_DEV(_type, _name) \
netlist.register_dev(NET_NEW(_type, _name)); \
#define NET_REMOVE_DEV(_name) \
netlist.remove_dev(# _name); \
#define NET_REGISTER_SIGNAL(_type, _name) \
NET_REGISTER_DEV(_type ## _ ## sig, _name) \
#define NET_CONNECT(_name, _input, _output) \
netlist.register_link(# _name "." # _input, # _output); \
#define NETDEV_PARAM(_name, _val) \
netlist.find_param(# _name)->setTo(_val);
#define NETLIST_NAME(_name) netlist ## _ ## _name
#define NETLIST_START(_name) \
ATTR_COLD void NETLIST_NAME(_name)(netlist_setup_t &netlist) \
{ \
#define NETLIST_END }
#define NETLIST_INCLUDE(_name) \
NETLIST_NAME(_name)(netlist); \
#define NETLIST_MEMREGION(_name) \
netlist.parse((char *)downcast<netlist_t &>(netlist.netlist()).machine().root_device().memregion(_name)->base()); \
#if defined(__GNUC__) && (__GNUC__ >= 3)
#define ATTR_ALIGN __attribute__ ((aligned(64)))
#else
#define ATTR_ALIGN
#endif
//============================================================
// MACROS / netlist devices
//============================================================
#define NETLIB_UPDATE(_chip) ATTR_HOT ATTR_ALIGN void _chip :: update(void)
#define NETLIB_START(_chip) ATTR_COLD ATTR_ALIGN void _chip :: start(void)
#define NETLIB_UPDATE_PARAM(_chip) ATTR_HOT ATTR_ALIGN void _chip :: update_param(void)
#define NETLIB_FUNC_VOID(_chip, _name) ATTR_HOT ATTR_ALIGN inline void _chip :: _name (void)
#define NETLIB_TIMER_CALLBACK(_chip) ATTR_HOT void _chip :: timer_cb(INT32 timer_id)
#define NETLIB_SIGNAL(_name, _num_input) \
class _name : public net_signal_t< _num_input > \
{ \
public: \
_name (netlist_setup_t *parent, const char *name) \
: net_signal_t(parent, name) { } \
ATTR_HOT void update(); \
}; \
#define NETLIB_DEVICE(_name, _priv) \
class _name : public net_dev_t \
{ \
public: \
_name (netlist_setup_t *parent, const char *name) \
: net_dev_t(parent, name) { } \
ATTR_HOT void update(); \
ATTR_COLD void start(); \
private: \
_priv \
} \
#define NETLIB_DEVICE_WITH_PARAMS(_name, _priv) \
class _name : public net_dev_t \
{ \
public: \
_name (netlist_setup_t *parent, const char *name) \
: net_dev_t(parent, name) { } \
ATTR_HOT void update_param(); \
ATTR_HOT void update(); \
ATTR_COLD void start(); \
private: \
_priv \
} \
// MAME specific
#define MCFG_NETLIST_ADD(_tag, _clock, _setup, _subcycles) \
MCFG_DEVICE_ADD(_tag, NETLIST, _clock) \
MCFG_NETLIST_SUBCYCLES(_subcycles) \
MCFG_NETLIST_SETUP(_setup) \
#define MCFG_NETLIST_REPLACE(_tag, _clock, _setup, _subcycles) \
MCFG_DEVICE_REPLACE(_tag, NETLIST, _clock) \
MCFG_NETLIST_SUBCYCLES(_subcycles) \
MCFG_NETLIST_SETUP(_setup) \
#define MCFG_NETLIST_SUBCYCLES(_subcycles) \
netlist_mame_device::static_set_subcycles(*device, _subcycles); \
#define MCFG_NETLIST_SETUP(_setup) \
netlist_mame_device::static_set_constructor(*device, NETLIST_NAME(_setup)); \
// ----------------------------------------------------------------------------------------
// Type definitions
// ----------------------------------------------------------------------------------------
enum net_input_type {
NET_INP_TYPE_PASSIVE,
NET_INP_TYPE_ACTIVE
};
enum { nst_LOW = 0, nst_HIGH = 1};
//typedef double net_sig_t;
//enum net_sig_t { nst_LOW = 0, nst_HIGH = 1};
// ----------------------------------------------------------------------------------------
// net_input_t
// ----------------------------------------------------------------------------------------
class net_output_t;
#if !USE_DOUBLE
#if 0
typedef UINT8 net_sig_t;
#define INPVAL(_x) (*(_x))
#define GETINPPTR(_x) (_x).Q_ptr()
typedef net_sig_t * RESTRICT net_input_t;
#else
typedef UINT8 net_sig_t;
//#define INPVAL(_x) ((_x)->Q())
#define INPVAL(_x) INP(_x)
#define GETINPPTR(_x) (&(_x))
class net_input_t {
public:
net_output_t * RESTRICT o;
};
#endif
#else
typedef double net_sig_t;
#define INPVAL(_x) ((int)*(_x))
#define GETINPPTR(_x) (_x).Q_ptr()
typedef net_sig_t * RESTRICT net_input_t;
#endif
typedef delegate<void (const net_sig_t)> net_output_delegate;
#if USE_DELEGATES
typedef delegate<void ()> net_update_delegate;
typedef delegate<void (const net_output_t *out, UINT8 ns_delay)> net_register_delegate;
#endif
class net_dev_t;
class net_param_t;
class netlist_setup_t;
class netlist_base_t;
// ----------------------------------------------------------------------------------------
// net_list_t
// ----------------------------------------------------------------------------------------
template <class _ListClass, int _NumElements>
struct net_list_t
{
public:
net_list_t() //(int max_elements = 64)
{
//m_list = global_alloc_array_clear(_ListClass, max_elements);
m_ptr = m_list;
m_ptr--;
}
ATTR_HOT inline void add(const _ListClass elem)
{
assert(m_ptr-m_list <= _NumElements - 1);
*(++m_ptr) = elem;
}
ATTR_HOT inline _ListClass *first() { return m_list; }
ATTR_HOT inline _ListClass *last() { return m_ptr; }
ATTR_HOT inline _ListClass item(int i) { return m_list[i]; }
inline int count() { return m_ptr - m_list + 1; }
ATTR_HOT inline bool empty() { return (m_ptr < m_list); }
ATTR_HOT inline void clear() { m_ptr = m_list - 1; }
private:
_ListClass * m_ptr;
_ListClass m_list[_NumElements];
};
// ----------------------------------------------------------------------------------------
// net_output_t
// ----------------------------------------------------------------------------------------
struct net_output_t
{
public:
net_output_t();
ATTR_HOT inline void clear() { set_Q(nst_LOW); }
ATTR_HOT inline void set() { set_Q(nst_HIGH); }
ATTR_HOT inline void setTo(const net_sig_t val) { set_Q(val); }
ATTR_HOT inline void setTo(const net_sig_t val, const UINT8 delay_ns) { set_Q(val,delay_ns); }
ATTR_HOT inline void setToNoCheck(const net_sig_t val)
{
m_new_Q = val;
register_in_list();
}
ATTR_HOT inline void setToNoCheck(const net_sig_t val, const UINT8 delay_ns)
{
m_new_Q = val;
register_in_list(delay_ns);
}
ATTR_COLD void initial(const net_sig_t val) { m_Q = val; m_new_Q = val; }
ATTR_HOT inline const net_sig_t Q() const { return m_Q; }
inline net_sig_t *Q_ptr() { return &m_Q; }
inline net_sig_t *new_Q_ptr() { return &m_new_Q; }
ATTR_COLD void register_con(net_dev_t *dev);
ATTR_HOT void update_devs();
ATTR_HOT inline void update_out() {
m_Q = m_new_Q;
//m_QD = m_new_QD;
}
ATTR_HOT inline const net_dev_t *ttl_dev() { return m_ttldev; }
ATTR_COLD void set_ttl_dev(net_dev_t *dev);
private:
ATTR_HOT inline void register_in_list(const UINT8 delay_ns);
ATTR_HOT inline void register_in_list();
ATTR_HOT inline void set_Q(const net_sig_t newQ)
{
if (newQ != m_new_Q)
{
m_new_Q = newQ;
//m_new_QD = newQ ? 1.0 : 0.0;
register_in_list();
}
}
ATTR_HOT inline void set_Q(const net_sig_t newQ, const UINT8 delay_ns)
{
if (newQ != m_new_Q)
{
m_new_Q = newQ;
//m_new_QD = newQ ? 1.0 : 0.0;
register_in_list(delay_ns);
}
}
net_sig_t m_Q;
net_sig_t m_new_Q;
double m_QD;
double m_new_QD;
netlist_base_t *m_netlist;
int m_num_cons;
#if USE_DELEGATES
net_update_delegate m_cons[32];
#else
//net_dev_t **m_cons;
net_dev_t *m_cons[32];
#endif
net_dev_t *m_ttldev;
};
// ----------------------------------------------------------------------------------------
// net_dev_t
// ----------------------------------------------------------------------------------------
class net_dev_t
{
public:
net_dev_t(netlist_setup_t *setup, const char *name)
: m_setup(setup), m_variable_input_count(false), m_name(name)
{
assert(name != NULL);
}
virtual ~net_dev_t();
ATTR_HOT virtual void timer_cb(INT32 timer_id) {}
const char *name() const { return m_name; }
inline netlist_setup_t *setup() { return m_setup; }
ATTR_HOT void update_timed();
ATTR_HOT virtual void update_param() {}
ATTR_HOT virtual void update() { }
ATTR_HOT inline net_sig_t INP(net_input_t &v) { return v.o->Q(); }
ATTR_COLD virtual void start() {}
net_list_t<const char *, 20> m_inputs;
ATTR_COLD bool variable_input_count() { return m_variable_input_count; }
/* stats */
osd_ticks_t total_time;
volatile INT32 stat_count;
protected:
void register_output(const char *name, net_output_t *out);
void register_input(const char *name, net_input_t *in, net_input_type type = NET_INP_TYPE_ACTIVE);
void register_param(const char *sname, net_param_t *param, double initialVal = 0.0);
netlist_setup_t *m_setup;
bool m_variable_input_count;
private:
const char *m_name;
};
class net_param_t
{
public:
net_param_t() { }
inline void setTo(const double param) { m_param = param; m_ttldev->update_param(); }
inline void setTo(const int param) { m_param = param; m_ttldev->update_param(); }
inline void initial(const double val) { m_param = val; }
inline void initial(const int val) { m_param = val; }
inline double Value() { return m_param; }
inline int ValueInt() { return m_param; }
ATTR_HOT inline const net_dev_t *ttl_dev() { return m_ttldev; }
void set_ttl_dev(net_dev_t *dev) { m_ttldev = dev; }
private:
double m_param;
net_dev_t *m_ttldev;
};
// ----------------------------------------------------------------------------------------
// net_signal_t
// ----------------------------------------------------------------------------------------
template <int _numdev>
class net_signal_t : public net_dev_t
{
public:
net_signal_t(netlist_setup_t *parent, const char *name)
: net_dev_t(parent, name) { }
//ATTR_HOT virtual void update() = 0;
ATTR_COLD void start()
{
const char *sIN[8] = { "I1", "I2", "I3", "I4", "I5", "I6", "I7", "I8" };
register_output("Q", &m_Q);
for (int i=0; i < _numdev; i++)
register_input(sIN[i], &m_i[i]);
}
protected:
net_input_t m_i[_numdev];
net_output_t m_Q;
};
// ----------------------------------------------------------------------------------------
// netlist_base_timer_t
// ----------------------------------------------------------------------------------------
class netlist_base_timer_t
{
public:
netlist_base_timer_t() {}
virtual void adjust_timer(double delay) = 0;
};
// ----------------------------------------------------------------------------------------
// netlist_setup_t
// ----------------------------------------------------------------------------------------
class netlist_setup_t
{
public:
struct net_input_setup_t {
public:
net_input_setup_t(net_input_t *inp, net_input_type type = NET_INP_TYPE_ACTIVE)
: m_inp(inp), m_type(type) {}
net_input_t *inp() { return m_inp; }
net_input_type type() { return m_type; }
private:
net_input_t *m_inp;
net_input_type m_type;
};
typedef tagmap_t<net_dev_t *, 393> tagmap_devices_t;
typedef tagmap_t<astring *, 393> tagmap_astring_t;
typedef tagmap_t<net_output_t *, 393> tagmap_output_t;
typedef tagmap_t<net_input_setup_t *, 393> tagmap_input_t;
typedef tagmap_t<net_param_t *, 393> tagmap_param_t;
netlist_setup_t(netlist_base_t &netlist);
~netlist_setup_t();
netlist_base_t &netlist() { return m_netlist; }
net_dev_t *register_dev(net_dev_t *dev);
void remove_dev(const char *name);
void register_output(const char *name, net_output_t *out);
void register_input(const char *name, net_input_t *inp, net_input_type type = NET_INP_TYPE_ACTIVE);
void register_alias(const char *alias, const char *out);
void register_param(const char *sname, net_param_t *param);
void register_link(const char *sin, const char *sout);
net_output_t *find_output(const char *outname_in);
net_param_t *find_param(const char *param_in);
void register_callback(const char *devname, net_output_delegate delegate);
void parse(char *buf);
void resolve_inputs(void);
/* not ideal, but needed for save_state */
tagmap_output_t m_outputs;
void print_stats();
protected:
private:
netlist_base_t &m_netlist;
tagmap_devices_t m_devices;
tagmap_astring_t m_alias;
tagmap_input_t m_inputs;
tagmap_param_t m_params;
tagmap_astring_t m_links;
void step_all_devices();
net_output_t *find_output_exact(const char *outname_in);
const char *resolve_alias(const char *name) const;
};
// ----------------------------------------------------------------------------------------
// netlist_base_t
// ----------------------------------------------------------------------------------------
class netlist_base_t
{
public:
netlist_base_t(bool sub_cycle_exact);
virtual ~netlist_base_t();
virtual netlist_base_timer_t *alloc_timer(net_dev_t *dev, INT32 timer_id) = 0;
void set_clock_freq(int clockfreq);
void set_gatedelay(int gatedelay);
ATTR_HOT inline void register_in_list(net_output_t *out, const UINT8 delay_ns)
{
m_output_list[m_sub_cycle_exact ? ((m_current + delay_ns / m_divisor) & m_netlist_mask) : 0].add(out);
}
ATTR_HOT inline void register_in_list(net_output_t *out)
{
m_output_list[m_sub_cycle_exact ? (m_current & m_netlist_mask) : 0].add(out);
}
ATTR_HOT inline void reset_list()
{
m_output_list[m_sub_cycle_exact ? m_current : 0].clear();
}
ATTR_COLD void reset_lists()
{
for (int i = 0; i <= m_netlist_mask; i++)
m_output_list[i].clear();
}
ATTR_HOT void process_list(void);
protected:
UINT8 m_current;
UINT8 m_divisor;
UINT8 m_netlist_mask;
bool m_sub_cycle_exact;
net_list_t<net_output_t *, 512> m_output_list[NET_LIST_SIZE];
int m_gatedelay;
int m_clockfreq;
private:
};
// ----------------------------------------------------------------------------------------
// dev_callback
// ----------------------------------------------------------------------------------------
class netdev_callback : public net_dev_t
{
public:
netdev_callback(netlist_setup_t *parent, const char *name)
: net_dev_t(parent, name)
{
register_input("IN", &m_in);
}
void register_callback(net_output_delegate callback)
{
m_callback = callback;
}
ATTR_HOT void update();
private:
net_input_t m_in;
net_output_delegate m_callback;
};
// ----------------------------------------------------------------------------------------
// Inline implementations
// ----------------------------------------------------------------------------------------
ATTR_HOT inline void net_output_t::register_in_list(const UINT8 delay_ns)
{
m_netlist->register_in_list(this, delay_ns);
}
ATTR_HOT inline void net_output_t::register_in_list()
{
m_netlist->register_in_list(this);
}
// ----------------------------------------------------------------------------------------
// net_dev class factory
// ----------------------------------------------------------------------------------------
class net_dev_t_base_factory
{
public:
virtual ~net_dev_t_base_factory() {}
virtual net_dev_t *Create(netlist_setup_t *setup, const char *name) = 0;
const char *name() { return m_name; }
const char *classname() { return m_classname; }
protected:
const char *m_name; /* device name */
const char *m_classname; /* device class name */
};
template <class C>
class net_dev_t_factory : public net_dev_t_base_factory
{
public:
net_dev_t_factory(const char *name, const char *classname) { m_name = name; m_classname = classname; }
net_dev_t *Create(netlist_setup_t *setup, const char *name)
{
net_dev_t *r = global_alloc_clear(C(setup, name));
return r;
}
};
net_dev_t *net_create_device_by_classname(const char *classname, netlist_setup_t *setup, const char *icname);
net_dev_t *net_create_device_by_name(const char *name, netlist_setup_t *setup, const char *icname);
// ----------------------------------------------------------------------------------------
// MAME glue classes
// ----------------------------------------------------------------------------------------
class netlist_timer_t : public netlist_base_timer_t
{
public:
netlist_timer_t(device_t &parent, net_dev_t *dev, INT32 timer_id)
: netlist_base_timer_t()
{
m_param = timer_id;
m_timer = parent.machine().scheduler().timer_alloc(timer_expired_delegate(&netlist_timer_t::timer_cb, "netlist_timer_t::timer_cb", this), dev);
}
virtual void adjust_timer(double delay)
{
m_timer->adjust(attotime::from_double(delay) ,m_param, attotime::never);
}
void timer_cb(void *ptr, INT32 param)
{
net_dev_t *dev = (net_dev_t *) ptr;
dev->timer_cb(param);
}
private:
INT32 m_param;
emu_timer *m_timer;
};
class netlist_t : public netlist_base_t
{
public:
netlist_t(device_t &parent, bool sub_cycle_exact)
: netlist_base_t(sub_cycle_exact),
m_parent(parent)
{}
virtual ~netlist_t() { };
inline running_machine &machine() { return m_parent.machine(); }
virtual netlist_base_timer_t *alloc_timer(net_dev_t *dev, INT32 timer_id)
{
netlist_timer_t *ret = new netlist_timer_t(m_parent, dev, timer_id);
return ret;
}
device_t &parent() { return m_parent; }
private:
device_t &m_parent;
};
// ======================> netlist_mame_device
class netlist_mame_device : public device_t,
public device_execute_interface
{
public:
template<bool _Required, class _NETClass>
class output_finder;
class optional_output;
class required_output;
class optional_param;
class required_param;
class on_device_start;
// construction/destruction
netlist_mame_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
static void static_set_subcycles(device_t &device, int subcycles);
static void static_set_constructor(device_t &device, void (*setup_func)(netlist_setup_t &));
netlist_setup_t &setup() { return *m_setup; }
netlist_t &netlist() { return *m_netlist; }
//ATTR_HOT inline UINT64 Clocks() { return m_clockcnt; }
ATTR_HOT inline int SubCycles() const { return m_subcycles; }
net_list_t<on_device_start *, 393> m_device_start_list;
protected:
// device-level overrides
virtual void device_config_complete();
virtual void device_start();
virtual void device_stop();
virtual void device_reset();
virtual void device_post_load();
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
virtual UINT64 execute_clocks_to_cycles(UINT64 clocks) const;
virtual UINT64 execute_cycles_to_clocks(UINT64 cycles) const;
ATTR_HOT virtual void execute_run();
netlist_t *m_netlist;
net_output_t *m_clock_input;
netlist_setup_t *m_setup;
private:
void step_one_clock();
void save_state();
int m_clock;
int m_subcycles;
void (*m_setup_func)(netlist_setup_t &);
UINT32 dummy[8];
int m_icount;
int m_ss;
int m_clk;
};
// ======================> netlist_output_finder
class netlist_mame_device::on_device_start
{
public:
virtual bool OnDeviceStart() = 0;
};
// device finder template
template<bool _Required, class _NETClass>
class netlist_mame_device::output_finder : public device_t::object_finder_base<_NETClass>,
netlist_mame_device::on_device_start
{
public:
// construction/destruction
output_finder(device_t &base, const char *tag, const char *output)
: object_finder_base<_NETClass>(base, tag), m_output(output) { }
// finder
virtual bool findit()
{
device_t *device = this->m_base.subdevice(this->m_tag);
m_netlist = dynamic_cast<netlist_mame_device *>(device);
if (device != NULL && m_netlist == NULL)
{
void mame_printf_warning(const char *format, ...) ATTR_PRINTF(1,2);
mame_printf_warning("Device '%s' found but is not netlist\n", this->m_tag);
}
m_netlist->m_device_start_list.add(this);
return this->report_missing(m_netlist != NULL, "device", _Required);
}
protected:
netlist_mame_device *m_netlist;
const char *m_output;
};
// optional device finder
class netlist_mame_device::optional_output : public netlist_mame_device::output_finder<false, net_output_t>
{
public:
optional_output(device_t &base, const char *tag, const char *output) : output_finder<false, net_output_t>(base, tag, output) { }
virtual bool OnDeviceStart()
{
this->m_target = m_netlist->setup().find_output(m_output);
return this->report_missing(this->m_target != NULL, "output", false);
}
};
// required devices are similar but throw an error if they are not found
class netlist_mame_device::required_output : public netlist_mame_device::output_finder<true, net_output_t>
{
public:
required_output(device_t &base, const char *tag, const char *output) : output_finder<true, net_output_t>(base, tag, output) { }
virtual bool OnDeviceStart()
{
this->m_target = m_netlist->setup().find_output(m_output);
return this->report_missing(this->m_target != NULL, "output", true);
}
};
// optional device finder
class netlist_mame_device::optional_param : public netlist_mame_device::output_finder<false, net_param_t>
{
public:
optional_param(device_t &base, const char *tag, const char *output) : output_finder<false, net_param_t>(base, tag, output) { }
virtual bool OnDeviceStart()
{
this->m_target = m_netlist->setup().find_param(m_output);
return this->report_missing(this->m_target != NULL, "parameter", false);
}
};
// required devices are similar but throw an error if they are not found
class netlist_mame_device::required_param : public netlist_mame_device::output_finder<true, net_param_t>
{
public:
required_param(device_t &base, const char *tag, const char *output) : output_finder<true, net_param_t>(base, tag, output) { }
virtual bool OnDeviceStart()
{
this->m_target = m_netlist->setup().find_param(m_output);
return this->report_missing(this->m_target != NULL, "output", true);
}
};
// device type definition
extern const device_type NETLIST;
#endif