diff --git a/RMFT2.cpp b/RMFT2.cpp
index 5a91541..d03efbf 100644
--- a/RMFT2.cpp
+++ b/RMFT2.cpp
@@ -16,6 +16,23 @@
* You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see .
*/
+
+/* EXRAILPlus planned FEATURE additions
+ F1. DCC accessory packet opcodes (short and long form)
+ F2. ONAccessory catchers
+ F3. Turnout descriptions for Withrottle
+ F4. Oled announcements (depends on HAL)
+ F5. Withrottle roster info
+ F6. Multi-occupancy semaphore
+ F7. Self starting sequences
+ F8. Park/unpark
+ */
+/* EXRAILPlus planned TRANSPARENT additions
+ T1. RAM based fast lookup for sequences ON* event catchers and signals.
+ T2. Extend to >64k
+ */
+
+
#include
#include "RMFT2.h"
#include "DCC.h"
@@ -53,67 +70,162 @@ RMFT2 * RMFT2::pausingTask=NULL; // Task causing a PAUSE.
// and all others will have their locos stopped, then resumed after the pausing task resumes.
byte RMFT2::flags[MAX_FLAGS];
+LookList * RMFT2::sequenceLookup=NULL;
+LookList * RMFT2::signalLookup=NULL;
+LookList * RMFT2::onThrowLookup=NULL;
+LookList * RMFT2::onCloseLookup=NULL;
+
#define GET_OPCODE GETFLASH(RMFT2::RouteCode+progCounter)
#define GET_OPERAND(n) GETFLASHW(RMFT2::RouteCode+progCounter+1+(n*3))
#define SKIPOP progCounter+=3
+
+ LookList::LookList(int16_t size) {
+ m_size=size;
+ m_loaded=0;
+ if (size) {
+ m_lookupArray=new int16_t[size];
+ m_resultArray=new int32_t[size];
+ }
+ }
+ void LookList::add(int16_t lookup, int32_t result) {
+ if (m_loaded==m_size) return; // and forget
+ m_lookupArray[m_loaded]=lookup;
+ m_resultArray[m_loaded]=result;
+ m_loaded++;
+ }
+ int32_t LookList::find(int16_t value) {
+ for (int16_t i=0;iadd(red,progCounter);
+ break;
}
- if (opcode==OPCODE_TURNOUT) {
- VPIN id=GET_OPERAND(0);
+ case OPCODE_TURNOUT: {
+ VPIN id=operand;
int addr=GET_OPERAND(1);
byte subAddr=GET_OPERAND(2);
DCCTurnout::create(id,addr,subAddr);
- continue;
+ break;
}
- if (opcode==OPCODE_SERVOTURNOUT) {
- int16_t id=GET_OPERAND(0);
+ case OPCODE_SERVOTURNOUT: {
+ VPIN id=operand;
VPIN pin=GET_OPERAND(1);
int activeAngle=GET_OPERAND(2);
int inactiveAngle=GET_OPERAND(3);
int profile=GET_OPERAND(4);
ServoTurnout::create(id,pin,activeAngle,inactiveAngle,profile);
- continue;
+ break;
}
- if (opcode==OPCODE_PINTURNOUT) {
- int16_t id=GET_OPERAND(0);
+ case OPCODE_PINTURNOUT: {
+ VPIN id=operand;
VPIN pin=GET_OPERAND(1);
VpinTurnout::create(id,pin);
- continue;
+ break;
}
- // other opcodes are not needed on this pass
+
+ case OPCODE_ROUTE:
+ case OPCODE_AUTOMATION:
+ case OPCODE_SEQUENCE:
+ sequenceLookup->add(operand,progCounter);
+ break;
+
+ case OPCODE_ONTHROW:
+ onThrowLookup->add(operand,progCounter);
+ break;
+
+ case OPCODE_ONCLOSE:
+ onCloseLookup->add(operand,progCounter);
+ break;
+
+ default: // Ignore
+ break;
+ }
}
+ sequenceLookup->add(0,0); // add default start sequence
SKIPOP; // include ENDROUTES opcode
- DIAG(F("EXRAIL %db, MAX_FLAGS=%d"), progCounter,MAX_FLAGS);
+
+ DIAG(F("EXRAIL %db, fl=%d seq=%d, sig=%d, onT=%d, onC=%d"),
+ progCounter,MAX_FLAGS,
+ sequenceCount, signalCount, onThrowCount, onCloseCount);
+
new RMFT2(0); // add the startup route
}
@@ -199,7 +311,7 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
{
int route=(paramCount==2) ? p[1] : p[2];
uint16_t cab=(paramCount==2)? 0 : p[1];
- int pc=locateRouteStart(route);
+ int pc=sequenceLookup->find(route);
if (pc<0) return false;
RMFT2* task=new RMFT2(pc);
task->loco=cab;
@@ -313,28 +425,12 @@ RMFT2::~RMFT2() {
}
void RMFT2::createNewTask(int route, uint16_t cab) {
- int pc=locateRouteStart(route);
+ int pc=sequenceLookup->find(route);
if (pc<0) return;
RMFT2* task=new RMFT2(pc);
task->loco=cab;
}
-
-int RMFT2::locateRouteStart(int16_t _route) {
- if (_route==0) return 0; // Route 0 is always start of ROUTES for default startup
- for (int progCounter=0;;SKIPOP) {
- byte opcode=GET_OPCODE;
- if (opcode==OPCODE_ENDEXRAIL) {
- DIAG(F("RMFT2 sequence %d not found"), _route);
- return -1;
- }
- if ((opcode==OPCODE_ROUTE || opcode==OPCODE_AUTOMATION || opcode==OPCODE_SEQUENCE)
- && _route==(int)GET_OPERAND(0)) return progCounter;
- }
- return -1;
-}
-
-
void RMFT2::driveLoco(byte speed) {
if (loco<=0) return; // Prevent broadcast!
if (diag) DIAG(F("EXRAIL drive %d %d %d"),loco,speed,forward^invert);
@@ -567,7 +663,7 @@ void RMFT2::loop2() {
break;
case OPCODE_FOLLOW:
- progCounter=locateRouteStart(operand);
+ progCounter=sequenceLookup->find(operand);
if (progCounter<0) kill(F("FOLLOW unknown"), operand);
return;
@@ -577,7 +673,7 @@ void RMFT2::loop2() {
return;
}
callStack[stackDepth++]=progCounter+3;
- progCounter=locateRouteStart(operand);
+ progCounter=sequenceLookup->find(operand);
if (progCounter<0) kill(F("CALL unknown"),operand);
return;
@@ -628,7 +724,7 @@ void RMFT2::loop2() {
case OPCODE_START:
{
- int newPc=locateRouteStart(operand);
+ int newPc=sequenceLookup->find(operand);
if (newPc<0) break;
new RMFT2(newPc);
}
@@ -636,7 +732,7 @@ void RMFT2::loop2() {
case OPCODE_SENDLOCO: // cab, route
{
- int newPc=locateRouteStart(GET_OPERAND(1));
+ int newPc=sequenceLookup->find(GET_OPERAND(1));
if (newPc<0) break;
RMFT2* newtask=new RMFT2(newPc); // create new task
newtask->loco=operand;
@@ -716,22 +812,23 @@ void RMFT2::kill(const FSH * reason, int operand) {
/* static */ void RMFT2::doSignal(VPIN id,bool red, bool amber, bool green) {
// CAUTION: hides class member progCounter
- for (int progCounter=0;; SKIPOP){
- byte opcode=GET_OPCODE;
- if (opcode==OPCODE_ENDEXRAIL) return;
- if (opcode!=OPCODE_SIGNAL) continue;
- byte redpin=GET_OPERAND(0);
- if (redpin!=id)continue;
- byte amberpin=GET_OPERAND(1);
- byte greenpin=GET_OPERAND(2);
- // If amberpin is zero, synthesise amber from red+green
- IODevice::write(redpin,red || (amber && (amberpin==0)));
- if (amberpin) IODevice::write(amberpin,amber);
- if (greenpin) IODevice::write(greenpin,green || (amber && (amberpin==0)));
- return;
- }
- }
+ int progCounter=signalLookup->find(id);
+ if (progCounter<0) return;
+ VPIN redpin=GET_OPERAND(0);
+ if (redpin!=id) return; // something wrong in lookup
+ VPIN amberpin=GET_OPERAND(1);
+ VPIN greenpin=GET_OPERAND(2);
+ // If amberpin is zero, synthesise amber from red+green
+ IODevice::write(redpin,red || (amber && (amberpin==0)));
+ if (amberpin) IODevice::write(amberpin,amber);
+ if (greenpin) IODevice::write(greenpin,green || (amber && (amberpin==0)));
+ }
+
void RMFT2::turnoutEvent(int16_t turnoutId, bool closed) {
+ // Hunt for an ONTHROW/ONCLOSE for this turnout
+ // caution hides class progCounter;
+ int progCounter= (closed?onCloseLookup:onThrowLookup)->find(turnoutId);
+ if (progCounter<0) return;
// Check we dont already have a task running this turnout
RMFT2 * task=loopTask;
@@ -743,18 +840,9 @@ void RMFT2::kill(const FSH * reason, int operand) {
task=task->next;
if (task==loopTask) break;
}
- // Hunt for an ONTHROW/ONCLOSE for this turnout
- byte huntFor=closed ? OPCODE_ONCLOSE : OPCODE_ONTHROW ;
- // caution hides class progCounter;
- for (int progCounter=0;; SKIPOP){
- byte opcode=GET_OPCODE;
- if (opcode==OPCODE_ENDEXRAIL) return;
- if (opcode!=huntFor) continue;
- if (turnoutId!=(int16_t)GET_OPERAND(0)) continue;
- task=new RMFT2(progCounter); // new task starts at this instruction
+
task->onTurnoutId=turnoutId; // flag for recursion detector
- return;
- }
+ task=new RMFT2(progCounter); // new task starts at this instruction
}
void RMFT2::printMessage2(const FSH * msg) {
diff --git a/RMFT2.h b/RMFT2.h
index 90080b7..0abacce 100644
--- a/RMFT2.h
+++ b/RMFT2.h
@@ -58,6 +58,18 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
static const short MAX_FLAGS=256;
#define FLAGOVERFLOW(x) x>=MAX_FLAGS
+class LookList {
+ public:
+ LookList(int16_t size);
+ void add(int16_t lookup, int32_t result);
+ int32_t find(int16_t value);
+ private:
+ int16_t m_size;
+ int16_t m_loaded;
+ int16_t * m_lookupArray;
+ int32_t * m_resultArray;
+};
+
class RMFT2 {
public:
static void begin();
@@ -75,7 +87,6 @@ private:
static void streamFlags(Print* stream);
static void setFlag(VPIN id,byte onMask, byte OffMask=0);
static bool getFlag(VPIN id,byte mask);
- static int locateRouteStart(int16_t _route);
static int16_t progtrackLocoId;
static void doSignal(VPIN id,bool red, bool amber, bool green);
static void emitRouteDescription(Print * stream, char type, int id, const FSH * description);
@@ -97,7 +108,11 @@ private:
static bool diag;
static const FLASH byte RouteCode[];
static byte flags[MAX_FLAGS];
-
+ static LookList * sequenceLookup;
+ static LookList * signalLookup;
+ static LookList * onThrowLookup;
+ static LookList * onCloseLookup;
+
// Local variables - exist for each instance/task
RMFT2 *next; // loop chain
int progCounter; // Byte offset of next route opcode in ROUTES table