'***************************************************
'           IRQ coding for midi-in                 *
'                    by                            *
'             Godfried-Willem Raes                 *
'                 Version 2.8                      *
'***************************************************
' Interrupt-driven serial buffer for USART MIDI receive using a hardware high priority interrupt.
' 08.10.2025 - Version for the <Ubu> robot
' 05.08.2026: adapting for <Ekla>
' 20.10.2026: adapted for <Gesta>

                ' Create the SYSTEM variables and aliases for the Buffered commands
    Dim PP0     As Byte System                   ' \
    Dim PP0H    As Byte System                   ' / Storage for FSR0L\H registers
    Dim FSR0SaveInt As PP0                       ' alias
    Dim FSR0SaveIntH As PP0H                     ' alias
    Dim USART_FSR0_Save As FSR0SaveInt.Word      ' Alias the FSR0L\H storage to PP0\H
    Dim USART_FSR1_Save As Word System           ' Storage for FSR1L\H registers
    Dim USART_FSR0 As FSR0L.Word                 ' alias
    Dim USART_FSR1 As FSR1L.Word                 ' alias
    Dim IndexIn As Byte System                   ' Pointer to the next empty location in the buffer
    Dim IndexOut As Byte System                  ' Pointer to the location of the oldest character in the buffer
   Reminders = On
   Warnings = On

' Point to the high priority hardware interrupt handler
On_Hardware_Interrupt GoTo High_Prior_Interrupt
On_Low_Interrupt GoTo Low_Prior_Interrupt 

GoTo _Over_IRQ_Handler                           ' Jump over the subroutines

High_Prior_Interrupt:
_Timer0_IRQ:
     If INTCONbits_T0IF = 1 Then
        Clear INTCONbits_T0IF                         ' Clear the Timer0 Overflow flag
        Inc Cnt.Word1                                 ' would it be slower to do Inc Cnt.Word1 instead of CntHw here?
        Cnt.Word0 = CntLw                             ' 08.12.2019
        Btg Debug_Led                                 ' asm bit toggle - debug led watchdog.Port must be set to output!!!
        Retfie Fast
      EndIf   
_UART_IRQ:   
      If PIR1bits_RCIF = 1 Then                      ' Was it a USART1 byte Receive that triggered the interrupt ?
        Movlw 6                                    ' Yes. So Mask out unwanted bits
        Andwf RCSTA,w                              ' Check for errors
        Bnz _Uart_Error                            ' Was either error status bit set?
        USART_FSR1_Save = USART_FSR1               ' Save FSR1L\H registers
        Inc IndexIn                                ' Move up the buffer index (0-255)
                                                   ' if the buffer is smaller, but still a power of 2, we can
                                                   'implement circularity simply as:
                                                   'IndexIn = IndexIn & ~USART_Buffersize
                                                   'or, if it's faster, mask the highest limit bit:
                                                   'Clear IndexIn.7     = for a 128 byte buffer
                                                   'Clear IndexIn.6     = for a 64 byte buffer 
        USART_FSR1 = VarPtr Ringbuffer             ' Point FSR1L\H to _USART_RingBuffer
        USART_FSR1 = USART_FSR1 + IndexIn          ' Add the buffer position to FSR1L\H
        INDF1 = RCREG                                    ' Place the received character into the buffer
        USART_FSR1 = USART_FSR1_Save                     ' Restore FSR1L\H registers
        'Toggle PORTB.5  'debug  - checked. This works.
        Retfie Fast                                      ' Exit from the interrupt, restoring the WREG, STATUS, and BSR registers
_Uart_Error:
        WREG = RCREG                                     ' empty the 2 byte UART buffer
        WREG = RCREG
        Clear RCSTAbits_CREN                             ' Clear receiver status
        Set RCSTAbits_CREN
      EndIf        
      Retfie Fast                   
  
Proc GetMidiIn (), Byte  
     If IndexIn <> IndexOut Then
       Inc IndexOut                              ' Increment IndexOut pointer (0 to 255)
       USART_FSR0_Save = USART_FSR0              ' Save FSR0L\H registers
       USART_FSR0 = VarPtr Ringbuffer            ' Point FSR0L\H to Ringbuffer
       USART_FSR0 = USART_FSR0 + IndexOut        ' Add the buffer position to FSR0L\H
       WREG = INDF0                              ' Read buffer location (IndexOut) into WREG
       Result = WREG                             ' return it in the function
       USART_FSR0 = USART_FSR0_Save              ' Restore FSR0\H registers
       Set STATUSbits_C                          ' Set CARRY flag to indicate a byte received 
       Ret                                       ' compulsary here!
    Else
        WREG = 255  
        Result = WREG                               
        Btfss STATUS,C                           ' Return with the CARRY flag clear to indicate timed out
        Ret                                      'compulsary here  
    EndIf
EndProc 


Proc Init_Usart_Interrupt ()
    Clear IndexIn                               ' Clear the buffer internal pointer
    Clear IndexOut                              ' Clear the buffer external pointer
    Set PIE1bits_RCIE                           ' Enable interrupt on USART1 receive
    #ifdef __Low_Interrupts_Enabled             ' Are we using low priority interrupts as well ?
    Set IPR1bits_RCIP                           ' Yes. So USART1 Receive Interrupt to High priority
    #endif
    Set INTCONbits_GIE                          ' enable general interrups
    Set INTCONbits_PEIE                         ' Enable global and peripheral interrupts
    'Set INTCONbits_T0IE             ' Enable Timer0 overflow interrupt, done on opentimer0()
EndProc

Proc Clear_Serial_Buffer ()
    PIE1bits_RCIE = 0                           ' Disable interrupt on USART1 receive
    Clear Ringbuffer                            ' Clear the serial buffer
    Clear IndexIn                               ' Clear the buffer internal pointer
    Clear IndexOut                              ' Clear the buffer external pointer
    Set PIE1bits_RCIE                           ' Re-Enable interrupt on USART1 receive
EndProc

'--------------------------------------------------------------------------------
' Initialise the USART1 interrupt
' Input     : None
' Output    : None
' Notes     : Enables interrupt on USART1 receive.
'           : Enables global and peripheral interrupts
'           : If Prioritised interrupts are used, the USART interrupt priority is made high

'$define InitUSARTInterrupt () Init_Usart_Interrupt    'added 31.08.2010 gwr
'Init_Usart_Interrupt Macro
'    GoSub _Init_Usart
'    Endm
'#ifdef Init_Usart_Interrupt#Req
'_Init_Usart:
'    Clear IndexIn                               ' Clear the buffer internal pointer
'    Clear IndexOut                              ' Clear the buffer external pointer
'    Set PIE1bits_RCIE                           ' Enable interrupt on USART1 receive
'    #ifdef __Low_Interrupts_Enabled             ' Are we using low priority interrupts as well ?
'    Set IPR1bits_RCIP                           ' Yes. So USART1 Receive Interrupt to High priority
'    #endif
'    Set INTCONbits_GIE                          ' enable general interrups
'    Set INTCONbits_PEIE                         ' Enable global and peripheral interrupts
'    'Set INTCONbits_T0IE             ' Enable Timer0 overflow interrupt, done on opentimer0()
'    Return
'#endif

'--------------------------------------------------------------------------------
' Clear the Serial Buffer
' Input     : None
' Output    : None
' Notes     : Also resets the index pointers to the serial buffer
'$define ClearSerialBuffer () Clear_Serial_Buffer       ' added 31.08.2010 - gwr
'Clear_Serial_Buffer Macro
'    GoSub _Clear_Usart_Buffer
'    Endm
'#ifdef Clear_Serial_Buffer#Req
'_Clear_Usart_Buffer:
'    PIE1bits_RCIE = 0                           ' Disable interrupt on USART1 receive
'    Clear Ringbuffer                            ' Clear the serial buffer
'    Clear IndexIn                               ' Clear the buffer internal pointer
'    Clear IndexOut                              ' Clear the buffer external pointer
'    Set PIE1bits_RCIE                           ' Re-Enable interrupt on USART1 receive
'    Return
'#endif

 '-------------------------------------------------------
' Low Priority Interrupt on Timer3 overflow
Low_Prior_Interrupt:
Timer3_ISR:
   ' for <Ekla> we use this timer for motor control
   ' for <Gesta> we can use it for lite automation.
    Context Save                    ' Save the contents of WREG, STATUS, and BSR
    Clear PIR2bits_TMR3IF           ' Clear the Timer3 interrupt flag.
    Clear Tim3 

    If CC69 > 0 Then
        If NrLowNotes = 0 Then
            Clear Lite120
        Else
            Set Lite120
        EndIf
        If NrHighNotes = 0 Then
            Clear Lite122
        Else
            Set Lite122
        EndIf   
    EndIf 
Context Restore                 ' Restore the contents of WREG, STATUS, and BSR, then exit interrupt
'-------------------------------------------------------

_Over_IRQ_Handler:


