You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
470 lines
11 KiB
470 lines
11 KiB
/***************************************************************************
|
|
* Copyright (C) 2005 by David Saxton *
|
|
* david@bluehaze.org *
|
|
* *
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
* it under the terms of the GNU General Public License as published by *
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
* (at your option) any later version. *
|
|
***************************************************************************/
|
|
|
|
#include "component.h"
|
|
#include "gpsimprocessor.h"
|
|
#include "pin.h"
|
|
#include "simulator.h"
|
|
#include "switch.h"
|
|
|
|
#include <kstaticdeleter.h>
|
|
#include <tqtimer.h>
|
|
|
|
|
|
//BEGIN class Simulator
|
|
Simulator * Simulator::m_pSelf = 0l;
|
|
static KStaticDeleter<Simulator> staticSimulatorDeleter;
|
|
|
|
Simulator * Simulator::self()
|
|
{
|
|
if (!m_pSelf)
|
|
staticSimulatorDeleter.setObject( m_pSelf, new Simulator() );
|
|
return m_pSelf;
|
|
}
|
|
|
|
|
|
Simulator::Simulator()
|
|
{
|
|
m_currentChain = 0;
|
|
m_llNumber = 0;
|
|
m_stepNumber = 0;
|
|
m_bIsSimulating = true;
|
|
m_gpsimProcessors = 0l;
|
|
m_componentCallbacks = 0l;
|
|
m_components = 0l;
|
|
m_ordinaryCircuits = 0l;
|
|
m_switches = 0l;
|
|
|
|
unsigned max = unsigned(LOGIC_UPDATE_RATE/LINEAR_UPDATE_RATE);
|
|
for ( unsigned i = 0; i < max; i++ )
|
|
{
|
|
m_pStartStepCallback[i] = 0l;
|
|
m_pNextStepCallback[i] = 0l;
|
|
}
|
|
|
|
LogicConfig lc;
|
|
m_pChangedLogicStart = new LogicOut( lc, false );
|
|
m_pChangedLogicLast = m_pChangedLogicStart;
|
|
|
|
m_pChangedCircuitStart = new Circuit;
|
|
m_pChangedCircuitLast = m_pChangedCircuitStart;
|
|
|
|
TQTimer * stepTimer = new TQTimer(this);
|
|
connect( stepTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT(step()) );
|
|
stepTimer->start(1);
|
|
}
|
|
|
|
|
|
Simulator::~Simulator()
|
|
{
|
|
delete m_pChangedLogicStart;
|
|
delete m_pChangedCircuitStart;
|
|
|
|
detachAll(m_gpsimProcessors);
|
|
detachAll(m_components);
|
|
detachAll(m_componentCallbacks);
|
|
detachAll(m_ordinaryCircuits);
|
|
detachAll(m_switches);
|
|
}
|
|
|
|
|
|
void Simulator::step()
|
|
{
|
|
if (!m_bIsSimulating)
|
|
return;
|
|
|
|
// We are called a thousand times a second (the maximum allowed by TQTimer),
|
|
// so divide the LINEAR_UPDATE_RATE by 1e3 for the number of loops we need
|
|
// to do.
|
|
const unsigned maxSteps = unsigned(LINEAR_UPDATE_RATE/1e3);
|
|
for ( unsigned i = 0; i < maxSteps; ++i )
|
|
{
|
|
m_llNumber = 0;
|
|
m_stepNumber++;
|
|
|
|
// Update the non-logic parts of the simulation
|
|
LinkedList<Component> * component = m_components;
|
|
while (component)
|
|
{
|
|
component->data()->stepNonLogic();
|
|
component = component->m_pNext;
|
|
}
|
|
LinkedList<Circuit> * circuit = m_ordinaryCircuits;
|
|
while (circuit)
|
|
{
|
|
circuit->data()->doNonLogic();
|
|
circuit = circuit->m_pNext;
|
|
}
|
|
LinkedList<Switch> * sw = m_switches;
|
|
while (sw)
|
|
{
|
|
sw->data()->bounce();
|
|
sw = sw->m_pNext;
|
|
}
|
|
|
|
// Update the logic parts of our simulation
|
|
const unsigned max = unsigned(LOGIC_UPDATE_RATE/LINEAR_UPDATE_RATE);
|
|
for ( m_llNumber = 0; m_llNumber < max; ++m_llNumber )
|
|
{
|
|
// Update the logic components
|
|
LinkedList<ComponentCallback> * callback = m_componentCallbacks;
|
|
while (callback)
|
|
{
|
|
callback->data()->callback();
|
|
callback = callback->m_pNext;
|
|
}
|
|
|
|
callback = m_pStartStepCallback[m_llNumber];
|
|
while (callback)
|
|
{
|
|
LinkedList<ComponentCallback> * next = callback->m_pNext;
|
|
callback->m_pNext = 0l;
|
|
callback->data()->callback();
|
|
callback = next;
|
|
}
|
|
m_pStartStepCallback[m_llNumber] = 0l;
|
|
|
|
#ifndef NO_GPSIM
|
|
// Update the gpsim processors
|
|
LinkedList<GpsimProcessor> * gpsimProcessor = m_gpsimProcessors;
|
|
while (gpsimProcessor)
|
|
{
|
|
gpsimProcessor->data()->executeNext();
|
|
gpsimProcessor = gpsimProcessor->m_pNext;
|
|
}
|
|
#endif
|
|
|
|
|
|
int prevChain = m_currentChain;
|
|
m_currentChain = 1 - m_currentChain;
|
|
|
|
|
|
// Update the non-logic circuits
|
|
if ( Circuit * changed = m_pChangedCircuitStart->nextChanged(prevChain) )
|
|
{
|
|
for ( Circuit * circuit = changed; circuit; circuit = circuit->nextChanged(prevChain) )
|
|
circuit->setCanAddChanged(true);
|
|
|
|
m_pChangedCircuitStart->setNextChanged( 0l, prevChain );
|
|
m_pChangedCircuitLast = m_pChangedCircuitStart;
|
|
|
|
do
|
|
{
|
|
Circuit * next = changed->nextChanged(prevChain);
|
|
changed->setNextChanged( 0l, prevChain );
|
|
changed->doLogic();
|
|
changed = next;
|
|
}
|
|
while (changed);
|
|
}
|
|
|
|
// Call the logic callbacks
|
|
if (LogicOut * changed = m_pChangedLogicStart->nextChanged(prevChain))
|
|
{
|
|
for ( LogicOut * out = changed; out; out = out->nextChanged(prevChain) )
|
|
out->setCanAddChanged(true);
|
|
|
|
m_pChangedLogicStart->setNextChanged( 0l, prevChain );
|
|
m_pChangedLogicLast = m_pChangedLogicStart;
|
|
do
|
|
{
|
|
LogicOut * next = changed->nextChanged(prevChain);
|
|
changed->setNextChanged( 0l, prevChain );
|
|
|
|
double v = changed->isHigh() ? changed->outputHighVoltage() : 0.0;
|
|
|
|
for ( PinList::iterator it = changed->pinListBegin; it != changed->pinListEnd; ++it )
|
|
{
|
|
if ( Pin * pin = *it )
|
|
pin->setVoltage(v);
|
|
}
|
|
|
|
LogicIn * logicCallback = changed;
|
|
while (logicCallback)
|
|
{
|
|
logicCallback->callCallback();
|
|
logicCallback = logicCallback->nextLogic();
|
|
}
|
|
|
|
changed = next;
|
|
}
|
|
while (changed);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Simulator::slotSetSimulating( bool simulate )
|
|
{
|
|
if ( m_bIsSimulating == simulate )
|
|
return;
|
|
|
|
m_bIsSimulating = simulate;
|
|
emit simulatingStateChanged(simulate);
|
|
}
|
|
|
|
|
|
void Simulator::createLogicChain( LogicOut * logicOut, const LogicInList & logicInList, const PinList & pinList )
|
|
{
|
|
if (!logicOut)
|
|
return;
|
|
|
|
bool state = logicOut->outputState();
|
|
|
|
logicOut->setUseLogicChain(true);
|
|
logicOut->pinList = pinList;
|
|
logicOut->pinListBegin = logicOut->pinList.begin();
|
|
logicOut->pinListEnd = logicOut->pinList.end();
|
|
|
|
LogicIn * last = logicOut;
|
|
const LogicInList::const_iterator end = logicInList.end();
|
|
for ( LogicInList::const_iterator it = logicInList.begin(); it != end; ++it )
|
|
{
|
|
LogicIn * next = *it;
|
|
last->setNextLogic(next);
|
|
last->setLastState(state);
|
|
last = next;
|
|
}
|
|
last->setNextLogic(0l);
|
|
last->setLastState(state);
|
|
|
|
// Mark it as changed, if it isn't already changed...
|
|
LogicOut * changed = m_pChangedLogicStart->nextChanged(m_currentChain);
|
|
while (changed)
|
|
{
|
|
if ( changed == logicOut )
|
|
return;
|
|
changed = changed->nextChanged(m_currentChain);
|
|
}
|
|
addChangedLogic(logicOut);
|
|
logicOut->setCanAddChanged(false);
|
|
|
|
if ( !m_logicChainStarts.contains( logicOut ) )
|
|
m_logicChainStarts << logicOut;
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
void Simulator::attach( LinkedList<T> ** start, T * data )
|
|
{
|
|
if (!data)
|
|
return;
|
|
|
|
while ( *start && (*start)->m_pNext )
|
|
{
|
|
if ( (*start)->data() == data )
|
|
return;
|
|
start = & (*start)->m_pNext;
|
|
}
|
|
|
|
if (*start)
|
|
(*start)->m_pNext = new LinkedList<T>(data);
|
|
else
|
|
*start = new LinkedList<T>(data);
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
void Simulator::detach( LinkedList<T> ** start, T * data )
|
|
{
|
|
if (!data)
|
|
return;
|
|
|
|
while (*start)
|
|
{
|
|
if ( (*start)->data() == data )
|
|
{
|
|
LinkedList<T> * toDelete = *start;
|
|
*start = (*start)->m_pNext;
|
|
delete toDelete;
|
|
return;
|
|
}
|
|
|
|
start = & (*start)->m_pNext;
|
|
}
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
void Simulator::detachAll( LinkedList<T> * list )
|
|
{
|
|
while (list)
|
|
{
|
|
LinkedList<T> * next = list->m_pNext;
|
|
delete list;
|
|
list = next;
|
|
}
|
|
}
|
|
|
|
|
|
void Simulator::attachGpsimProcessor( GpsimProcessor * cpu )
|
|
{
|
|
attach( & m_gpsimProcessors, cpu );
|
|
}
|
|
|
|
|
|
void Simulator::detachGpsimProcessor( GpsimProcessor * cpu )
|
|
{
|
|
detach( & m_gpsimProcessors, cpu );
|
|
}
|
|
|
|
|
|
void Simulator::attachComponentCallback( Component * component, VoidCallbackPtr function )
|
|
{
|
|
attach( & m_componentCallbacks, new ComponentCallback( component, function ) );
|
|
}
|
|
|
|
|
|
void Simulator::attachComponent( Component * component )
|
|
{
|
|
if ( !component || !component->doesStepNonLogic() )
|
|
return;
|
|
|
|
attach( & m_components, component );
|
|
}
|
|
|
|
|
|
void Simulator::detachComponent( Component * component )
|
|
{
|
|
detach( & m_components, component );
|
|
detachComponentCallbacks(component);
|
|
}
|
|
|
|
|
|
void Simulator::attachSwitch( Switch * sw )
|
|
{
|
|
attach( & m_switches, sw );
|
|
}
|
|
|
|
|
|
void Simulator::detachSwitch( Switch * sw )
|
|
{
|
|
detach( & m_switches, sw );
|
|
}
|
|
|
|
|
|
void Simulator::detachComponentCallbacks( Component * component )
|
|
{
|
|
LinkedList<ComponentCallback> * callback = m_componentCallbacks;
|
|
while (callback)
|
|
{
|
|
LinkedList<ComponentCallback> * next = callback->m_pNext;
|
|
ComponentCallback * data = callback->data();
|
|
if ( data->component() == component )
|
|
{
|
|
detach( & m_componentCallbacks, data );
|
|
delete data;
|
|
}
|
|
callback = next;
|
|
}
|
|
}
|
|
|
|
|
|
void Simulator::attachCircuit( Circuit * circuit )
|
|
{
|
|
if (!circuit)
|
|
return;
|
|
attach( & m_ordinaryCircuits, circuit );
|
|
addChangedCircuit(circuit);
|
|
circuit->setCanAddChanged(false);
|
|
}
|
|
|
|
|
|
void Simulator::removeLogicInReferences( LogicIn * logicIn )
|
|
{
|
|
if ( !logicIn )
|
|
return;
|
|
|
|
TQValueList<LogicOut*>::iterator end = m_logicChainStarts.end();
|
|
for ( TQValueList<LogicOut*>::iterator it = m_logicChainStarts.begin(); it != end; ++it )
|
|
{
|
|
LogicIn * logicCallback = *it;
|
|
while (logicCallback)
|
|
{
|
|
if ( logicCallback->nextLogic() == logicIn )
|
|
logicCallback->setNextLogic( logicCallback->nextLogic()->nextLogic() );
|
|
logicCallback = logicCallback->nextLogic();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Simulator::removeLogicOutReferences( LogicOut * logic )
|
|
{
|
|
m_logicChainStarts.remove( logic );
|
|
|
|
// Any changes to the code below will probably also apply to Simulator::detachCircuit
|
|
|
|
if ( m_pChangedLogicLast == logic )
|
|
{
|
|
LogicOut * previous_1 = 0l;
|
|
LogicOut * previous_2 = 0l;
|
|
for ( LogicOut * logic = m_pChangedLogicStart; logic; )
|
|
{
|
|
if (previous_1)
|
|
previous_2 = previous_1;
|
|
previous_1 = logic;
|
|
logic = logic->nextChanged( m_currentChain );
|
|
}
|
|
|
|
m_pChangedLogicLast = previous_2;
|
|
}
|
|
|
|
for ( unsigned chain = 0; chain < 2; ++chain )
|
|
{
|
|
for ( LogicOut * prevChanged = m_pChangedLogicStart; prevChanged; prevChanged = prevChanged->nextChanged( chain ) )
|
|
{
|
|
LogicOut * nextChanged = prevChanged->nextChanged( chain );
|
|
if ( nextChanged == logic )
|
|
prevChanged->setNextChanged( nextChanged->nextChanged( chain ), chain );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void Simulator::detachCircuit( Circuit * circuit )
|
|
{
|
|
if (!circuit)
|
|
return;
|
|
|
|
detach( & m_ordinaryCircuits, circuit );
|
|
|
|
// Any changes to the code below will probably also apply to Simulator::removeLogicOutReferences
|
|
|
|
if ( m_pChangedCircuitLast == circuit )
|
|
{
|
|
Circuit * previous_1 = 0l;
|
|
Circuit * previous_2 = 0l;
|
|
for ( Circuit * circuit = m_pChangedCircuitStart; circuit; )
|
|
{
|
|
if (previous_1)
|
|
previous_2 = previous_1;
|
|
previous_1 = circuit;
|
|
circuit = circuit->nextChanged( m_currentChain );
|
|
}
|
|
|
|
m_pChangedCircuitLast = previous_2;
|
|
}
|
|
|
|
for ( unsigned chain = 0; chain < 2; ++chain )
|
|
{
|
|
for ( Circuit * prevChanged = m_pChangedCircuitStart; prevChanged; prevChanged = prevChanged->nextChanged( chain ) )
|
|
{
|
|
Circuit * nextChanged = prevChanged->nextChanged( chain );
|
|
if ( nextChanged == circuit )
|
|
prevChanged->setNextChanged( nextChanged->nextChanged( chain ), chain );
|
|
}
|
|
}
|
|
}
|
|
//END class Simulator
|
|
|
|
#include "simulator.moc"
|