' ********************************************************************
' *                    PIC firmware for <Gesta>                      *
' *                           Hub board                              *
' *                  coded by dr.Godfried-Willem Raes                *
' * https://www.logosfoundation.org/instrum_gwr/gesta/picworks       *
' *                  source code development directory:              *
' *                         Gesta_Hub.bas                            *  
' *                         Version 1.0                              *
' *                Proton Compiler version 3.5.8.1                   *
' ********************************************************************
' hardware: single side PCB 08.10.2025 - film for <Kazumi> used
' -------------------------------------------------------------------
' 15.10.2026: Start coding for <Gesta>
' 16.10.2026: Nmap lookup introduced.
' 19.10.2026: The board is the same as that used for Kazumi.
' 20.10.2026: motor code stemming from <Ekla> removed. Lite code debugged.
'             PCB finished.
' 26.10.2026: first firing up of the robot. Ctrl66 works, everything else remains dead.
'             he midi-out seems to work o.k., so the problem must be the beaterboards. 
' 27.10.2026: Only port A.0 seems to react on the damper-high pic...
'             HSROut kommando's met bewerkingen vervangen.
'             seems to work fine now. 
' 29.10.2026: sustain pedal support added
' 30.10.2026: lite automation worked out. There are bugs though...
' 01.11.2026: bug hunting...
'             DIM xxx as DWORD ACCESS is required for var's used in IRQ handlers.
' 02.11.2026: seems to work fine, but we notice some glitchy flashes on the lites.


Include "18F2620.inc"      'version for the <Gesta> board.         (40MHz)
'Include "18F2520.inc"     'also possible.                       (40MHz)
'Clear

' Mapping defines for midi-events on pin outputs and inputs:
    $define Power_Relay      PORTA.4     ' o.k. for <Gesta>
' Mosfet outputs:  
    $define Display          PORTA.5   ' Logos-display   noot 124
    $define Lite_LoLeft      PORTC.0   ' kan knipperen
    $define Lite_PWM2        PORTC.1   ' can pwm HPWM2  
    $define Lite_PWM1        PORTC.2   ' can pwm HPWM1                
    $define Lite_LoRight     PORTC.3   ' kan knipperen
    $define Lite_HiLeft      PORTC.4   ' kan knipperen 
    $define Lite_HiRight     PORTC.5   ' kan knipperen\
              
' inputs:    
    $define sensor1        PORTA.0     ' not used 
    $define sensor2        PORTA.1     ' not used  
' LED's:    
     $define GreenLed      PORTB.4       ' on/off   
     $define Debug_Led     PORTB.5       ' for testing - red led - watchdog
     $define Loopspeed     PORTB.0       ' B.3 and B.4 are NC
    
Declare All_Digital = True        ' makes all analog pins, digital for I/O
Clear SSPCON1.5                   ' make sure RC3 is free for use.
' configure the input and output pins:
TRISA = %11000011     'bits set to 0 are output, 1 = input  - bits 6 and 7 are the clock!
TRISB = %11100000     'bits 6 and 7 are for the ICP, bit 5 is the red LED
TRISC = %11000000     'RC6 en RC7 zijn USART  I/O and must be set to input     
Input sensor1        
Input sensor2     
   
'constant definitions:
Symbol fPwm  = 21000   ' PWMminF
    ' midi note mapping
Symbol LoLeft_note = 120   
Symbol LoRight_note= 121    
Symbol HiLeft_note = 122  
Symbol HiRight_note = 123 
Symbol PWM1_note = 124       ' nc
Symbol PWM2_note = 125       ' nc
Symbol Display_note = 126     
   

Symbol lowest_note = 60 
Symbol highest_note = 108 
Symbol NrTasks = 4         ' 0 - 3
Symbol LastTask = NrTasks - 1

' constants:
Symbol True = %1
Symbol False = %0
Symbol CC19_default = 64      ' new 30.10.2026 - release time for dampers
  
'initialisations for the midi input parser:
Symbol Midichannel = 3                         ' Gesta channel - only on hubboard!
Symbol MidiLowNoteChannel = 0                  ' output channels
Symbol MidiHighNoteChannel = 1
Symbol MidiLowDampChannel = 2
Symbol MidiHighDampChannel = 3
Symbol NoteOff_Status = 128 + Midichannel      ' 2 bytes follow
Symbol NoteOn_Status  = 144 + Midichannel
Symbol Keypres_Status = 160 + Midichannel
Symbol Control_Status = 176 + Midichannel
Symbol ProgChange_Status = 192 + Midichannel   ' 1 byte message
Symbol Aftertouch_Status = 208 + Midichannel   ' 1 byte follows
Symbol Pitchbend_Status  = 224 + Midichannel   ' lsb msb follow
' Setup the USART
Declare Hserial_Baud = 31250                   ' Set baud rate for the USART to MIDI specs.
Declare Hserial_TXSTA = 0x24                   ' instead of the normal 0x20 - ?? 0x24               
Declare Hserial_Clear = On                     ' should clear on errors. Bytes get lost of course...
' Create variables:
    Dim Cnt As Dword Access                    ' 1 cnt-unit ought to be 24 us.
    Dim CntHw As Cnt.Word1                     ' used in the timer0 interrupt, to create a 32 bit timer
    Dim CntLw As TMR0L.Word                    ' this is the trick to read both TMR0L and TMR0H
                                               ' it makes Cntlw the low word of cnt
                                               ' We still have to copy the contents of Lw to Cnt
    Dim time As Cnt                            ' was : Dword  'frozen copy of Cnt
    Dim MaxTim As time.28                      ' overflow flag used to reset timer and to power down
                                               ' time.30 is 7 hours.
                                               ' time.29 is 214 minutes or 3h30
                                               ' time.28 would be 107 minutes - checked and found o.k.
 
    Dim T3 As Dword Access                     ' make 32-bit timer
    Dim Tim3Hw As T3.Word1 
    Dim Tim3 As TMR3L.Word                     ' same trick for timer3 , 16-bits
    Dim LiteCnt As Byte Access                 ' used in low priority interrupt for timer 3
    Dim LiteTog As Byte Access
    Dim Tog As LiteTog.0                       ' single Bit 
    
    Dim Bytein  As Byte Access                    ' midi byte read from buffer
    Dim StBit As Bytein.7                         ' highest bit of ByteIn
    Dim i As Byte                                 ' general purpose counter
    ' midi variables
    Dim statusbyte As Byte System
    Dim noteUit As Byte System                    ' note off + release value
    Dim release As Byte System
    Dim noteAan As Byte System                    ' note on + release value
    Dim velo As Byte System
    Dim notePres As Byte                   ' note pressure + pressure value
    Dim pres As Byte 
    Dim Ctrl As Byte System                       ' continuous controller + value
    Dim value As Byte System
    Dim prog As Byte                        ' program change + program-byte
    Dim aft  As Byte                        ' channel aftertouch
    Dim pblsb As Byte                       ' pitch bend lsb
    Dim pbmsb As Byte                       ' pitch bend msb
    
    Dim note As Byte System
    Dim kanaal As Byte System
    Dim CC64 As Byte Access                     ' sustain pedal
    Dim Sustain As CC64                         ' alias
    Dim CC19 As Byte System                     ' release controller - not forwarded!    
    Dim CC66 As Byte Access                     ' global on/off switch
        Dim PowerOn As CC66.0                   ' handled on the hub board.
    Dim CC69 As Byte Access                     ' lite automation    
    Dim TimVals[NrTasks] As Dword         ' lijst met timer waarden - de kleinste is eerst aan de beurt
    Dim Nxt As Dword System               ' waarde voor de eerstvolgende timer
    Dim idx As Byte System                ' index voor de eerstvolgende timer 
    Dim j As Float
    ' for light automation:   
    Dim LiteFlags As Byte                 ' for autorepeats and lite-flashing
    Dim Lit As Byte                       ' for reading Lmap[]
    Dim Lites As Dword Access             ' used in irq3
    Dim LowModule As Lites.Word0
    Dim HighModule As Lites.Word1             
    Dim LoLeft As LowModule.Byte0        ' nrnotes!
    Dim LoRight As LowModule.Byte1
    Dim HiLeft As HighModule.Byte0
    Dim HiRight As HighModule.Byte1 
    
    Dim Pres0 As Byte
    Dim Pres1 As Byte
    Dim Pres2 As Byte
    Dim Pres3 As Byte
    Dim Pres4 As Byte
    Dim Pres5 As Byte
'-----------------------------------------------------------------------------------------
' Load the USART Interrupt handler and buffer read subroutines into memory
Dim Ringbuffer[256] As Byte           ' Array for holding received characters
Include "Gesta_Hub_Irq.inc"           ' for UART,Timer0, Timer3 Interrupt
Dim Dur5[128] As Word                 ' duration lookup for lite flashing repetitions
Dim Nmap[128] As Byte
Dim Lmap[128] As Byte                 ' voor de 4 lichtjes

' Main program starts here:-----------------------------------------------------------
MAIN:
    Low Power_Relay
    High Debug_Led        ' we must make sure the port is set to output
    Clear CC66            ' all flags
    Clear CC64            ' sostenuto pedal  = clear sustain
    Set CC69              ' by default light automation is ON
    Clear Lites           ' dword
    Low PORTA.2       ' not used
    Low PORTA.3
    Low GreenLed
    Low Lite_LoRight
    Low Lite_LoLeft
    Low Lite_HiLeft
    Low Lite_HiRight   
    Low Lite_PWM1
    Low Lite_PWM2  
    Low Display   
    Clear LiteFlags
    Set TimVals                      ' no timers running on startup
    
    Init_Usart_Interrupt ()          ' Initiate the USART serial buffer interrupt
                                     ' this procedure is in the include file
    Clear_Serial_Buffer ()           ' Clear the serial buffer and reset its pointers
                             
                                     ' in the include as well
                                     ' Configure Timer0 for:
'                                      Clear TMR0L and TMR0H registers
'                                      Interrupt on Timer0 overflow
'                                      16-bit operation
'                                      Internal clock source  40MHz
'                                      1:256 Prescaler : thus 40MHz / 256 = 156.250kHz
'                                      6.4 us per clock-tick
         Clear T1CON
         Clear IntConBits_T0IF      ' clear interrupt flag
         Set INTCONBITS_T0IE        ' enable interrupt on overflow 
         T0CON = %10000111           ' bit 7 = enable/disable
                                     ' bit 6 = 1=8 bit, 0=16 bit
                                     ' bit 5 = 1 pin input, 0= Internal Clk0
                                     ' bit 4 = HL or LH transition when bit5 =1 
                                     ' bit 3 = 1= bypass prescaler, 0= input from prescaler
                                     ' bit 2-0 = prescaler select: 111= 1:256
                                     ' Setup the High priorities for the interrupts
' TIMER1: if enabled, all midi-in is blocked, so it must interfere with the UART                                     
             ' Configure Timer1 for:
'                        Clear TMR1L and TMR1H registers
'                        Interrupt on Timer1 overflow
'                        16-bit read/write mode
'                        Internal clock source
'                        1:8 Prescaler
'    OpenTimer1(TIMER_INT_ON & T1_16BIT_RW & T1_SOURCE_INT & T1_PS_1_8)    ' dit kompileert o.k.                               
' TIMER2: if enabled, the UART stops working...
'    Opentimer2 (Timer_Int_On & T2_POST_1_16 & T2_PS_1_16)     ' dit lukt... maar de timer is nodig voor de UART...
' TIMER3:                                                              
' Configure Timer3 for:                     
'                        Interrupt on Timer3 overflow
'                        16-bit read/write mode
'                        Internal clock source
'                        1:8 Prescaler
'                        Dont sync external clock input

'Period = Period_default      ' controller 67

 ' doing it this way seems to work:
    Clear T3CON
    Clear PIR2BITS_TMR3IF   ' clear IRQ flag
    Set PIE2BITS_TMR3IE     ' irq on
    Clear Tim3              ' Clear TMR3L And TMR3H registers
   ' Tim3 = Period           ' period5 = 59288            ' 5 ms - measured and found correct.
    Set RCONbits_IPEN       ' Enable priority interrupts
    Clear IPR2bits_TMR3IP   ' Set Timer3 as a low priority interrupt source                   
    ' we can also set T3Con in one instruction as:
    T3CON = %10110001               ' oef, now it works...
                            ' bit 7 = 16 bit mode
                            ' bit 6,3 = 0, 0
                            ' bit 5,4 = 1:8 prescale
                            ' bit 2 = 0
                            ' bit 1 = 0 Internal clock = Fosc/4
                            ' bit 0 : 1= enable timer 3, 0= disable
                            ' maximum count = 52.42ms, 1 tick =0.8uS, freq.=19Hz
    ' Set up priority interrupts.
  '  IPR1bits_TMR1IP = 0    ' Set Timer1 as a low priority interrupt source
  '  INTCONbits_PEIE = 1    ' Enable peripheral interrupts
  '  INTCONbits_GIE = 1     ' Enable global interrupts                        
    Set PIE2Bits_TMR3IE     ' enable irq
    Set T3CON.0           ' enable timer 3
Dur_Lookup ()                  
Note_Map ()               ' lookup table for notes/channels
Lite_map ()               ' lookup for the lights

idx = SortTimers () 

    ' just a dummy
HRSOut Control_Status, 80, 0         ' should do nothing      

    ' start the main program loop:
Do                                   
        Cnt.Word0 = CntLw            ' read timer 0
                                     ' now time is an alias for Cnt
        Bytein = GetMidiIn ()        ' Read data from the serial buffer, with no timeout
                                     ' Start the midi parser.                                                                                   
        Midi_Parse:
            If Bytein > Control_Status Then    ' here higher statusses are not implemented.
                If Bytein > 253 Then       '254 = midiclock, 255= reset
                                           'midiclock can interrupt all other msg's...
                                           '255 had to be intercepted since thats what we
                                           'get when no new byte flows in.
                Else
                    Clear statusbyte       'reset the status byte
                End If
                GoTo Check_Timers          'throw away...
            EndIf
            If StBit =1 Then               'should be faster than If Bytein > 127 Then
                                           'status byte received, bit 7 is set
                Clear statusbyte           'if on another channel, the statusbyte needs a reset
                Select Bytein              'eqv to Select case ByteIn
                       Case NoteOff_Status
                            statusbyte = Bytein
                            Set noteUit          'reset value. Cannot be 0 !!!
                            Set release          '0 is a valid midi note!
                       Case NoteOn_Status
                            statusbyte = Bytein
                            Set noteAan             '= 255
                            Set velo                '= 255
                       Case Keypres_Status          
                            statusbyte = Bytein
                            Set notePres 
                            Set pres 
                       Case Control_Status          ' controllers and switches
                            statusbyte = Bytein
                            Set Ctrl 
                            Set value 
'                       Case ProgChange_Status       ' can be used for different lookup tables
'                            statusbyte = Bytein
'                            Set prog
'                       Case Aftertouch_Status        ' not used on this board
'                            statusbyte = Bytein
'                            Set aft 
'                       Case Pitchbend_Status        
'                            statusbyte = Bytein
'                            Set pblsb 
'                            Set pbmsb
                EndSelect
            Else                                        'midi byte is 7 bits
                Select statusbyte
                       Case 0                           'not a message for this channel
                            GoTo Check_Timers           'disregard
                       Case NoteOff_Status
                            If noteUit = 255 Then
                                noteUit = Bytein
                            Else
                                release = Bytein        'message complete, so we can do the action...
                                If PowerOn = 1 Then
                                    Select noteUit
                                       Case 60 To 96    ' dempers
                                            note = Nmap[noteUit]      ' 0 - 25
                                                If note.7 = 0 Then
                                                    kanaal = 128 + MidiLowDampChannel        ' channel 2
                                                Else
                                                    kanaal = 128 + MidiHighDampChannel       ' channel 3
                                                    Clear note.7
                                                EndIf
                                                If CC64 = 0 Then          ' sustain pedal off
                                                     If release = 0 Then release = CC19       
                                                     HRSOut kanaal, note, release  
                                                'Else
                                                     ' doe niks, geen dempers!
                                                     ' we moeten wel lichtjes doven echter!!!
                                                EndIf
                                                ' check the lights-bits
                                                Lit = Lmap[noteUit]    
                                                Select Lit
                                                    Case 0
                                                        If LoLeft > 0 Then Dec LoLeft   ' nr.notes low left
                                                    Case 1
                                                        If LoRight > 0 Then Dec LoRight 
                                                    Case 2
                                                        If HiLeft > 0 Then Dec HiLeft
                                                    Case 3
                                                        If HiRight > 0 Then Dec HiRight
                                                EndSelect
                                       Case 97 To 108 ' alleen voor de lichtjes!
                                                Lit = Lmap[noteUit]
                                                Select Lit
                                                    Case 0
                                                        If LoLeft > 0 Then Dec LoLeft   ' nr.notes low left
                                                    Case 1
                                                        If LoRight > 0 Then Dec LoRight 
                                                    Case 2
                                                        If HiLeft > 0 Then Dec HiLeft
                                                    Case 3
                                                        If HiRight > 0 Then Dec HiRight
                                                EndSelect                                                                                                
                                       Case LoLeft_note       ' 120            
                                             Clear Lite_LoLeft  
                                             Clear LiteFlags.0
                                             Clear LoLeft
                                             Set TimVals[0]
                                             idx = SortTimers ()                                
                                       Case LoRight_note                  
                                             Clear Lite_LoRight
                                             Clear LiteFlags.1
                                             Clear LoRight
                                             Set TimVals[1]
                                             idx = SortTimers ()
                                       Case HiLeft_note       ' 122            
                                             Clear Lite_HiLeft  
                                             Clear LiteFlags.2
                                             Clear HiLeft
                                             Set TimVals[2]
                                             idx = SortTimers ()                                   
                                       Case HiRight_note                  
                                             Clear Lite_HiRight
                                             Clear LiteFlags.3
                                             Clear HiRight
                                             Set TimVals[3]
                                             idx = SortTimers ()                                             
                                       Case PWM1_note         
                                             HPWM 1, 0, fPwm       
                                       Case PWM2_note
                                             HPWM 2, 0, fPwm
                                       Case Display_note        
                                             Clear Display                                                                                                                
                                    EndSelect
                                EndIf
                                Set noteUit '= 255                         'reset
                            EndIf
                       Case NoteOn_Status
                            If noteAan = 255 Then
                                noteAan = Bytein
                            Else
                                velo = Bytein
                                If velo = 0 Then                      ' note off via velo=0
                                    If PowerOn = 1 Then
                                    Select noteAan
                                       Case 60 To 96  ' dempers
                                            note = Nmap[noteAan]      ' 0 - 25
                                                If note.7 = 0 Then
                                                    kanaal = 128 + MidiLowDampChannel        ' channel = 2
                                                Else
                                                    kanaal = 128 + MidiHighDampChannel       ' channel = 3
                                                    Clear note.7
                                                EndIf
                                                If CC64 = 0 Then          ' pedal off
                                                     If release = 0 Then release = CC19       
                                                     HRSOut kanaal, note, release  
                                                'Else
                                                     ' doe niks, geen dempers!
                                                EndIf
                                                Lit = Lmap[noteAan]
                                                ' check the lights-bits
                                                Select Lit
                                                    Case 0
                                                        If LoLeft > 0 Then Dec LoLeft   ' nr.notes low left
                                                    Case 1
                                                        If LoRight > 0 Then Dec LoRight 
                                                    Case 2
                                                        If HiLeft > 0 Then Dec HiLeft
                                                    Case 3
                                                        If HiRight > 0 Then Dec HiRight
                                                EndSelect
                                       Case 97 To 108 ' alleen voor de lichtjes!
                                                Lit = Lmap[noteAan]
                                                Select Lit
                                                    Case 0
                                                        If LoLeft > 0 Then Dec LoLeft   ' nr.notes low left
                                                    Case 1
                                                        If LoRight > 0 Then Dec LoRight 
                                                    Case 2
                                                        If HiLeft > 0 Then Dec HiLeft
                                                    Case 3
                                                        If HiRight > 0 Then Dec HiRight
                                                EndSelect                                                                                                
                                       Case LoLeft_note       ' 120            
                                             Clear Lite_LoLeft  
                                             Clear LiteFlags.0
                                             Clear LoLeft
                                             Set TimVals[0]
                                             idx = SortTimers ()                                   
                                       Case LoRight_note                  
                                             Clear Lite_LoRight
                                             Clear LiteFlags.1
                                             Clear LoRight
                                             Set TimVals[1]
                                             idx = SortTimers ()
                                       Case HiLeft_note       ' 122            
                                             Clear Lite_HiLeft  
                                             Clear LiteFlags.2
                                             Clear HiLeft
                                             Set TimVals[2]
                                             idx = SortTimers ()                                   
                                       Case HiRight_note                  
                                             Clear Lite_HiRight
                                             Clear LiteFlags.3
                                             Clear HiRight
                                             Set TimVals[3]
                                             idx = SortTimers ()                                             
                                       Case PWM1_note         
                                             HPWM 1, 0, fPwm       
                                       Case PWM2_note
                                             HPWM 2, 0, fPwm
                                       Case Display_note        
                                             Clear Display                                                                                                                                                                                                              
                                    EndSelect
                                    EndIf
                                Else                                       ' else for if velo = 0
                                    If PowerOn = 1 Then                    ' dont do lights nor send note-on's if power is off
                                    Select noteAan
                                           Case 60 To 108 
                                                note = Nmap[noteAan]
                                                ' here we first clear dampers that may be on, than we send the note-on to
                                                ' the beater solenoids
                                                If note.7 = 0 Then
                                                    If Sustain = 0 Then                   ' = CC64
                                                        kanaal = 144 + MidiLowDampChannel   ' this is correct, 144 note on releases the damper
                                                        HRSOut kanaal, note, 85           ' channel = 2, for dampers
                                                                                          ' velo is irrelevant here, 85 = %01010101 
                                                    EndIf
                                                    kanaal = 144 + MidiLowNoteChannel   ' channel = 0
                                                Else
                                                    Clear note.7
                                                    If Sustain = 0 Then
                                                        kanaal = 144 + MidiHighDampChannel                                                  
                                                        HRSOut kanaal, note, 85           ' channel = 3, for dampers
                                                    EndIf
                                                    kanaal = 144 + MidiHighNoteChannel  ' channel = 1
                                                EndIf       
                                                HRSOut kanaal, note, velo               ' en nu pas sturen we de note-on
                                                ' lichtjes:
                                                Lit =  Lmap[noteAan]
                                                Select Lit
                                                    Case 0
                                                        Set Lite_LoLeft
                                                        Inc LoLeft   ' nr.notes low left
                                                    Case 1
                                                        Set Lite_LoRight
                                                        Inc LoRight 
                                                    Case 2
                                                        Set Lite_HiLeft
                                                        Inc HiLeft
                                                    Case 3
                                                        Set Lite_HiRight
                                                        Inc HiRight
                                                EndSelect                                                                                    
                                           Case LoLeft_note       ' 120            
                                                 Set Lite_LoLeft  
                                                 Set LiteFlags.0
                                                 Pres0 = velo
                                                 If velo < 127 Then
                                                    TimVals[0] = time + Dur5[velo]
                                                    idx = SortTimers ()
                                                 EndIf                               
                                           Case LoRight_note                  
                                                 Set Lite_LoRight
                                                 Set LiteFlags.1
                                                 Pres1 = velo
                                                 If velo < 127 Then
                                                    TimVals[1] = time + Dur5[velo]
                                                    idx = SortTimers ()
                                                 EndIf
                                           Case HiLeft_note       ' 122            
                                                 Set Lite_HiLeft  
                                                 Set LiteFlags.2
                                                 Pres2 = velo
                                                 If velo < 127 Then
                                                    TimVals[2] = time + Dur5[velo]
                                                    idx = SortTimers ()  
                                                 EndIf                                 
                                           Case HiRight_note                  
                                                 Set Lite_HiRight
                                                 Set LiteFlags.3
                                                 Pres3 = velo
                                                 If velo < 127 Then
                                                    TimVals[3] = time + Dur5[velo]
                                                    idx = SortTimers ()   
                                                 EndIf                                          
                                           Case PWM1_note 
                                                 velo = velo << 1  
                                                 Inc velo      
                                                 HPWM 1, velo, fPwm       
                                           Case PWM2_note
                                                 velo = velo << 1
                                                 Inc velo
                                                 HPWM 2, velo, fPwm
                                           Case Display_note          ' 126
                                                 Set Display                                            
                                        EndSelect     
                                    EndIf                                    
                                    Set noteAan                 
                                EndIf  
                            EndIf
                       Case Keypres_Status           
                            If notePres = 255 Then
                                notePres = Bytein       ' = note
                            Else
                                pres = Bytein
                                KeyPres ()     
                                ' changing flashing speed or brightness on the lites required this!
                            EndIf
                       Case Control_Status            'this is where the action takes place for controllers
                            If Ctrl = 255 Then
                                Ctrl = Bytein
                            Else
                                value = Bytein
                                Select Ctrl                    ' this is filtered though.
                                    Case 64                    ' sustain pedal
                                         CC64 = value
                                         kanaal = 176 + MidiLowDampChannel
                                         HRSOut kanaal, 64, value
                                         kanaal = 176 + MidiHighDampChannel
                                         HRSOut kanaal, 64, value
                                    Case 66  
                                         kanaal = 176 + MidiLowNoteChannel      
                                         HRSOut kanaal, 66, value   
                                         kanaal = 176 + MidiHighNoteChannel
                                         HRSOut kanaal, 66, value 
                                         kanaal = 176 + MidiLowDampChannel
                                         HRSOut kanaal, 66, value 
                                         kanaal = 176 + MidiHighDampChannel
                                         HRSOut kanaal, 66, value 
                                    Case 123     
                                         kanaal = 176 + MidiLowNoteChannel      
                                         HRSOut kanaal, 123, 0
                                         kanaal = 176 + MidiHighNoteChannel
                                         HRSOut kanaal, 123, 0
                                         kanaal = 176 + MidiLowDampChannel
                                         HRSOut kanaal, 123, 0 
                                         kanaal = 176 + MidiHighDampChannel
                                         HRSOut kanaal, 123, 0                                 
                                EndSelect
                                Controller ()  ' handles all controllers relevant for this board only
                            EndIf
                       Case ProgChange_Status                 'not implemented on <Gesta>
                            If prog = 255 Then                'single byte message
                                 prog = Bytein          
                                 Set prog
                            EndIf
                        Case Aftertouch_Status                'not implemented on <Gesta>
                             If aft = 255 Then
                                  aft = Bytein
                                  Set aft
                             EndIf
                       Case Pitchbend_Status                  'not implemented
                            If pblsb = 255 Then
                                pblsb = Bytein
                            Else
                                pbmsb = Bytein
                                Set pblsb
                            EndIf                    
                EndSelect
            EndIf

Check_Timers:
If idx < NrTasks Then          ' we moeten alleen checken wanneer er een timer loopt
   If time >= Nxt Then         ' nagaan of de eerstvolgende timer afgelopen is...
        ' in dit geval is de eerste timer afgelopen en moeten we de juiste aktie ondernemen:
        Set Nxt.31                                    ' timer reset, is immers afgelopen
        ' aan de hand van idx weten we welke timer dit is
        Select idx   
               Case 0                             
                     If LiteFlags.0 = 0 Then
                       Clear Lite_LoLeft
                       Set TimVals[0]
                     Else
                       Toggle Lite_LoLeft
                       TimVals[0] = time + Dur5[Pres0] 
                     EndIf
                     idx = SortTimers ()
               Case 1
                     If LiteFlags.1 = 0 Then
                       Clear Lite_LoRight           
                       Set TimVals[1]
                     Else
                       Toggle Lite_LoRight
                       TimVals[1] = time + Dur5[Pres1] 
                     EndIf
                     idx = SortTimers () 
               Case 2
                     If LiteFlags.2 = 0 Then
                       Clear Lite_HiLeft
                       Set TimVals[2]
                     Else
                       Toggle Lite_HiLeft
                       TimVals[2] = time + Dur5[Pres2] 
                     EndIf
                     idx = SortTimers ()  
               Case 3
                     If LiteFlags.3 = 0 Then
                       Clear Lite_HiRight
                       Set TimVals[3]
                     Else
                       Toggle Lite_HiRight
                       TimVals[3] = time + Dur5[Pres3] 
                     EndIf
                     idx = SortTimers ()                                           
               'Case Else
               '     ' in dit geval is idx geset
               '     GoTo jumpout               
        EndSelect
   EndIf
      ' beveiliging tegen overflow crashes...
      ' in dit geval loopt er weliswaar een timer!
      If MaxTim = 1 Then
                    Clear time
                    Clear Lites
                    Set TimVals
                    idx = SortTimers ()
      EndIf
Else
      ' idx > LastTask, no timers running, so to avoid overflows, we can reset the loop timer
      If MaxTim = 1 Then Clear time    
EndIf        
              
  Btg PORTB.0     ' average loop-speed: 
                  ' 22.08.2026, Ekla : 121kHz
                  ' 02.11.2026, Gesta : 117kHz
Loop
     

Proc SortTimers (),Byte
        'look up the next smallest timer value in the Timvals array
        ' zoek de de volgende kleinste timer waarde:
        Set Result           ' was idx, byte
        Set Nxt.31           ' nxt is set on entry. - for speed, we just set the highest bit
        For i = 0 To LastTask           
            If TimVals[i] < Nxt Then
               Nxt = TimVals[i]         
               Result = i
            EndIf
        Next i
EndProc

Proc KeyPres () 
    ' 06.08.2026: To be implemented for <Gesta> !!!
    ' 25.08.2026: done.
    Select notePres
        'Case lowest_note To highest_note       
        '    HRSOut Keypres_Status, notePres, pres
        ' could we steer noterepetitions from here?
        Case LoLeft_note
            Pres0 = pres
        Case LoRight_note
            Pres1 = pres
        Case HiLeft_note
            Pres2 = pres
        Case HiRight_note
            Pres3 = pres
        Case PWM1_note
            pres = pres + pres 
            Inc pres
            HPWM 1, pres, fPwm
        Case PWM2_note
            pres = pres + pres
            Inc pres
            HPWM 2, pres, fPwm
    EndSelect                
    Set notePres 
EndProc

Proc ProgChange ()
    ' not implemented on this board nor on <Ekla>
    Set prog 
EndProc

Proc Pitchbend ()
    ' used on <Ekla> not on <Gesta>
    Set pblsb 
EndProc

Proc Aftertouch ()
     'this is the channel aftertouch, affecting any playing note
     'not implemented here.
    Set aft                             
EndProc

Proc Controller ()
    Select Ctrl
           Case 19
                ' release controller.  - only on this board!
                release = value + 1
                If release > 127 Then release = 127
                CC19 = release              ' 1-127
           Case 64
                If value > 0 Then
                    Sustain = 1
                Else
                    Sustain = 0
                EndIf
                'CC64 = Sustain
           Case 66
                'on/off for the robot
                ' this also goes to the note boards!
                If value = 0 Then
                    PowerDown ()
                Else
                    Set Power_Relay      ' stroom AAN via optorelais
                    Set GreenLed 
                    Set PowerOn      ' CC66.0  =1
                    CC19 = CC19_default
                    Clear Lites
                    Set CC69         ' auto lights on
                    Clear Sustain
                EndIf
           Case 69 
                CC69 = value         ' lite-automation
                Clear Lites          ' dword
                If CC69 = 0 Then
                    Clear Display     ' lite 126
                    Clear Lite_LoRight
                    Clear Lite_LoLeft
                    Clear Lite_HiRight
                    Clear Lite_HiLeft                
                EndIf
           Case 123
                ' all notes/lites off - no controller resets 
                Clear Display     ' lite 126
                Clear Lite_LoRight
                Clear Lite_LoLeft
                Clear Lite_HiRight
                Clear Lite_HiLeft
                Clear Lites
                HPWM 1, 0, fPwm   
                HPWM 2, 0, fPwm   
                Set TimVals  
               ' Set TimVals[0]
               ' Set TimVals[1]
               ' Set TimVals[2]
               ' Set TimVals[3]
                idx = SortTimers () 
    EndSelect
    Set Ctrl                                  'mandatory reset
EndProc

Proc PowerDown ()
               Clear Power_Relay      ' power and motor off
               Clear PowerOn          ' CC66.0  =0               
               Set TimVals            ' cancel all timers
               Clear time
               Clear Lites
               Clear LiteFlags
               Clear GreenLed
               Clear Lite_LoRight            
               Clear Lite_LoLeft
               Clear Lite_HiRight
               Clear Lite_HiLeft
               Clear Display
               HPWM 2,0,fPwm          
               HPWM 1,0,fPwm
               CC19 = CC19_default
               Clear Sustain
               idx = SortTimers ()    ' idx should be 255 here.
EndProc

Proc Dur_Lookup ()
    'lookup for flashing lights:
    Set Dur5[0]         
    Dur5[127] =  17218  
    j = Dur5[127] / 127
    For i = 1 To 127
        Dur5[i] = Dur5[0] - (j * i)
    Next i                                      
EndProc  

Proc Note_Map ()
    ' note remapping for <Gesta>
    ' bit 7 is always 1 channel higher
    ' we can use the same lookup for the dampers.
    Set Nmap
    Nmap[60] = 0          ' low module
    Nmap[61] = 1
    Nmap[62] = 128 + 0    ' high module
    Nmap[63] = 128 + 1
    Nmap[64] = 2
    Nmap[65] = 3
    Nmap[66] = 128 + 2
    Nmap[67] = 128 + 3
    Nmap[68] = 4
    Nmap[69] = 5
    Nmap[70] = 128 + 4
    Nmap[71] = 128 + 5
    Nmap[72] = 6
    Nmap[73] = 7
    Nmap[74] = 128 + 6
    Nmap[75] = 128 + 7
    Nmap[76] = 8
    Nmap[77] = 9
    Nmap[78] = 128 + 8
    Nmap[79] = 128 + 9
    Nmap[80] = 10
    Nmap[81] = 11
    Nmap[82] = 128 + 10
    Nmap[83] = 128 + 11 
    Nmap[84] = 12
    Nmap[85] = 13
    Nmap[86] = 128 + 12
    Nmap[87] = 128 + 13
    Nmap[88] = 14
    Nmap[89] = 15
    Nmap[90] = 128 + 14
    Nmap[91] = 128 + 15     
    Nmap[92] = 16
    Nmap[93] = 17
    Nmap[94] = 128 + 16
    Nmap[95] = 128 + 17
    Nmap[96] = 18
    Nmap[97] = 19
    Nmap[98] = 128 + 18
    Nmap[99] = 128 + 19     
    Nmap[100] = 20
    Nmap[101] = 21
    Nmap[102] = 128 + 20
    Nmap[103] = 128 + 21
    Nmap[104] = 22
    Nmap[105] = 23
    Nmap[106] = 128 + 22
    Nmap[107] = 128 + 23
    Nmap[108] = 24
EndProc

Proc Lite_map ()
    Set Lmap
    Lmap[60] = 0
    Lmap[61] = 0
    Lmap[62] = 2
    Lmap[63] = 2
    Lmap[64] = 0
    Lmap[65] = 0
    Lmap[66] = 2
    Lmap[67] = 2
    Lmap[68] = 0
    Lmap[69] = 0
    Lmap[70] = 2
    Lmap[71] = 2
    Lmap[72] = 0
    Lmap[73] = 0
    Lmap[74] = 2
    Lmap[75] = 2  
    Lmap[76] = 0
    Lmap[77] = 0
    Lmap[78] = 2
    Lmap[79] = 2
    Lmap[80] = 0
    Lmap[81] = 0
    Lmap[82] = 2
    Lmap[83] = 2              
    Lmap[84] = 0
    Lmap[85] = 1
    Lmap[86] = 3
    Lmap[87] = 3
    Lmap[88] = 1
    Lmap[89] = 1
    Lmap[90] = 3
    Lmap[91] = 3
    Lmap[92] = 1
    Lmap[93] = 1
    Lmap[94] = 3
    Lmap[95] = 3
    Lmap[96] = 1
    Lmap[97] = 1
    Lmap[98] = 3
    Lmap[99] = 3
    Lmap[100] = 1
    Lmap[101] = 1
    Lmap[102] = 3
    Lmap[103] = 3
    Lmap[104] = 1
    Lmap[105] = 1
    Lmap[106] = 3
    Lmap[107] = 3
    Lmap[108] = 1
EndProc
'[EOF]

