Welcome Guest, you are in: Login

Fruit Of The Shed

Navigation (MMBasic)






Search the wiki

»


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.

Note: Any required file(s) are available in the attachments tab (top right).

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()
'
'==============================================================================