; ****************** Macro definitions ******************************** ;+++++ ; PAGE/BANK0/1/2/3 selects register bank 0/1/2/3. ; Leave set to BANK0 normally. BANK0 MACRO BCF STATUS,RP0 ; clear bank select bits BCF STATUS,RP1 BCF STATUS,IRP ; clear indirect adressing bit ENDM BANK1 MACRO BSF STATUS,RP0 ; BCF STATUS,RP1 ; BCF STATUS,IRP ; clear indirect adressing bit ENDM BANK2 MACRO BCF STATUS,RP0 ; BSF STATUS,RP1 BSF STATUS,IRP ; set bit for indirect adressing ENDM BANK3 MACRO BSF STATUS,RP0 ; BSF STATUS,RP1 BSF STATUS,IRP ; set bit for indirect adressing ENDM ; macros for accessing page's directly PAGE0 MACRO BCF PCLATH,3 BCF PCLATH,4 ENDM PAGE1 MACRO BSF PCLATH,3 BCF PCLATH,4 ENDM PAGE2 MACRO BCF PCLATH,3 BSF PCLATH,4 ENDM PAGE3 MACRO BSF PCLATH,3 BSF PCLATH,4 ENDM ;+++++ ; TABLE_JUMP Calculates an eventuntual table boundary crossing ; set's up the PCLATH register correctly ; Offset must be in w-reg, offset 0 jumps to the next instr. ; TABLE_JUMP MACRO MOVWF Table_Temp ; save wanted offset MOVLW LOW($+8) ; get low adress ( of first instr. after macro ) ADDWF Table_Temp,F ; add offset MOVLW HIGH($+6) ; get highest 5 bits ( of first instr. after macro ) BTFSC STATUS,C ; page crossed ? ( 256 byte ) ADDLW 0x01 ; Yes add one to high adress MOVWF PCLATH ; load high adress in latch MOVF Table_Temp,W ; get computed adress MOVWF PCL ; And jump ENDM ;+++++ ; SET_PCLATH 'help' macro for LONG_CALL ; Set's/clears PCLATH bits 3:4 according to ; 'variable' PCLATH_34 ; SET_PCLATH MACRO PCLATH_34 IF(PCLATH_34&0x10) BSF PCLATH,4 ELSE BCF PCLATH,4 ENDIF IF(PCLATH_34&0x08) BSF PCLATH,3 ELSE BCF PCLATH,3 ENDIF ENDM ;+++++ ; SET_PCLATH4 'help' macro for LONG/SHORT_CALL ; Set's/clears PCLATH bit 4 according to ; 'variable' PCLATH_4 ; SET_PCLATH4 MACRO PCLATH_4 IF(PCLATH_4&0x10) BSF PCLATH,4 ELSE BCF PCLATH,4 ENDIF ENDM ;+++++ ; SET_PCLATH3 'help' macro for LONG/SHORT_CALL ; Set's/clears PCLATH bit 3 according to ; 'variable' PCLATH_3 ; SET_PCLATH3 MACRO PCLATH_3 IF(PCLATH_3&0x08) BSF PCLATH,3 ELSE BCF PCLATH,3 ENDIF ENDM ;+++++ ; LONG_CALL long call, sets the page bits 4:5 of PCLATH ; so call can cross ANY page boundary, reset's PCLATH after call. ; w-reg is left untouched. LONG_CALL MACRO LABEL LOCAL DEST_HIGH, SOURCE_HIGH, DIFF_HIGH DEST_HIGH SET (HIGH(LABEL)&0x18) ; save bit's 4:5 of dest adress SOURCE_HIGH SET (HIGH($)&0x18) ; --- || --- source adress DIFF_HIGH SET DEST_HIGH ^ SOURCE_HIGH ; get difference ( XOR ) IF (DIFF_HIGH == 0) ; same page, SHOULD generate no extra code, delta 0 pages MESSG "Call on same page, replace LONG_CALL with PCALL " LABEL NOP ; redundant NOP's NOP CALL LABEL NOP NOP ELSE ; test if both bits must be set ? i.e. page0<->page3 or page2<->page3 IF (DIFF_HIGH == 0x18) ; difference in BOTH bit's, delta 2 pages ;MESSG "Setting page bit's for long page crossing call" SET_PCLATH DEST_HIGH ; set both bits in PCLATH CALL LABEL SET_PCLATH SOURCE_HIGH ; reset both bits in pclath ELSE ; if we end up here then one BSF/BCF is enough, i.e. delta 1 page ; i.e. page0<->1 or page2<->3 MESSG "Call only one page, replace LONG_CALL with SHORT_CALL " LABEL IF (DIFF_HIGH == 0x10) ; diff in high bit NOP ; redundant NOP SET_PCLATH4 DEST_HIGH ; set high(4) bit of PCLATH CALL LABEL SET_PCLATH4 SOURCE_HIGH NOP ; redundant NOP ELSE ; lowest bit only NOP ; redundant NOP SET_PCLATH3 DEST_HIGH ; set low(3) bit of PCLATH CALL LABEL SET_PCLATH3 SOURCE_HIGH NOP ENDIF ENDIF ENDIF ENDM ;+++++ ; SHORT_CALL short call, code for calling between page0<->1 or page2<->3 ; Reset's PCLATH after call. ; w-reg is left untouched. SHORT_CALL MACRO LABEL LOCAL DEST_HIGH, SOURCE_HIGH, DIFF_HIGH DEST_HIGH SET (HIGH(LABEL)&0x18) ; save bit's 4:5 of dest adress SOURCE_HIGH SET (HIGH($)&0x18) ; --- || --- source adress DIFF_HIGH SET DEST_HIGH ^ SOURCE_HIGH ; get difference ( XOR ) IF (DIFF_HIGH == 0) ; same page, SHOULD generate no extra code, delta 0 pages MESSG "Call on same page, replace SHORT_CALL with PCALL " LABEL NOP ; redundant NOP's CALL LABEL NOP ELSE ; for safety check so we do not require LONG_CALL IF ((DIFF_HIGH&0x18)==0x18) MESSG " WARNING ! Replace SHORT_CALL with LONG_CALL " LABEL ENDIF ;MESSG "Setting page bit's for short page crossing call" IF (DIFF_HIGH == 0x10) ; diff in high bit SET_PCLATH4 DEST_HIGH ; set high(4) bit of PCLATH CALL LABEL SET_PCLATH4 SOURCE_HIGH ELSE ; lowest bit only SET_PCLATH3 DEST_HIGH ; set low(3) bit of PCLATH CALL LABEL SET_PCLATH3 SOURCE_HIGH ENDIF ENDIF ENDM ;+++++ ; PCALL page call, code for calling on same page ; outputs messages if LONG/SHORT call could/must be used ; PCALL MACRO LABEL LOCAL DEST_HIGH, SOURCE_HIGH, DIFF_HIGH DEST_HIGH SET (HIGH(LABEL)&0x18) ; save bit's 4:5 of dest adress SOURCE_HIGH SET (HIGH($)&0x18) ; --- || --- source adress DIFF_HIGH SET DEST_HIGH ^ SOURCE_HIGH ; get difference ( XOR ) IF (DIFF_HIGH == 0) ; same page, call ok CALL LABEL ELSE ; for safety check so we do not require LONG_CALL IF ((DIFF_HIGH&0x18)==0x18) MESSG " WARNING ! Replace PCALL with LONG_CALL " LABEL CALL LABEL ; INCORRECT Call !!! ELSE MESSG " WARNING ! Replace PCALL with SHORT_CALL " LABEL CALL LABEL ENDIF ENDIF ENDM ;+++++ ; PUSH/PULL save and restore W,PCLATH,STATUS and FSR registers - ; used on interrupt entry/exit PUSH MACRO MOVWF Saved_W ; save w reg SWAPF STATUS,W ;The swapf instruction, unlike the movf, affects NO status bits, which is why it is used here. CLRF STATUS ; sets to BANK0 MOVWF Saved_Status ; save status reg MOVF PCLATH,W MOVWF Saved_Pclath ; save pclath CLRF PCLATH MOVF FSR,W MOVWF Saved_Fsr ; save fsr reg ENDM PULL MACRO MOVF Saved_Fsr,W ; get saved fsr reg MOVWF FSR ; restore MOVF Saved_Pclath,W ; get saved pclath MOVWF PCLATH ; restore SWAPF Saved_Status,W ; get saved status in w MOVWF STATUS ; restore status ( and bank ) SWAPF Saved_W,F ; reload into self to set status bits SWAPF Saved_W,W ; and restore ENDM ;+++++ ; DISABLE_IRQ disable global irq DISABLE_IRQ MACRO LOCAL STOP_INT STOP_INT BCF INTCON,GIE ; disable global interrupt BTFSC INTCON,GIE ; check if disabled GOTO STOP_INT ; nope, try again ENDM ;+++++ ; ENABLE_IRQ enable global irq ENABLE_IRQ MACRO BSF INTCON,GIE ; enable global interrupt ENDM