'***************************************************
'           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>

                ' 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 

'--------------------------------------------------------------------------------
' 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
    Context Save                    ' Save the contents of WREG, STATUS, and BSR
    Clear PIR2bits_TMR3IF           ' Clear the Timer3 interrupt flag.
    Inc MotCnt                      ' in 10ms units
   ' Tim3 = Period10                ' = 53039 for 10 ms units and total period = 1 second    12497 ticks
   ' Tim3 = Period5                 ' = 59288 for 5 ms units and period = 500ms.              6248 ticks
   ' Tim3 = Period25                ' = 62412 for 2.5ms units and period = 250ms.             3124 ticks
    Tim3 = Period                   ' now a variable rather than a constant. 
    If MotCnt >= MotTim Then
        Clear MotCnt                ' reset timer
        If Mtog = 1 Then            ' was dus ingeschakeld
            MotTim = TOff[motspeed]     ' nu moet motor off
            Clear Motor_Relay
            Clear BlueLed
        Else
            MotTim = TOn[motspeed]
            Set Motor_Relay
            Set BlueLed
        EndIf
        Btg Mtog  ' =Toggle Mtog 
    EndIf
    
'       Tim3 = 53039   '208  = 50ms        ' on time
'                      '65328 = 33.7 us (2964.97 Hz) 
'                      '13066 = 40 ms 
'                      '52262 = ca 10 ms (47.0781 Hz)
'                      '53039 = 10 ms (50Hz)     = 12497 ticks    
Context Restore                 ' Restore the contents of WREG, STATUS, and BSR, then exit interrupt
'-------------------------------------------------------

_Over_IRQ_Handler:


