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