'***************************************************
'           IRQ coding for midi-in                 *
'                    by                            *
'             Godfried-Willem Raes                 *
'                 Version 2.5                      *
'***************************************************
' Interrupt-driven serial buffer for USART MIDI receive using a hardware high priority interrupt.
' This subroutine replaces the compiler's Hserin/Hrsin and HrsOut commands library subroutines
' For use with 18F devices only
' 25.09.2010 - first redaction
' 12.10.2010 - included in Pedal firmware
' 20.04.2011 - included in Fa firmware, hub
' 30.04.2011 - now user low prior. irq for sampling the sensor
'              include renamed Midi_Hub_Irq.inc
'              Timers include no longer required.
' 23.05.2011 - 32 bit timer coding improved
' 20.08.2013 - used for <Whisper>
'              low interrupts will not happen here!
' 20.04.2015 - adapted to compiler upgrade.
'              some minor changes.
' 06.06.2015 - trying to get it to work properly again...
'              works fine on <Bello>
' 27.08.2015 - included in code for <Tinti>
' 09.11.2015 - included in code for <Zi>
' 23.01.2016 - included in code for <pp2>
' 26.04.2018 - included in code for <pos>
' 09.12.2019 - bug corrected in timer0 IRQ handling.
'              midi-in proc changed as in Tubo coding 

'#Disable HRSIn, HRSOut
                ' Disable the compiler's library subroutines for Hrsin
                ' statements starting with # are ASM compiler directives
                ' 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 MidiIn  As Byte System
    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

' Create the actual serial buffer in high memory
                                                      ' Warnings = OFF
                                                      ' Reminders = OFF
'    Reserve_RAM 256                                   ' Reserve some RAM at the top of memory as a serial buffer (MAX 255)
                                                      ' 40 could be enough
                ' remmed out 15.04.2015, compiler compaint...
   ' Symbol USART_BufferSize = __Reserve_Ram_Size      ' The amount of RAM reserved is the buffer size (MAX 255)
   ' Dim Ringbuffer[USART_BufferSize+1] As Byte At __Start_Of_Reserve_Ram    ' Array for holding received characters
                                                      '25.09.2010: by setting it to 256
                                                      'we get an automatic overflow to 0   Hence:
   ' Symbol USART_BufferSize = 255      ' The amount of RAM reserved is the buffer size (MAX 255)
'    Dim Ringbuffer[256] As Byte System ' At __Start_Of_Reserve_Ram    ' Array for holding received characters
                                        ' here we place the buffer at a fixed adres in memory, in casu 256 to 511
                                        ' this causes trouble with the main program were arrays are dynamically assigned.
                                        ' the dim dummy() statement in the main program solves the problem.
                                        ' __Start_Of_Reserve_Ram seems to be = Bank1_Start
                                                       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

'----------------------------------------------------------------
' USART ring buffer interrupt handler for MIDI and 32-bit timer0
' Input     : Interrupt is triggered on reception of a byte from the USART and on timer0 overflow
' Output    : Array Ringbuffer holds the bytes received
'           : Byte IndexIn points to the current location within the buffer
' Notes     : Does not indicate if an error occurs but does clear the error flags and carries on

High_Prior_Interrupt:
'    Context Save                                   ' added in after compiler upgrade 20.04.2015- not required for High IRQ
_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                             ' 09.12.2019 
        Btg PORTB.5 '=Debug_Led                                   ' asm bit toggle - debug led watchdog
        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 
        'If IndexIn >= USART_BufferSize Then        ' End of buffer reached ?
        '   Clear IndexIn                           ' Yes. So clear _USART_IndexIn
        'EndIf
        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                                ' Also place it into PP0
       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:
    Context Save                    ' Save the contents of WREG, STATUS, and BSR
    Clear PIR2bits_TMR3IF           ' Clear the Timer3 interrupt flag.
'    Tim3 = 46036                    ' gives 64 S/s rate, 19500 ticks, each 0.8uS, to FFFF
'     Tim3 = 65506                       ' gives 24us
'    TMR3L = 225
'    Tim3 = 225
'    Inc time                        ' dword counter
    Context Restore                 ' Restore the contents of WREG, STATUS, and BSR, then exit interrupt
'    Retfie                          ' required?
'Return
'-------------------------------------------------------
_Over_IRQ_Handler:
