;********************************************************************** ;* Filename: interrupt.asm ;* Project: EPROM emulator ;* Date: 05 June 2005 ;* Version: 0.1 ;* Author: Philip Pemberton ;* ;* Interrupt service routine ;********************************************************************** #include "config.inc" ; Device configuration ;********************************************************************** ;** HEADERS ;********************************************************************** extern uart_RXIntr ; receive interrupt routine in UART module extern uart_TXIntr ; transmit interrupt routine in UART module ;********************************************************************** ;** CONSTANTS ;********************************************************************** save_FSR equ TRUE ;indicate whether ISR must save/restore FSR niregs equ 0 ;num of IREGn private interrupt routine registers lbank equ 0 ;register bank for the local state of this module lbankadr equ bankadr(lbank) ;address within local state register bank ;********************************************************************** ;** VARIABLES ;********************************************************************** ; Global state. defram gbankadr ; Local state. defram lbankadr ; The following state is private to the interrupt service routine, and ; must always be in bank 0. defram 0 STATUS_save res 1 ;saved copy of STATUS, nibbles swapped if save_FSR FSR_save res 1 ;saved copy of FSR (if FSR save enabled) endif if ncodepages > 1 PCLATH_save res 1 ;saved copy of PCLATH (if multiple code pages) endif ; Define the IREGn general register for use during an interrupt. The ; general registers REGn must not be altered during an interrupt without ; being explicitly saved/restored, which usually makes them unusable. ; The IREGn registers are used instead, although these are in bank 0 ; instead of global memory. The number of IREGn registers is defined ; by the constant NIREGS above. The first is IREG0, then IREG1, etc. ii set 0 while ii < niregs ;once for each IREG register to define ireg#v(ii) res 1 ;define this IREGn register global ireg#v(ii) ;make it global for interrupt code in other modules ii set ii + 1 ;advance to next IREG number endw ;back to define next IREG ; This state is private to the interrupt service routine and must always ; be accessible regardless of the current direct register bank setting. udata_shr w_save res 1 ;saved W during interrupt, mapped to all banks .intr code ;********************************************************************** ;** SUBROUTINES ;********************************************************************** ;---------------------------------- ; Subroutine: intr_Init ; Inputs: ; None ; Outputs: ; None ; Function: ; Initialize the interrupt system and other state managed by this module. glbsub intr_Init ; Enable interrupts. Any interrupts used have already been enabled ; individually, but interrupts are still disabled globally. ifdef PIR1 dbankif PIR1 clrf PIR1 ;clear any existing peripheral 1 intr condition endif ifdef PIR2 dbankif PIR2 clrf PIR2 ;clear any existing peripheral 2 intr condition endif ifdef PEIE bsf INTCON, PEIE ;enable peripheral interrupts endif bsf INTCON, GIE ;globally enable interrupts leave ;*********************************************************************** ; Interrupt service routine. ; ; The processor executes a call to location 4 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. .intr_svc code 4 ; Start at interrupt vector location movwf w_save ; Save W swapf STATUS, W ; Make copy of STATUS without effecting STATUS bits clrf STATUS ; Select direct and indirect register banks 0 dbankis 0 ibankis 0 movwf STATUS_save ; Save old STATUS value with nibbles swapped if save_FSR ; FSR needs to be saved ? movf FSR, W ; Save FSR movwf FSR_save endif if ncodepages > 1 ; Multiple code pages may be in use ? movf PCLATH, W ; Save PCLATH movwf PCLATH_save clrf PCLATH ; Now definitely on code page 0 endif ; W, STATUS, FSR (if SAVE_FSR set), and PCLATH (if multiple code pages) ; have been saved. Direct and indirect register banks 0 are selected, and ; the bank assumptions have been set accordingly. Program memory page 0 ; is selected. ; Check for UART receive interrupt. dbankif PIR1 btfss PIR1, RCIF ; Interrupt condition asserted ? goto no_uart_recv ; No gjump uart_RXIntr ; Handle interrupt, will go to INTR_RET_UART when done no_uart_recv: dbankis PIR1 ; Check for UART transmitter ready interrupt. dbankif PIE1 btfss PIE1, TXIE ; Is this interrupt enabled ? goto no_uart_xmit ; No dbankif PIR1 btfss PIR1, TXIF ; Interrupt condition asserted ? goto no_uart_xmit ; No gjump uart_TXIntr ; Handle interrupt, will go to INTR_RET_UART when done no_uart_xmit: unbank gjump 0 ; Unexpected interrupt, should never happen ; Restore state to when the interrupt occurred and return from interrupt. intr_ret: unbank ; Common interrupt exit point glbent intr_ret_uart ; UART interrupt routines return here when done clrf STATUS ; Register bank settings are now 0 dbankis 0 ibankis 0 if ncodepages > 1 ; Multiple code pages may be in use ? movf PCLATH_save, W ; Restore PCLATH movwf PCLATH endif if save_FSR ; FSR needs to be restored ? movf FSR_save, W ; Restore FSR movwf FSR endif swapf STATUS_save, W ; Get old STATUS with nibble order restored movwf STATUS ; Restore STATUS, register banks now unknown swapf w_save, F ; Swap nibbles in saved copy of W swapf w_save, W ; Restore original W retfie ; Return from interrupt, re-enable interrupts END