;   ***************************************************************
;   * Copyright (C) 2005, Embed Inc (http://www.embedinc.com)     *
;   *                                                             *
;   * Permission to copy this file is granted as long as this     *
;   * copyright notice is included in its entirety at the         *
;   * beginning of the file, whether the file is copied in whole  *
;   * or in part and regardless of whether other information is   *
;   * added to the copy.                                          *
;   *                                                             *
;   * The contents of this file may be used in any way,           *
;   * commercial or otherwise.  This file is provided "as is",    *
;   * and Embed Inc makes no claims of suitability for a          *
;   * particular purpose nor assumes any liability resulting from *
;   * its use.                                                    *
;   ***************************************************************
;
;   ***************************************************************
;   *                    ------ WARNING ------                    *
;   * THIS TEMPLATE IS FOR THE PIC18 FAMILY.  USE QQQ_INTR.ASPIC  *
;   * AS A TEMPLATE FOR THE PIC16 FAMILY.                         *
;   ***************************************************************
;
;   Interrupt service and related routines.
;
/include "qq2.ins.aspic"

         extern_flags        ;declare global flag bits EXTERN

;*******************************************************************************
;
;   Configuration constants.
;
intr_priorities equ false    ;disable multiple interrupt priorities
;
;   Indicate which FSRs are to be saved by the single/high and low priority
;   interrupt routines.
;
;   FSR0 is used by the FIFO_xxx macros, and must be saved if FIFOs are accessed
;   from interrupt code.  Note that the UART interrupt routines use FIFOs.
;
;   FSR1 has no dedicated purpose in the general PIC development environment.
;
;   FSR2 is reserved as the software stack pointer.  This stack will be used to
;   save state during an interrupt.  FSR2 must therefore not be explicitly
;   saved.  It will automatically be restored if the same number of bytes are
;   popped from the stack as are pushed to the stack.
;
save_fsr0 equ    true        ;indicate whether save/restore FSR0 in sgl/high intr
save_fsr1 equ    false       ;indicate whether save/restore FSR1 in sgl/high intr

save_fsr0l equ   false       ;indicate whether save/restore FSR0 in low prio intr
save_fsr1l equ   false       ;indicate whether save/restore FSR1 in low prio intr

;**********
;
;   Derived constants.
;


;*******************************************************************************
;
;   Global state.
;
;   The following global state is in the normal register bank for global state.
;   The bank is GBANK, and GBANKADR is an address guaranteed to be within this
;   bank.
;
         defram  gbankadr
         iregs_define        ;define registers exclusively for interrupt routines

;*******************************************************************************
;
;   Local state.  This is always in the same register bank as the global
;   state.
;


.intr    code
;*******************************************************************************
;
;   Subroutine INTR_INIT
;
;   Initialize the interrupt system and other state managed by this module.
;
intr_init glbsub
;
;   Initialize global state.
;

;
;   Initialize local state.
;

;
;   Enable interrupts.  The interrupt system was reset at startup to all
;   interrupts disabled, single interrupt priority, and all interrupt priorities
;   set to the lowest.  Any interrupts that are needed have been individually
;   configured, but interrupts are still globally disabled.
;
  if intr_priorities         ;using multiple priority interrupts ?
         dbankif rcon
         bsf     rcon, ipen  ;configure for multiple interrupt priorities
         bsf     intcon, gieh ;enable high priority interrupts
         bsf     intcon, giel ;enable low priority interrupts
    else                     ;using a single interrupt priority
         bsf     intcon, peie ;enable the peripheral interrupts
         bsf     intcon, gie ;globally enable interrupts
    endif

         leaverest

;*******************************************************************************
;
;   High priority or single interrupt service routine.
;
;   The processor executes a call to location 8 on an interrupt, and in addition
;   globally disables interrupts.  These are re-enabled at the end of the ISR by
;   the RETFIE instruction.
;
;   Note that subroutine calls must be minimized or avoided in the ISR.  Since
;   an interrupt can come at any time in the main code, any additional call
;   stack locations used here are not available anywhere else.
;
;   The fast register stack is used to save/restore W, STATUS, and BSR for this
;   interrupt.
;
  if intr_priorities
.intr_high code  h'8'        ;high priority interrupt vector
    else
.intr_svc code   h'8'        ;single priority interrupt vector
    endif
         unbank              ;indicate the bank setting is unknown
;
;   W, STATUS, and BSR have been automatically saved onto the fast register
;   stack by the interrupt hardware.
;
  if save_fsr0               ;need to save FSR0 ?
         pushreg fsr0l
         pushreg fsr0h
    endif

  if save_fsr1               ;need to save FSR1 ?
         pushreg fsr1l
         pushreg fsr1h
    endif

;*******************************************************************************
;*******************************************************************************
;
;   Low priority interrupt service routine.
;
;   This section of code gets inserted if multiple priority interrupts are
;   enabled.  The high priority interrupt vector is at 8, and the low priority
;   vector at 18h.  We assume that the interrupt service routine requires more
;   than the 8 instructions between the two vectors, so the high priority
;   service routine must jump to a different location to avoid colliding with
;   the low priority interrupt vector.  In that case, the high priority
;   interrupt handler continues immediately after the low priority interrupt
;   handler code.
;
;   If multiple interrupt priorites are disabled, then there is nothing special
;   about location 18h and the interrupt handler can continue right over it
;   without harm.
;
  if intr_priorities         ;multiple interrupt priorities in use ?
         jump    intr_high_cont ;continue after low priority handler
;
;   Low priority interrupt service routine.  This routine can not use the fast
;   call stack and must save/restore W, STATUS, and BSR explicitly.
;
.intr_low code   h'18'       ;low priority interrupt vector
         unbank              ;indicate the bank setting is unknown
         movwf   preinc2     ;save W onto the software stack
         swapf   indf2       ;swap so that can be read with SWAPF on restore
         pushreg status      ;save STATUS onto the software stack
         pushreg bsr         ;save BSR onto the software stack

    if save_fsr0l            ;need to save FSR0 ?
         pushreg fsr0l
         pushreg fsr0h
      endif

    if save_fsr1l            ;need to save FSR1 ?
         pushreg fsr1l
         pushreg fsr1h
      endif
;
;   W, STATUS, BSR, and the general FSRs (if enabled) have been saved.  Now
;   determine the interrupt condition and service it.
;

         reset               ;unexpected interrupt, should never happen
;
;   Done servicing the low priority interrupt condition.  Now restore to the
;   state at the start of the interrupt and return from the interrupt.
;
intr_retl unbank             ;common low priority interrupt exit point

    if save_fsr1l            ;need to restore FSR1 ?
         popreg  fsr1h
         popreg  fsr1l
      endif

    if save_fsr0l            ;need to restore FSR0 ?
         popreg  fsr0h
         popreg  fsr0l
      endif

         popreg  bsr         ;pop BSR from software stack to restore it
         popreg  status      ;pop STATUS from software stack to restore it
         swapf   postdec2, w ;pop W from software stack to restore it
         retfie              ;return from the interrupt
;
;   Continue the high priority interrupt service routine here.
;
intr_high_cont unbank
         unbank
    endif                    ;end of multiple interrupt priorities in use case
;
;   End of code inserted only if multiple interrupt priorities are in use.
;
;*******************************************************************************
;*******************************************************************************
;
;   The high or single interrupt priority routine continues here.
;
;   W, STATUS, BSR, and the general FSRs (if enabled) have been saved.  Now
;   determine the interrupt condition and service it.
;

;
;   Check for UART receive interrupt.
;
         extern  uart_intr_recv ;receive interrupt routine in UART module
         dbankif pir1
         btfss   pir1, rcif
         jump    no_uart_recv
         gjump   uart_intr_recv ;handle interrupt, will go to INTR_RET_UART on done
no_uart_recv dbankis pir1
;
;   Check for UART transmitter ready interrupt.  The interrupt being enabled is
;   also checked.  This is because the UART interrupt routines enable/disable
;   the interrupt on the fly.
;
         extern  uart_intr_xmit ;receive interrupt routine in UART module
         dbankif pie1
         btfss   pie1, txie  ;UART transmit interrupt is enabled ?
         jump    no_uart_xmit ;no, skip this section
         dbankif pir1
         btfss   pir1, txif  ;UART transmit interrupt is signalled ?
         jump    no_uart_xmit ;no
         gjump   uart_intr_xmit ;handle interrupt, will go to INTR_RET_UART on done
no_uart_xmit dbankis pir1

         reset               ;unexpected interrupt, should never happen

;****************************************
;
;   Done servicing the high priority or single interrupt.  Now restore to the
;   state at the start of the interrupt and return from the interrupt.  W,
;   STATUS, and BSR have been saved on the hardware fast register stack and will
;   be restored as part of the RETFIE FAST instruction.
;
intr_ret unbank              ;common high/single priority interrupt exit point
         glbent  intr_ret_uart ;UART interrupt routines return here when done

  if save_fsr1               ;need to restore FSR1 ?
         popreg  fsr1h
         popreg  fsr1l
    endif

  if save_fsr0               ;need to restore FSR0 ?
         popreg  fsr0h
         popreg  fsr0l
    endif

         retfie  fast        ;return from interrupt, restore W, STATUS, and BSR

         end