===MM_Event=== //This module is part of the original MMBasic library. It is reproduced here with kind permission of Hugh Buckle and Geoff Graham. Be aware it may reference functionality which has changed or is deprecated in the latest versions of MMBasic.// **SE02.BAS**: 'File....: SE02.Bas ' 'Author..: Simon Whittam ' 'Email...: s.whittam(at)xnet.co.nz ' 'Date....: 8th September 2013 ' 'Language: Maximite BASIC, v4.4 ' 'Purpose.: To provide an example of a Event Driven framework that: ' uses circular First In, First Out (FIFO) event queues comprised of String Arrays. ' debounces switch inputs. ' uses Bit flags stored in a numeric variable. ' is comprised of two co-operating Finite State Machines (FSM). ' uses non blocking timers (10ms and 1sec) for delays. ' uses events to move between states and communicate between FSM's. ' displays State/Event changes ' the use of pointer to a SUB (ON var GOSUB A0,A1,A2,...) ' ' The code example turns on & off the toggling of a LED only when a pushbutton ' switch has been pushed 3 times and remains pushed, all within 2 seconds. ' 'Version.: v1.1 ' 'License.: Attribution-NonCommercial-ShareAlike 3.0 Australia (CC BY-NC-SA 3.0 AU) ' 'Ref.....: http://www.state-machine.com/qm/ ' http://geoffg.net/MonoMaximite.html ' http://mmbasic.com/ ' http://creativecommons.org/licenses/by-nc-sa/3.0/au/ ' '============================================================================== Library Load "Bit.Lib" 'BEGIN EventQ1.Lib - Initialisation Library Load "EventQ1.Lib" Option BASE 0 'event queue data referenced as an offset from: 0 - (Length -1) 'Configure event queue as empty EQ1Length = 4 'Event queue length. EQ1WriteFlag = 1 'Gets toggled after writing to last location in event queue. EQ1ReadFlag = 1 'Gets toggled after reading last location in event queue. EQ1NextWrite = 0 'Next location to write a byte. EQ1NextRead = 0 'Next location to read a byte. Dim EQ1$(1) LENGTH EQ1Length 'Create a circular event queue using a string/byte array. EQ1$ = "FIFO" 'END EventQ1.Lib - Initialisation '============================================================================== 'BEGIN EventQ2.Lib - Initialisation Library Load "EventQ2.Lib" 'Option BASE 0 'event queue data referenced as an offset from: 0 - (Length -1) 'Configure event queue as empty EQ2Length = 4 'Event queue length. EQ2WriteFlag = 1 'Gets toggled after writing to last location in event queue. EQ2ReadFlag = 1 'Gets toggled after reading last location in event queue. EQ2NextWrite = 0 'Next location to write a byte. EQ2NextRead = 0 'Next location to read a byte. Dim EQ2$(1) LENGTH EQ2Length 'Create a circular event queue using a string/byte array. EQ2$ = "FIFO" 'END EventQ2.Lib - Initialisation '============================================================================== 'Define I/O pins used Pin(11) = 1 ' Pin 11 -> 1, LED off SetPin 12, 2 ' Pin 12, Input, 5v Digital, with external pull-up resistor SetPin 11, 9 ' Pin 11, Output, Open Collector, with LED & resistor to 5Vdc. 'SysFlags bit assignments ' SFTick10ms = 0 ' SFTick1s = 1 ' SFSwitch = 2 ' SFLED = 3 SysFlags = &B0000 ' Queue1 event definitions used ' Undefined1 = 0 ' Switch1.0 = 1 ' Switch1.1 = 2 ' TickTimeout1 = 3 ' SecTimeout1 = 4 ' Queue2 event definitions used ' Undefined2 = 0 ' TickTimeout2 = 1 ' Enable2 = 2 ' Disable2 = 3 ' SetTick 10, BitSet10ms, 1 SetTick 1000, BitSet1s , 2 ' Queue1 variables '================= SecTimeout1 = 0 TickTimeout1 = 0 Switch1DebounceCount = 0 CurState1 = 0 ' Current State PrvState1 = 0 ' Previous State CurEvent1 = 0 ' Undefined event CurStateEvent1 = 0 PrvStateEvent1 = 0 ' Queue2 variables '================= TickTimeout2 = 0 CurState2 = 0 ' Current State PrvState2 = 0 ' Previous State CurEvent2 = 0 ' Undefined event CurStateEvent2 = 0 PrvStateEvent2 = 0 Do While (1) '=========================================================================== 'BEGIN Debug1 ' PrvStateEvent1 = CurStateEvent1 'END Debug1 'Respond to events from Queue1 for Finite State Machine 1 CurEvent1 = EQ1Read() CurStateEvent1 = ( Fix( (CurState1 * 5) + CurEvent1 ) + 1 ) 'BEGIN Debug1 ' Print only first occurrence of State/Event change ' IF NOT( PrvStateEvent1 = CurStateEvent1 ) THEN ' PRINT "StateEvent1: " + STR$( CurStateEvent1 ) ' ENDIF 'END Debug1 On CurStateEvent1 GoSub A0,A1,A2,A3,A4,B0,B1,B2,B3,B4 '=========================================================================== 'BEGIN Debug2 ' PrvStateEvent2 = CurStateEvent2 'END Debug2 'Respond to events from Queue2 for Finite State Machine 2 CurEvent2 = EQ2Read() CurStateEvent2 = ( Fix( (CurState2 * 4) + CurEvent2 ) + 1 ) 'BEGIN Debug2 ' Print only first occurrence of State/Event change ' IF NOT( PrvStateEvent2 = CurStateEvent2 ) THEN ' PRINT "StateEvent2: " + STR$( CurStateEvent2 ) ' ENDIF 'END Debug2 On CurStateEvent2 GoSub E0,E1,E2,E3,F0,F1,F2,F3,G0,G1,G2,G3 '=========================================================================== 'Has SFTick10ms flag been set IF BitTstSet( SysFlags, 0 ) THEN DecTimeOut1( TickTimeout1, 3) DecTimeout2( TickTimeout2, 1) 'DecTimeoutN( TickTimeoutN, TickTimeoutEventN) DebounceSwitch1 'DebounceSwitchN SysFlags = BitClr( SysFlags, 0 ) 'Clear SFTick10ms flag. ENDIF 'Has SFTick1s flag been set If BitTstSet( SysFlags, 1 ) Then DecTimeout1( SecTimeout1, 4 ) 'DecTimeoutN( SecTimeoutN, TimeoutNEvent ) SysFlags = BitClr( SysFlags, 1 ) 'Clear SFTick1s flag. ENDIF Loop Print "Finished" End '============================================================================== BitSet10ms: 'GLOBAL: SysFlags 'Set SFTick10ms flag in SysFlags SysFlags = BitSet( SysFlags, 0 ) IReturn '============================================================================== BitSet1s: 'GLOBAL: SysFlags 'Set SFTick1s flag in SysFlags SysFlags = BitSet( SysFlags, 1 ) IReturn '============================================================================== Sub DecTimeout1( TimeoutCount, TimeoutEvent) 'Has Timeout already expired ? If TimeoutCount > 0 Then ' No, decrement Timeout count TimeoutCount = ( TimeoutCount - 1 ) 'Has Timeout delay expired ? If TimeoutCount = 0 Then ' Yes, add Timeout event to queue 1. EQ1WriteSuccess( TimeoutEvent ) ENDIF ENDIF End Sub 'DecTimeout1 ;============================================================================== Sub DecTimeout2( TimeoutCount, TimeoutEvent) 'Has Timeout already expired ? If TimeoutCount > 0 Then ' No, decrement Timeout count TimeoutCount = ( TimeoutCount - 1 ) 'Has Timeout delay expired ? If TimeoutCount = 0 Then ' Yes, add Timeout event to queue 2. EQ2WriteSuccess( TimeoutEvent ) ENDIF ENDIF End Sub 'DecTimeout2 ;============================================================================== Sub DebounceSwitch1 'GLOBAL: Switch1DebounceCount, SysFlags ' Is a switch state being debounced ? If Switch1DebounceCount = 0 Then 'No, Check for change of switch state. Local SwitchPin, SwitchBit SwitchBit = BitMask( SysFlags, 4 ) ' Mask SFSwitch flag, i.e. 2^2. SwitchPin = Pin(12) SwitchPin = BitToggle( SwitchPin, 0 ) ' Inputs are active low SwitchPin = BitShiftLeft( SwitchPin ) ' Match PIN bit position with position of SFSwitch bit in SysFags SwitchPin = BitShiftLeft( SwitchPin ) ' Has switch changed state ? If Not( SwitchPin = SwitchBit ) Then 'Yes, debounce switch1. SwitchDebounced( Switch1DebounceCount, 2, 2, 1 ) ENDIF Else 'Switch1DebounceCount <> 0 SwitchDebounced( Switch1DebounceCount, 2, 2, 1 ) ENDIF End Sub 'DebounceSwitch1 ;============================================================================== Sub SwitchDebounced( SwitchDebounceCount, SwitchBit, Switch1Event, Switch0Event ) 'GLOBAL: SysFlags SwitchDebounceCount = SwitchDebounceCount + 1 'Has 50ms (5 x 10ms) of debounce time elapsed ? If ( SwitchDebounceCount = 5 ) Then SysFlags = BitToggle( SysFlags, SwitchBit) ' Yes, toggle SFSwitch bit in SysFlags If BitTstSet( SysFlags, SwitchBit ) Then EQ1WriteSuccess( Switch1event ) ' Place Switch1 event in queue. Else EQ1WriteSuccess( Switch0Event ) ' Place Switch0 event in queue. ENDIF SwitchDebounceCount = 0 ENDIF End Sub 'SwitchDebounced ;============================================================================== A0: 'Q1, 1, State0 - Undefined Return ;============================================================================== A1: 'Q1, 2, State0 - Switch0 Return ;============================================================================== A2: 'Q1, 3, State0 - Switch1 PrvState1 = CurState1 CurState1 = 1 SecTimeout1 = 2 Switch1Count = 1 Return ;============================================================================== A3: 'Q1, 4, State0 - TickTimeout Return ;============================================================================== A4: 'Q1, 5, State0 - SecondTimeout Return ;============================================================================== B0: 'Q1, 6, State1 - Undefined Return ;============================================================================== B1: 'Q1, 7, State1 - Switch0 Return ;============================================================================== B2: 'Q1, 8, State1 - Switch1 Switch1Count = (Switch1Count + 1 ) Return ;============================================================================== B3: 'Q1, 9, State1 - TickTimeout Return ;============================================================================== B4: 'Q1, 10, State1 - SecondTimeout 'Has Switch1 been pressed twice and still pressed after 2 seconds If (Switch1Count = 3) And BitTstSet( SysFlags, 2 ) Then 'Toggle LED state SysFlags = BitToggle( SysFlags, 3) ' Toggle SFLed flag If BitTstSet( SysFlags, 3 ) THEN ' Pin(11) = 0 'Turn LED on EQ2WriteSuccess( 2 ) 'Send Enable2 event to event queue 2 Else ' Pin(11) = 1 'Turn LED off EQ2WriteSuccess( 3 ) 'Send Disable2 event to event queue 2 EndIf ENDIF Switch1Count = 0 PrvState1 = CurState1 CurState1 = 0 Return ;============================================================================== E0: 'Q2, 1, State0 - Undefined2 Return ;============================================================================== E1: 'Q2, 2, State0 - TickTimeout2 Return ;============================================================================== E2: 'Q2, 3, State0 - Enable2 PrvState2 = CurState2 CurState2 = 1 TickTimeout2 = 20 Pin(11) = 0 'Turn LED on Return ;============================================================================== E3: 'Q2, 4, State0 - Disable2 Return ;============================================================================== F0: 'Q2, 5, State1 - Undefined2 Return ;============================================================================== F1: 'Q2, 6, State1 - TickTimeout2 PrvState2 = CurState2 CurState2 = 2 TickTimeout2 = 20 Pin(11) = 1 'Turn LED off Return ;============================================================================== F2: 'Q2, 7, State1 - Enable2 Return ;============================================================================== F3: 'Q2, 8, State1 - Disable2 PrvState2 = CurState2 CurState2 = 0 Pin(11) = 1 'Turn LED off Return ;============================================================================== G0: 'Q2, 9, State2 - Undefined2 Return ;============================================================================== G1: 'Q2,10, State2 - TickTimeout2 PrvState2 = CurState2 CurState2 = 1 TickTimeout2 = 20 Pin(11) = 0 'Turn LED on Return ;============================================================================== G2: 'Q2, 11, State2 - Enable2 Return ;============================================================================== G3: 'Q2, 12, State2 - Disable2 PrvState2 = CurState2 CurState2 = 0 Pin(11) = 1 'Turn LED off Return ;============================================================================== ---- **BIT.LIB**: 'File....: Bit.Lib ' 'Author..: Simon Whittam ' 'Email...: s.whittam(at)xnet.co.nz ' 'Date....: 8th September 2013 ' 'Language: Maximite BASIC, v4.4 ' 'Purpose.: Provide bit manipulation functions of Bit Flags contained in a ' MM BASIC numeric variable. ' 'Version.: v1.0 ' 'License.: Attribution-NonCommercial-ShareAlike 3.0 Australia (CC BY-NC-SA 3.0 AU) ' 'Ref.....: MMBasic - User Library, BIN8.BAS, crackerjack ' http://lbpe.wikispaces.com/Bit.Shift ' https://en.wikipedia.org/wiki/Bit_manipulation ' http://geoffg.net/MonoMaximite.html ' http://mmbasic.com/ ' http://creativecommons.org/licenses/by-nc-sa/3.0/au/ ' '============================================================================== FUNCTION BitComp( Nbr ) BitComp = (-Nbr -1) END FUNCTION '============================================================================== FUNCTION BitSet( Nbr, Bit ) BitSet = ( Nbr OR 2^Bit ) END FUNCTION '============================================================================== FUNCTION BitClr( Nbr, Bit ) BitClr = ( Nbr AND BitComp(2^Bit) ) END FUNCTION '============================================================================== FUNCTION BitTstSet( Nbr, Bit ) BitTstSet = SGN( Nbr AND 2^Bit) END FUNCTION '============================================================================== FUNCTION BitTstClr( Nbr, Bit ) BitTstClr = SGN( BitComp(Nbr) AND 2^Bit) END FUNCTION '============================================================================== FUNCTION BitShiftRight( Nbr) BitShiftRight = FIX( Nbr / 2) END FUNCTION '============================================================================== FUNCTION BitMask( Nbr, Mask ) BitMask = ( Nbr AND Mask ) END FUNCTION '============================================================================== FUNCTION BitToggle( Nbr, Bit) BitToggle = (Nbr XOR 2^Bit) END FUNCTION '============================================================================== FUNCTION BitShiftLeft( Nbr) BitShiftLeft = ( Nbr * 2 ) END FUNCTION '============================================================================== FUNCTION BitRotLeft(Nbr) BitRotLeft = ((Nbr+Nbr) MOD 256) or (Nbr>127) END FUNCTION '============================================================================== FUNCTION BitRotRight(Nbr) BitRotRight = (128*(Nbr AND 1)) or FIX(Nbr/2) END FUNCTION '============================================================================== ---- **EVENTQ1.lib**: 'File....: EventQ1.Lib ' 'Author..: Simon Whittam ' 'Email...: s.whittam(at)xnet.co.nz ' 'Date....: 8th September 2013 ' 'Language: Maximite BASIC, v4.4 ' 'Purpose.: Create and initialise an instance of a First In, First Out circular event queue. ' The event queue saves single bytes in a small String Array and does not ' overwrite unread bytes. ' ' Intended for use with a event driven Finite State Machine. ' 'Version.: v1.1 ' 'License.: Attribution-NonCommercial-ShareAlike 3.0 Australia (CC BY-NC-SA 3.0 AU) ' 'Ref.....: https://en.wikipedia.org/wiki/Circular_buffer ' http://geoffg.net/MonoMaximite.html ' http://mmbasic.com/ ' http://creativecommons.org/licenses/by-nc-sa/3.0/au/ ' '============================================================================== 'BEGIN EventQ1.Lib - Initialisation 'Copy the "EventQ1.Lib - Initialisation" section to the beginning of the Main BASIC module. 'Unremark the NEXT line when using this file as as library file 'LIBRARY LOAD "EventQ1.Lib" OPTION BASE 0 'event queue data referenced as an offset from: 0 - (Length -1) 'Configure event queue as empty EQ1Length = 4 'Event queue length. EQ1WriteFlag = 1 'Gets toggled after writing to last location in event queue. EQ1ReadFlag = 1 'Gets toggled after reading last location in event queue. EQ1NextWrite = 0 'Next location to write a byte. EQ1NextRead = 0 'Next location to read a byte. DIM EQ1$(1) LENGTH EQ1Length 'Create a circular event queue using a string/byte array. EQ1$ = "FIFO" 'END EventQ1.Lib - Initialisation '============================================================================== 'BEGIN Test code 'Note: ' For an empty queue, the following variables are initialised as follows: ' EQ1WriteFlag = 1 ' EQ1ReadFlag = 1 ' EQ1NextWrite = 0 ' EQ1NextRead = 0 ' PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ1WriteFlag, EQ1ReadFlag, EQ1NextWrite, EQ1NextRead ) ) PRINT "EQWrite:" + STR$( EQ1Write( 49 ) ) PRINT "Queue:" + EQ1$(1) + ", QueWrite:"+ STR$(EQ1NextWrite) + ", QueRead:" + STR$(EQ1NextRead) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ1WriteFlag, EQ1ReadFlag, EQ1NextWrite, EQ1NextRead ) ) PRINT "EQWrite:" + STR$( EQ1Write( 50 ) ) PRINT "Queue:" + EQ1$(1) + ", QueWrite:"+ STR$(EQ1NextWrite) + ", QueRead:" + STR$(EQ1NextRead) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ1WriteFlag, EQ1ReadFlag, EQ1NextWrite, EQ1NextRead ) ) PRINT "EQWrite:" + STR$( EQ1Write( 51 ) ) PRINT "Queue:" + EQ1$(1) + ", QueWrite:"+ STR$(EQ1NextWrite) + ", QueRead:" + STR$(EQ1NextRead) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ1WriteFlag, EQ1ReadFlag, EQ1NextWrite, EQ1NextRead ) ) PRINT "EQWrite:" + STR$( EQ1Write( 52 ) ) PRINT "Queue:" + EQ1$(1) + ", QueWrite:"+ STR$(EQ1NextWrite) + ", QueRead:" + STR$(EQ1NextRead) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ1WriteFlag, EQ1ReadFlag, EQ1NextWrite, EQ1NextRead ) ) ' PRINT "EQWrite:" + STR$( EQ1Write( 53 ) ) EQ1WriteSuccess(53) PRINT "Queue:" + EQ1$(1) + ", QueWrite:"+ STR$(EQ1NextWrite) + ", QueRead:" + STR$(EQ1NextRead) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ1WriteFlag, EQ1ReadFlag, EQ1NextWrite, EQ1NextRead ) ) PRINT "EQ1WriteFlag:" + STR$(EQ1WriteFlag) + ", EQ1ReadFlag:" + STR$(EQ1ReadFlag) PRINT PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ1WriteFlag, EQ1ReadFlag, EQ1NextWrite, EQ1NextRead ) ) PRINT "Queue:" + EQ1$(1) + ", QueWrite:"+ STR$(EQ1NextWrite) + ", QueRead:" + STR$(EQ1NextRead) PRINT "QueRead:" + STR$( EQ1Read() ) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ1WriteFlag, EQ1ReadFlag, EQ1NextWrite, EQ1NextRead ) ) PRINT "Queue:" + EQ1$(1) + ", QueWrite:"+ STR$(EQ1NextWrite) + ", QueRead:" + STR$(EQ1NextRead) PRINT "QueRead:" + STR$( EQ1Read() ) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ1WriteFlag, EQ1ReadFlag, EQ1NextWrite, EQ1NextRead ) ) PRINT "Queue:" + EQ1$(1) + ", QueWrite:"+ STR$(EQ1NextWrite) + ", QueRead:" + STR$(EQ1NextRead) PRINT "QueRead:" + STR$( EQ1Read() ) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ1WriteFlag, EQ1ReadFlag, EQ1NextWrite, EQ1NextRead ) ) PRINT "Queue:" + EQ1$(1) + ", QueWrite:"+ STR$(EQ1NextWrite) + ", QueRead:" + STR$(EQ1NextRead) PRINT "QueRead:" + STR$( EQ1Read() ) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ1WriteFlag, EQ1ReadFlag, EQ1NextWrite, EQ1NextRead ) ) PRINT "EQ1WriteFlag:" + STR$(EQ1WriteFlag) + ", EQ1ReadFlag:" + STR$(EQ1ReadFlag) PRINT "Queue:" + EQ1$(1) + ", QueWrite:"+ STR$(EQ1NextWrite) + ", QueRead:" + STR$(EQ1NextRead) PRINT "QueRead:" + STR$( EQ1Read() ) 'END Test Code '============================================================================== FUNCTION EQ1Read() 'GLOBAL EQ1$(1), EQ1Length, EQ1WriteFlag, EQ1ReadFlag, EQ1NextWrite, EQ1NextRead 'Has entire circular event queue previously been read ? IF NOT(EQ1WriteFlag XOR EQ1ReadFlag) AND (EQ1NextWrite = EQ1NextRead) THEN 'Yes, return NULL (undefined) event. EQ1Read = 0 ELSE 'No, return next consecutive unread byte. EQ1Read = ASC( MID$( EQ1$(1), (EQ1NextRead+1), 1 ) ) 'Point to next byte location to read in circular buffer EQIncr( EQ1NextRead, EQ1ReadFlag, EQ1Length ) ENDIF END FUNCTION 'EQ1Read '============================================================================== FUNCTION EQ1Write( EQEvent ) 'GLOBAL EQ1$(1), EQ1Length, EQ1WriteFlag, EQ1ReadFlag, EQ1NextWrite, EQ1NextRead 'Has entire circular event queue previously been written to ? IF (EQ1WriteFlag XOR EQ1ReadFlag) AND (EQ1NextWrite = EQ1NextRead) THEN 'Yes, failed to write byte, event queue full. EQ1Write = 0 ELSE 'No, write to next free location in circular buffer. EQ1$(1) = LEFT$(EQ1$(1), EQ1NextWrite) + CHR$(EQEvent) + RIGHT$( EQ1$(1), (EQ1Length - EQ1NextWrite - 1) ) 'Point to next byte location to write in circular event queue. EQIncr( EQ1NextWrite, EQ1WriteFlag, EQ1Length ) EQ1Write = 1 'Successful in writing byte. ENDIF END FUNCTION 'EQ1Write '============================================================================== SUB EQ1WriteSuccess( EQEvent ) IF EQ1Write( EQEvent ) = 0 THEN PRINT "Unable to store event " + STR$(EQEvent) + ", event queue full" ENDIF END SUB 'EQ1WriteSuccess '============================================================================== SUB EQIncr( NextLoc, Flag, Length ) ' Point to next circular event queue location. NextLoc = (NextLoc + 1) 'Has next circular event queue location exceeded physical queue length ? IF NextLoc = Length THEN 'Yes, point to first physical queue location. NextLoc = 0 'Toggle flag bit to indicate location pointer ' is back to the beginning of the queue. Flag = (Flag XOR 1) ENDIF END SUB 'EQIncr '============================================================================== FUNCTION EQEvntRdy( EQWriteFlag, EQ1ReadFlag, EQNextWrite, EQNextRead ) 'GLOBAL EQ1WriteFlag, EQ1ReadFlag, EQ1NextWrite, EQ1NextRead EQEvntRdy = (EQWriteFlag XOR EQ1ReadFlag) OR (EQNextWrite <> EQNextRead) END FUNCTION 'EQEvntRdy() '============================================================================== ---- **EVENTQ2.lib**: 'File....: EventQ2.Lib ' 'Author..: Simon Whittam ' 'Email...: s.whittam(at)xnet.co.nz ' 'Date....: 8th September 2013 ' 'Language: Maximite BASIC, v4.4 ' 'Purpose.: Create and initialise an instance of a First In, First Out circular event queue. ' The event queue saves single bytes in a small String Array and does not ' overwrite unread bytes. ' ' Intended for use with a event driven Finite State Machine. ' 'Version.: v1.1 ' 'License.: Attribution-NonCommercial-ShareAlike 3.0 Australia (CC BY-NC-SA 3.0 AU) ' 'Ref.....: https://en.wikipedia.org/wiki/Circular_buffer ' http://geoffg.net/MonoMaximite.html ' http://mmbasic.com/ ' http://creativecommons.org/licenses/by-nc-sa/3.0/au/ ' '============================================================================== 'BEGIN EventQ2.Lib - Initialisation 'Copy the "EventQ2.Lib - Initialisation" section to the beginning of the Main BASIC module. 'Unremark the NEXT line when using this file as as library file 'LIBRARY LOAD "EventQ2.Lib" OPTION BASE 0 'event queue data referenced as an offset from: 0 - (Length -1) 'Configure event queue as empty EQ2Length = 4 'Event queue length. EQ2WriteFlag = 1 'Gets toggled after writing to last location in event queue. EQ2ReadFlag = 1 'Gets toggled after reading last location in event queue. EQ2NextWrite = 0 'Next location to write a byte. EQ2NextRead = 0 'Next location to read a byte. DIM EQ2$(1) LENGTH EQ2Length 'Create a circular event queue using a string/byte array. EQ2$ = "FIFO" 'END EventQ2.Lib - Initialisation '============================================================================== 'BEGIN Test code 'Note: ' For an empty queue, the following variables are initialised as follows: ' EQ2WriteFlag = 1 ' EQ2ReadFlag = 1 ' EQ2NextWrite = 0 ' EQ2NextRead = 0 ' PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ2WriteFlag, EQ2ReadFlag, EQ2NextWrite, EQ2NextRead ) ) PRINT "EQWrite:" + STR$( EQ2Write( 49 ) ) PRINT "Queue:" + EQ2$(1) + ", QueWrite:"+ STR$(EQ2NextWrite) + ", QueRead:" + STR$(EQ2NextRead) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ2WriteFlag, EQ2ReadFlag, EQ2NextWrite, EQ2NextRead ) ) PRINT "EQWrite:" + STR$( EQ2Write( 50 ) ) PRINT "Queue:" + EQ2$(1) + ", QueWrite:"+ STR$(EQ2NextWrite) + ", QueRead:" + STR$(EQ2NextRead) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ2WriteFlag, EQ2ReadFlag, EQ2NextWrite, EQ2NextRead ) ) PRINT "EQWrite:" + STR$( EQ2Write( 51 ) ) PRINT "Queue:" + EQ2$(1) + ", QueWrite:"+ STR$(EQ2NextWrite) + ", QueRead:" + STR$(EQ2NextRead) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ2WriteFlag, EQ2ReadFlag, EQ2NextWrite, EQ2NextRead ) ) PRINT "EQWrite:" + STR$( EQ2Write( 52 ) ) PRINT "Queue:" + EQ2$(1) + ", QueWrite:"+ STR$(EQ2NextWrite) + ", QueRead:" + STR$(EQ2NextRead) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ2WriteFlag, EQ2ReadFlag, EQ2NextWrite, EQ2NextRead ) ) ' PRINT "EQWrite:" + STR$( EQ2Write( 53 ) ) EQ2WriteSuccess(53) PRINT "Queue:" + EQ2$(1) + ", QueWrite:"+ STR$(EQ2NextWrite) + ", QueRead:" + STR$(EQ2NextRead) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ2WriteFlag, EQ2ReadFlag, EQ2NextWrite, EQ2NextRead ) ) PRINT "EQ2WriteFlag:" + STR$(EQ2WriteFlag) + ", EQ2ReadFlag:" + STR$(EQ2ReadFlag) PRINT PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ2WriteFlag, EQ2ReadFlag, EQ2NextWrite, EQ2NextRead ) ) PRINT "Queue:" + EQ2$(1) + ", QueWrite:"+ STR$(EQ2NextWrite) + ", QueRead:" + STR$(EQ2NextRead) PRINT "QueRead:" + STR$( EQ2Read() ) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ2WriteFlag, EQ2ReadFlag, EQ2NextWrite, EQ2NextRead ) ) PRINT "Queue:" + EQ2$(1) + ", QueWrite:"+ STR$(EQ2NextWrite) + ", QueRead:" + STR$(EQ2NextRead) PRINT "QueRead:" + STR$( EQ2Read() ) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ2WriteFlag, EQ2ReadFlag, EQ2NextWrite, EQ2NextRead ) ) PRINT "Queue:" + EQ2$(1) + ", QueWrite:"+ STR$(EQ2NextWrite) + ", QueRead:" + STR$(EQ2NextRead) PRINT "QueRead:" + STR$( EQ2Read() ) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ2WriteFlag, EQ2ReadFlag, EQ2NextWrite, EQ2NextRead ) ) PRINT "Queue:" + EQ2$(1) + ", QueWrite:"+ STR$(EQ2NextWrite) + ", QueRead:" + STR$(EQ2NextRead) PRINT "QueRead:" + STR$( EQ2Read() ) PRINT "EvntRdy:" + STR$( EQEvntRdy( EQ2WriteFlag, EQ2ReadFlag, EQ2NextWrite, EQ2NextRead ) ) PRINT "EQ2WriteFlag:" + STR$(EQ2WriteFlag) + ", EQ2ReadFlag:" + STR$(EQ2ReadFlag) PRINT "Queue:" + EQ2$(1) + ", QueWrite:"+ STR$(EQ2NextWrite) + ", QueRead:" + STR$(EQ2NextRead) PRINT "QueRead:" + STR$( EQ2Read() ) 'END Test Code '============================================================================== FUNCTION EQ2Read() 'GLOBAL EQ2$(1), EQ2Length, EQ2WriteFlag, EQ2ReadFlag, EQ2NextWrite, EQ2NextRead 'Has entire circular event queue previously been read ? IF NOT(EQ2WriteFlag XOR EQ2ReadFlag) AND (EQ2NextWrite = EQ2NextRead) THEN 'Yes, return NULL (undefined) event. EQ2Read = 0 ELSE 'No, return next consecutive unread byte. EQ2Read = ASC( MID$( EQ2$(1), (EQ2NextRead+1), 1 ) ) 'Point to next byte location to read in circular buffer EQIncr( EQ2NextRead, EQ2ReadFlag, EQ2Length ) ENDIF END FUNCTION 'EQ2Read '============================================================================== FUNCTION EQ2Write( EQEvent ) 'GLOBAL EQ2$(1), EQ2Length, EQ2WriteFlag, EQ2ReadFlag, EQ2NextWrite, EQ2NextRead 'Has entire circular event queue previously been written to ? IF (EQ2WriteFlag XOR EQ2ReadFlag) AND (EQ2NextWrite = EQ2NextRead) THEN 'Yes, failed to write byte, event queue full. EQ2Write = 0 ELSE 'No, write to next free location in circular buffer. EQ2$(1) = LEFT$(EQ2$(1), EQ2NextWrite) + CHR$(EQEvent) + RIGHT$( EQ2$(1), (EQ2Length - EQ2NextWrite - 1) ) 'Point to next byte location to write in circular event queue. EQIncr( EQ2NextWrite, EQ2WriteFlag, EQ2Length ) EQ2Write = 1 'Successful in writing byte. ENDIF END FUNCTION 'EQ2Write '============================================================================== SUB EQ2WriteSuccess( EQEvent ) IF EQ2Write( EQEvent ) = 0 THEN PRINT "Unable to store event " + STR$(EQEvent) + ", event queue full" ENDIF END SUB 'EQ2WriteSuccess '============================================================================== ' 'SUB EQIncr( NextLoc, Flag, Length ) ' ' ' Point to next circular event queue location. ' NextLoc = (NextLoc + 1) ' ' ' 'Has next circular event queue location exceeded physical queue length ? ' ' IF NextLoc = Length THEN ' ' 'Yes, point to first physical queue location. ' NextLoc = 0 ' ' 'Toggle flag bit to indicate location pointer ' ' is back to the beginning of the queue. ' Flag = (Flag XOR 1) ' ENDIF ' 'END SUB 'EQIncr ' '============================================================================== ' 'FUNCTION EQEvntRdy( EQWriteFlag, EQReadFlag, EQNextWrite, EQNextRead ) ' ' EQEvntRdy = (EQWriteFlag XOR EQReadFlag) OR (EQNextWrite <> EQNextRead) ' 'END FUNCTION 'EQEvntRdy() ' '==============================================================================