list b=4 ;************************************************************************ ; Following the example of Bob Blick's original propeler clock I made * ; my own version of this fancy clock and have re-written the code from * ; scratch. I used a bit more powerfull version of PIC cpu and added * ; these features: * ; - the clock keeps time AND DATE and Day of week * ; - can display analog hands or digital time and mixed mode where * ; time is displayed as analog hands and digital date * ; - setting the time/date or switch mode is done by RC5 remote control * ; - possibility to display a message programmed in ROM or EEPROM * ; * ;************************************************************************ ; * ; Filename: prop.asm * ; StartDate: 02/12/2001 * ; LastUpdate: 09/10/2018 * ; File Version: 2.30 * ; * ; Author: Soubry Henk * ; Company: Soubry Software Service * ; * ; Ported to 16F648A 16F88 * ; Displays day of week, century * ; Name of weekdays on 16 languages * ; Selectable date formats ((CC)YY-MM-DD / DD-MM-(CC)YY) * ; Rotation speed measurement * ; Two scrolling message with adjustable speed * ; Static text display * ; 12/24 hour digital/analog display mode * ; All digital display can be done in Binary Coded Decimal * ; Individualy controllable analog hands * ; Dot/arc second hand, bar/dot minute hand option * ; Doubble/single tick at 12'o clock * ; Store time and settings in a PCF8583 I2C RTC * ; Calibration pin can be used to select rotation direction * ; Vertical LED arrangement option, bottomn of display not mirrored * ; Vertical LED display rotates at Scrollspeed * ; * ;************************************************************************ ; * ; Files required: CharGen648.asm * ; Keys648.asm * ; DayNames648.asm * ; * ;************************************************************************ ; * ; Notes: * ; Pin assignment * ; Notes: * ; Pin assignment * ; Port A * ; 0 = inner display led (because RB0 is index) * ; 1 = analog outer led * ; 2 = I2C SCL use 2k pull up resistor to PIC's Vdd * ; 3 = OSC Calibration output @ 2.5kHz * ; 4 = I2C SDA use 2k pull up resistor to PIC's Vdd * ; 5 = Ir receiver Input only pin * ; 6..7 = Quartz oscillator * ; RA6 can be freed up using ext oscillator * ; Port B * ; 0 = index * ; 1..6 = Display leds (1=inner, 6=outer) * ; 7 = analog inner leds * ; * ;************************************************************************ ; * ; RTC Ram layout * ; 00 01 02 03 04 05 06 07 * ; cmd 1/10 sec minute hour day month Timer * ; 1/100 year1.0 wday 10Day/Day * ; sec not used * ; * ; 08 09 0A 0B 0C 0D 0E 0F * ; A L A R M R E G I S T E R S * ; not used * ; * ; 10 11 12 13 14 15 16 17 * ; Year ~Year Century Flags Disp Scroll Flags3 Flags4 * ; offs speed * ; * ; 18 19 1A 1B 1C 1D 1E 1F * ; Lang. Flags5 * ; * ;************************************************************************ ; Ir Remote control keys * ; * ; See keys.asm and keys.txt * ; * ; Afther time setting Toggle Outer Led command stores time to RTC * ;************************************************************************ errorlevel -302 ifdef __16F648A list p=16f648A ; 16F648A can be used #include ; processor specific variable definitions EEPROM_SIZE EQU 256 endif ifdef __16F88 list p=16f88 ; 16F88 can be used #include ; processor specific variable definitions #define T0IE TMR0IE #define T0IF TMR0IF #define _MCLRE_OFF _MCLR_OFF EEPROM_SIZE EQU 256 endif #include "CharGen648.asm" #include "Keys648.asm" #include "DayNames648.asm" ifdef __16F88 __CONFIG _CONFIG1, _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _MCLRE_OFF & _LVP_OFF __CONFIG _CONFIG2, _IESO_OFF & _FCMEN_OFF else __CONFIG _CP_OFF & _WDT_OFF & _BODEN_ON & _PWRTE_ON & _HS_OSC & _MCLRE_OFF & _LVP_OFF endif #define VersionMajor 0x02 #define VersionMinor 0x30 __idlocs 0xA000 | (VersionMajor << 8) | (VersionMinor) RTC_ADDR EQU 0xA0 ; Address of RTC ;***** VARIABLE DEFINITIONS #define VerticalLEDs ; un-comment this line if leds are arranged vertical to rotation ;;;;#define RA0OnTop #define RC5IntOnRA5 ; un-comment this line if RC5 input is on RA2 #define MotorCounterClockWise ; un-comment this line if motor is running counter clockwise ;#define MotorDirFromCalibration ; un-comment this line if get motor direction from calibration pin ;#define ReversTextScroll ; un-comment this line if your text must scroll from left to right (e.g. Hebrew) ;#define SlashedZero ; comment this line to use normal zero on time/date display #define RC5AddrCheck ; comment this line if RC5 Address checking not required #define UsePCF8583RTC ; comment this line if No PCF8583 RTC used #define DISPLAYOFFSET 0x37 ; Initial Display Offset for the hardware (display memory address) !!! ;#define RTC_Control 0x00 ; Control register #define RTC_Second 0x02 ; Address of Second ;#define RTC_Minute 0x03 ; Address of Minute ;#define RTC_Hour 0x04 ; Address of Hour / AM-PM flag and mode ;#define RTC_Day 0x05 ; Address of Day / 2 lsb of Year ;#define RTC_Month 0x06 ; Address of Week day / Month #define RTC_Year 0x10 ; Address of Year #define RTC_YearNeg 0x11 ; Address of negated value of Year #define RTC_century 0x12 ; Address of century #define RTC_flags 0x13 ; Address of flags #define RTC_disp_off 0x14 ; Address of DispOffset #define RTC_ScrollSp 0x15 ; Address of Scroll speed #define RTC_flags3 0x16 ; Address of flags3 #define RTC_flags4 0x17 ; Address of flags4 #define RTC_Language 0x18 ; Address of Language #define RTC_flags5 0x19 ; Address of flags5 ;Display memory Low byte ;DispI 0x020 ; 2*40 bytes from 0x20 -> 0x48 ;DispII 0x0A0 ; 2*80 bytes from 0xA0 -> 0xF0 ; Common memory 0x070-0x07F, 0x0F0-0x0FF, 0x170-0x17F, 0x1F0-0x1FF ;Vars in shared memory. This memory is available from any bank cblock 0x70 Scratch ; memory locations for general use Scratch2 ; Scratch3 ; Scratch4 ; ;Vars for building display-content BCD ; Binary coded decimal BCD_High ; high part digitindex ; index digit to display dotindex ; index dot to display dpi ; 0..119 display index for main program tdd ; temporary display data I2CWordAddr ; flags ; Digital display flags - more bits in flags5:5..4 flags2 ; System flags flags3 ; Analogue display flags - more bits in CCP1CON:5..4 iFSR ; copy of FSR used by interrupt routine w_temp ; variable used for context saving endc IF w_temp >= 0x80 ERROR "To many variables used in Common Bank" ENDIF ;Vars for display timing routines and interrupts cblock 0x20 DispI: .40 ; Display data 0..39 PeriodCnt_H ; 16 bit counter counts number of interrupt PeriodCnt_L ; between index-pulses (one revolution) ;!! Keep variable order - indirect access used to them v StorePCnt1_H ; first 16 bit storage for PeriodeCounter StorePCnt1_L ; StorePCnt2_H ; second 16 bit storage for PeriodeCounter StorePCnt2_L ; ;!! Keep variable order - indirect access used to them ^ SubSec_H ; 16 bit sub-second counter SubSec_L ; there are 2500 sub-seconds in 1/2 second PixelWidth ; Pixel-width = number of interrupts in one ON pixel PixelOff ; 8 bit down-counter to time PixelWidth PixelPitch_H ; PixelPitch = 16 bit number of interrupts between two pixel PixelPitch_L ; PixelPitch_F ; Fraction of PixelPitch needed for accuracy and to avoid jitter NewPPitch_H ; New pixel pitch calculated by main programm, will be NewPPitch_L ; copied to PixelPithc by interrupt routine NewPPitch_F ; NextPixel_H ; Next pixel @ PeriodCnt = NextPixel NextPixel_L ; NextPixel_F ; RoundedNext_L ; RoundedNext = Round(NextPixel) RoundedNext_H ; DispOffset ; Display offset compared to indexpuls TmrScroll ; Count Down timer for scroll delay ;Vars for time-keeping ;!! Keep variable order - Timecheck use indirect access to them v Second2 ; 0..119 counting half seconds Minute ; 0.. 59 Hour ; 0.. 23 Day ; 1.. Dmon-1 Month ; 1.. 12 Year ; 0.. 99 Century ; 0.. 99 ;!! Keep variable order - Timecheck use indirect access to them ^ DMon ; days in current Month + 1 WDay ; Day of week 0..6 - displayed as 1..7 : Monday is 1 Language flags5 ;Vars for RC5 decoding RC5_flags ; RC5_Tmr ; RC5_BitCnt ; RC5_Addr ; RC5_Cmd ; RC5_Cmd2 ; storage for previous cmd endc IF RC5_Cmd2 >= 0x70 ERROR "To many variables used in Bank 0" ENDIF ; start a new block of vars in bank1 cblock 0xA0 DispII: .80 ; Display data 40..119 endc ; start a new block of vars in bank2 cblock 0x120 ; Bank 2 - 16F648A 80 bytes ch_dot_point_H ; pointer the the current dot# in a character ch_dot_point_L ; points the the chargenerator table ch_dot_index ; index of dot# in a char, 0..5 ch_flags ; char flags ch_blanking ; counter for blanking the display when scrolling text rotation_L ; Rotation count rotation_H rot_bcd:5 ; Rotation in BCD 1/min pclath_temp ; variable used for context saving fsr_temp ; variable used for context saving status_temp ; variable used for context saving endc IF status_temp >= 0x160 ERROR "To many variables used in Bank 2" ENDIF ;********************************************************************** ; give meaningfull names to the scratch locations ; for the 16bit/8bit divide routine DIV_HI EQU Scratch ; 16 bit Divident DIV_LO EQU Scratch2 ; needed for 16 bit / 8 bit division DIV_Q EQU Scratch3 ; Quotient DIVISOR EQU Scratch4 ; Divisor ;********************************************************************** ; define Port bits ; Port A bPixel0 EQU 0 bOuterLED EQU 1 bCalibration EQU 3 ifdef UsePCF8583RTC bSCL EQU 2 bSDA EQU 4 endif ifdef RC5IntOnRA5 bRC5inp EQU 5 else bRC5inp EQU 2 endif ifdef UsePCF8583RTC if ((bOuterLED>5) || (bCalibration>5) || (bSCL>5) || (bSDA>5) || (bRC5inp>5)) error "RA6, RA7 pins are used for quartz oscillator" endif else if ((bOuterLED>5) || (bCalibration>5) || (bRC5inp>5)) error "RA6, RA7 pins are used for quartz oscillator" endif endif ifdef UsePCF8583RTC if ((bOuterLED==5) || (bCalibration==5) || (bSCL==5) || (bSDA==5)) error "RA5 in an input only pin" endif else if ((bOuterLED==5) || (bCalibration==5)) error "RA5 in an input only pin" endif endif if ((bOuterLED==4) || (bCalibration==4)) messg "RA4 in an open drain output pin - pull-up resistor required" endif ifdef UsePCF8583RTC if ((bOuterLED==bSCL) || (bCalibration==bSCL) || (bPixel0==bSCL) || (bSDA==bSCL) || (bRC5inp==bSCL) || (bSCL>4)) error "PORTA configuration error : bSCL" endif if ((bOuterLED==bSDA) || (bCalibration==bSDA) || (bPixel0==bSDA) || (bSDA==bSCL) || (bRC5inp==bSDA) || (bSDA>4)) error "PORTA configuration error : bSDA" endif endif ;PortB bIndexPuls EQU 0 ; Index pulse interrupt request bMinLED EQU 4 ; bSecLED EQU 5 ; Second hand's LED bTickLED EQU 6 ; Analogue clock's Ticks LED bInnerLED EQU 7 ; Analogue hand's inner 9 LEDs if (bIndexPuls!=0) error "Index puls has to be the RB0/External interrupt pin" endif if ( (bIndexPuls==bSecLED) || (bTickLED==bSecLED) || (bInnerLED==bSecLED) ) error "PORTB configuration error : bSecLED" endif if ( (bIndexPuls==bTickLED) || (bSecLED==bTickLED) || (bInnerLED==bTickLED) ) error "PORTB configuration error : bTickLED" endif if ( (bIndexPuls==bInnerLED) || (bSecLED==bInnerLED) || (bTickLED==bInnerLED) ) error "PORTB configuration error : bInnerLED" endif ; TMR1L, TMR1H used to count rotation speed ; T1CON bMotorDirCC EQU 1 ; Direction of motor rotation - Read at reset from PORTC,bCalibration bRC5_TimeSet EQU 2 ; Time setting commands were received bNewRot EQU 4 ; New rotation count avaible bPStorage EQU 5 ; Rotation time measurement buffer select ; CCP1CON ; More analog display flags bSTick12 EQU 4 ; Single / double tick at 12'o clock ; EQU 5 ; flags5 ; More digital display flags bBinMode EQU 4 ; Binary display mode ; EQU 5 ; CCPR1L ; Stores a copy of PORTC - needed only for sw I2C ; CCPR1H ; Stores Scrolling speed ; define flag bits ; Digital Display flags bShowOuter EQU 0 ; Show / hide outer ring bShowDayName EQU 1 ; Show day's name instead day of week, temperature, humidity bDisp12Mode EQU 2 ; 12 / 24 hour mode bDateFormat EQU 3 ; Use YYYY-MM-DD / DD-MM-YYYY or YY-MM-DD / DD-MM-YY date format bShowWDay EQU 4 ; Show / hide day of week, temperature, humidity - Show always date bShowDTime EQU 5 ; Show / hide digital time bShowDDate EQU 6 ; Show / hide digital date bShowCentury EQU 7 ; Show / hide century ; define flag3 bits ; Analog Display flags ;bShow36912 EQU 0 ; Show / hide drawing III, VI, IX, XII around the clock bShowTicks EQU 1 ; Show / hide ticks bSecMode EQU 2 ; Dot / Arc second hand bMinMode EQU 3 ; Dot / Bar minute hand bShowSecHand EQU 4 ; Show / hide second hand bShowMinHand EQU 5 ; Show / hide minute hand bShowHourHand EQU 6 ; Show / hide hour hand bAnalMode24h EQU 7 ; 24 / 12 hour mode ; define flag2 bits ; System flags bDspEnabled EQU 0 ; Blank the display - motor slow or not stabilized bNewPCnt EQU 1 ; New pitch calculated bNewTime EQU 2 ; New time calculated bNewPPitch EQU 3 ; New pitch measured bText EQU 4 ; Text on display bScrollOn EQU 5 ; Scrolling text on display bDemo EQU 6 ; Demo mode active bTimeInv EQU 7 ; Time invalid ; RC5 flags ; Cleared in interrupt routine at RC5_error bRC5_WaitStart EQU 0 bRC5_DataReady EQU 1 ; EQU 2 ; Reserved for backward compatibility bRC5_Idle EQU 3 bRC5_ReSynced EQU 4 bRC5_prev_inp EQU bRC5inp ; Has to be on same bit as bRC5inp on PortA bRC5_HalfBit EQU 6 ; EQU 7 ; Can be used, but interrupt routine clears RC5_flags if error in communication if ((bRC5_WaitStart==bRC5_prev_inp) || (bRC5_DataReady==bRC5_prev_inp) || (bRC5_HalfBit==bRC5_prev_inp) || (bRC5_Idle==bRC5_prev_inp) || (bRC5_ReSynced==bRC5_prev_inp)) error "RC5_flags configuration error" endif ifdef UsePCF8583RTC if ((bPixel0==bRC5_prev_inp) || (bOuterLED==bRC5_prev_inp) || (bSCL==bRC5_prev_inp) || (bCalibration==bRC5_prev_inp) || (bSDA==bRC5_prev_inp) || (bRC5inp!=bRC5_prev_inp)) error "PORTA configuration error" endif else if ((bPixel0==bRC5_prev_inp) || (bOuterLED==bRC5_prev_inp) || (bCalibration==bRC5_prev_inp) || (bRC5inp!=bRC5_prev_inp)) error "PORTA configuration error" endif endif if (bRC5inp!=bRC5_prev_inp) error "bRC5inp and bRC5_prev_inp have to be on same bit position" endif ;********************************************************************** ; PortA - a copy of PORTA is in CCPR1L ; -!-!- Don't use bsc, bsf, operation with PORTA,f (like xorwf PORTA,f). Only clrf PORTA and movwf PORTA will work -!-!- ; -!-!- Make modification on CCPR1L and use only XorToPorta, ToPorta rutines -!-!- ; -!-!- Disable interrupt (bcf INTCON,GIE) before these routines, enable (bsf INTCON,GIE) afther -!-!- ;#define Pixel0 PORTA,BPixel0 #define OuterLED PORTA,bOuterLED #define SCL PORTA,bSCL #define Calibration PORTA,bCalibration #define SDA PORTA,bSDA #define RC5inp PORTA,bRC5inp ; PortB #define IndexPuls PORTB,bIndexPuls ; CCPR1L used for copy of PORTA ; PORTA read-modify-write instructions set SCL and SDA to 1 -- external pull up resistor ; PORTA read-modify-writes disturb I2C routines - CCPR1L is used to store value written to PORTA ; T1CON #define MotorDirCC T1CON,bMotorDirCC #define NewRot T1CON,bNewRot #define PStorage T1CON,bPStorage #define RC5_TimeSet T1CON,bRC5_TimeSet ; TMR1 registers used for rotation metering ; CCP1CON flags4 #define STick12 CCP1CON,bSTick12 ; flags5 #define BinMode flags5,bBinMode ; CCPR1H register used to store scroll speed ; Flags #define ShowOuter flags,bShowOuter #define ShowDayName flags,bShowDayName #define Digit12Mode flags,bDisp12Mode #define DateFormat flags,bDateFormat #define ShowWDay flags,bShowWDay #define ShowDTime flags,bShowDTime #define ShowDDate flags,bShowDDate #define ShowCentury flags,bShowCentury ; Flags3 #define Show36912 flags3,bShow36912 #define ShowTicks flags3,bShowTicks #define SecMode flags3,bSecMode #define MinMode flags3,bMinMode #define ShowSecHand flags3,bShowSecHand #define ShowMinHand flags3,bShowMinHand #define ShowHourHand flags3,bShowHourHand #define AnalMode24h flags3,bAnalMode24h ; Flags2 #define fDspEnabled flags2,bDspEnabled #define NewPCnt flags2,bNewPCnt #define NewTime flags2,bNewTime #define NewPPitch flags2,bNewPPitch #define fText flags2,bText #define fScrollOn flags2,bScrollOn #define fDemo flags2,bDemo #define fTimeInv flags2,bTimeInv ; RC5 Flags #define RC5_WaitStart RC5_flags,bRC5_WaitStart #define RC5_DataReady RC5_flags,bRC5_DataReady #define RC5_HalfBit RC5_flags,bRC5_HalfBit #define RC5_Idle RC5_flags,bRC5_Idle #define RC5_prev_inp RC5_flags,bRC5_prev_inp #define RC5_ReSynced RC5_flags,bRC5_ReSynced ;****************************************************************************** ; Define macro's for a Select - Case ;****************************************************************************** select_w macro sel_last set 0 ;setup variable only endm case macro val xorlw val ^ sel_last btfsc STATUS,Z sel_last set val endm casenomatch macro val xorlw val ^ sel_last btfss STATUS,Z sel_last set val endm ; ;****************************************************************************** ; Define EEPROM content for initial message ;****************************************************************************** ORG 0x2170 ; Start of EEPROM message ; 0123456789ABCDE F de "Propeller Clock", 0x00 ORG 0x2100 ; Start of EEPROM page 0 de "===> This version of Bob Blick's Propeller Clock was built with " de "16F684A, PCF8583 RTC ",0x83,0x84 de " Ver. ",VersionMajor | 0x30 ,".", ((VersionMinor>>4) & 0x0F) | 0x30, ((VersionMinor) & 0x0F) | 0x30, " <===" de 0x00 ORG 0x2180 ; Start of EEPROM page 1 de "===> Displays day of week, rotation speed and store times in PCF8583 RTC ",0x83,0x84," <===" de " ",0x83,0x84 de 0x00 ;********************************************************************** ORG 0x000 ; processor reset vector clrf PORTA clrf PORTB clrf CCPR1L goto main ; go to beginning of program ;********************************************************************** ORG 0x004 ; interrupt vector location movwf w_temp ; context saveing swapf w_temp, f swapf STATUS, w ; Save STATUS bcf STATUS,RP0 bsf STATUS,RP1 ; Context saving variables are on Bank2 movwf status_temp swapf PCLATH,w ; Save PCLATH movwf pclath_temp ; Needed only if >2K program memory used movf FSR,w ; context saving movwf fsr_temp ; clrf PCLATH ; IT routine is in 0x000..0x7FF clrf STATUS ; select bank 0 & irp 0 for interrupt stuff ;********************************************************************** INT_RB0 btfss INTCON,INTF ; interrupt on RB0? goto INT_TMR0 ; nope, TMR0 is next ;---------- RB0 interrupt ! == Index Sensor movlw 0xff ; movwf TMR0 ; force TMR0 to 0xFF movlw StorePCnt1_H btfss PStorage movlw StorePCnt2_H movwf FSR movf PeriodCnt_H,w ; movwf INDF incf FSR,f movf PeriodCnt_L,w ; movwf INDF movlw 1< Test to double TMR0 speed incfsz PeriodCnt_L,f ; Increment 16-bit period counter goto lDisp_1 ; incfsz PeriodCnt_H,f ; goto lDisp_1 ; if overflow in MSB period counter = speed to low bcf flags2,bDspEnabled bcf CCPR1L,bOuterLED; Other bit of PortA, PortB bits and PortC 0 will be cleared by lDisp_1 call ToPortA lDisp_1 movf RoundedNext_L,w ; PeriodCnt = RoundedNextPixel ? xorwf PeriodCnt_L,w ; btfss STATUS,Z ; goto lPixelOn ; no, check if there is a pixel on movf RoundedNext_H,w ; xorwf PeriodCnt_H,w ; btfss STATUS,Z ; goto lPixelOn ; no, check if there is a pixel on ; yes, display next pixeldata movf iFSR,w ; load new memory pointer in w movwf FSR ; load File Select register with Display Index movlw 0x00 ; Blank the display if rotation too slow btfsc flags2,bDspEnabled movf INDF,w ; Get display data movwf PORTB ; PortB = DisplayData(index) xorwf CCPR1L,w andlw (1 << bPixel0) call XorToPortA ; dispatch bit 0 to PORTA ifdef MotorDirFromCalibration btfsc MotorDirCC ; Get direction of motor rotation call CheckDecrementFSR ; decrement FSR, check correct progress of diplay memory pointer btfss MotorDirCC ; Get direction of motor rotation call CheckIncrementFSR ; increment FSR, check correct progress of diplay memory pointer else ifdef MotorCounterClockWise call CheckDecrementFSR ; decrement FSR, check correct progress of diplay memory pointer else call CheckIncrementFSR ; increment FSR, check correct progress of diplay memory pointer endif endif movwf iFSR ; Store it in i(nterrupt)FSR movf PixelPitch_F,w ; NextPixel = NextPixel + PixelPitch addwf NextPixel_F,f ; btfss STATUS,C ; goto no_overflow ; incf NextPixel_L,f ; fraction overflow, add 1 to Low byte btfsc STATUS,Z ; incf NextPixel_H,f ; low byte roll-over, add 1 to High byte no_overflow: movf PixelPitch_L,w ; addwf NextPixel_L,f ; btfsc STATUS,C ; incf NextPixel_H,f ; low byte overflow, add 1 to High byte movf NextPixel_L,w ; RoundedNext = NextPixel movwf RoundedNext_L ; movf NextPixel_H,w ; movwf RoundedNext_H ; btfss NextPixel_F,7 ; IF NextPixel_Fraction >= 128 then goto NotRoundUp ; incf RoundedNext_L,f ; ROUND UP btfsc STATUS,Z ; incf RoundedNext_H,f ; NotRoundUp: movf PixelWidth,w ; movwf PixelOff ; PixelOff = PixelWidth goto lDispDone ; lPixelOn movf PixelOff,w ; Is there a pixel on? btfsc STATUS,Z ; PixelOff = 0 ? goto lDispDone ; no, jump decfsz PixelOff,f ; pixel is on, countdown goto lDispDone ; clrf PORTB ; turn off display LED's bcf CCPR1L,bPixel0 ; also turn off PortC LED call ToPortA lDispDone bcf INTCON,T0IF ; clear TMR0 interrupt flag before return ;-------- INT_TMR2 btfss PIR1,TMR2IF ; interrupt on TMR2? goto INT_EXIT ; nope, interrupt is done! ; do the TMR2 stuff, we get here every 200uSec movlw 1<>7) | (1<2K program memory used movwf PCLATH swapf status_temp, w ; restore context for main program movwf STATUS ; this will also restore bank selection swapf w_temp, w retfie ; return from interrupt ;****************************************************************************** ; Init sub second counter -- SubSecond = 0x10000 - 2500 = 0xF63C InitSubSecCnt movlw 0x3C ; load counter SubSecond = 0x10000 - 2500 = 0xF63C movwf SubSec_L ; movlw 0xF6 ; movwf SubSec_H ; return ;********************************************************************** div16X8Divisor ; Store divisor movwf DIVISOR div16X8 ; DIV_HI and DIV_LO / DIVISOR. result to DIV_Q ; remainder in DIV_LO ; does not deal with divide by 0 case clrf DIV_Q div_1 movf DIVISOR, W subwf DIV_LO, F btfsc STATUS, C ; if negative skip goto div_2 div_borrow movlw .1 subwf DIV_HI, F ; DIV_HI = DIV_HI - 1 btfss STATUS, C ; if no borrow occurred goto div_done div_2 incf DIV_Q, F goto div_1 div_done movf DIVISOR, W ; re-add DIVISOR to DIV_LO to get addwf DIV_LO, W ; remainder in DIV_LO movwf DIV_HI ; shift remainder to DIV_HI movf DIV_Q,w ; Return with quotient in w return ;********************************************************************** ; Calculate pixel pitch CalcPixelPitch ; that is a hard one. We have to divide by 120 !!! ; PixelPitch = PeriodCnt / 120 movf StorePCnt1_H,w ; check if two period counters movwf DIV_LO ; xorwf StorePCnt2_H,w ; are the same. If not, don't btfss STATUS,Z ; calculate new pixelpitch because goto CalcDone ; the rotation speed was not stable movf StorePCnt1_L,w ; xorwf StorePCnt2_L,w ; btfss STATUS,Z ; goto CalcDone ; clrf DIV_HI ; Divider = 00:StorePCnt1_H movlw .120 ; Init divisor call div16X8Divisor movwf NewPPitch_H ; movf StorePCnt1_L,w ; movwf DIV_LO ; call div16X8 ; Divider = Remainder:StorePCnt1_L movwf NewPPitch_L ; clrf DIV_LO ; call div16X8 ; Divider = Remainder:00 movwf NewPPitch_F ; ; start calculation for pixel width clrc ; Clear Carry rrf NewPPitch_L,w ; movwf PixelWidth ; W = PixelWidth = NewPPitch_L * 4 / 8 clrc rrf PixelWidth,f ; PixelWidth = NewPPitch_L * 2 / 8 clrc rrf PixelWidth,f ; PixelWidth = NewPPitch_L * 1 / 8 addwf PixelWidth,f ; PixelWidth = NewPPitch_L * 5 / 8 bsf flags2,bDspEnabled ; If bDspEnabled int. routine clears PORTA,bOuterLED call SetOuterLed ; set Port for Outerled equal to flags OuterLED bit NotON: bsf NewPPitch ; signal new Pitch value to interrupt routine CalcDone: bcf NewPCnt ; clear flag return ;****************************************************************************** ; Check correct progress of sec, min, hour, day, day of week, month, year ;****************************************************************************** TimeCheck movlw Second2 movwf FSR ; FSR -> Second2 movlw .120 ; Check for second overflow call CheckAndIncNext ; FSR -> Minute movlw .60 ; Check for minute overflow call CheckAndIncNext ; FSR -> Hour movlw .24 ; Check for hour overflow call CheckAndIncNext ; FSR -> Day btfsc STATUS,C ; if Day incremented increment WDay as well incf WDay,f movlw .7 ; if Day of week == 7 subwf WDay,w btfsc STATUS,C ; if Day of week < 7 then c=0 (borrow) movwf WDay ; Day of week = 0 movlw .32 ; DMon = 32 movwf DMon movf Month,w ; movwf Scratch ; For months <8 odd months have 31 days btfsc Month,3 incf Scratch,f ; For months >7 even months have 31 days btfss Scratch,0 decf DMon,f xorlw .2 ; Month = February ? btfss STATUS,Z ; goto lNotFeb ; continue decf DMon,f ; February has max 29 days movf Year,w btfsc STATUS,Z ; Year == 0 check Century instead movf Century,w andlw 0x03 btfss STATUS,Z decf DMon,f ; Year is not a LeapYear, dec DMon for Feb lNotFeb movf DMon,w call CheckAndIncNext ; Check for day overflow btfsc STATUS,C ; if day cleared, correct day incf Day,f ; FSR -> Month movlw .13 ; Check for month overflow call CheckAndIncNext btfsc STATUS,C ; if month cleared, correct month incf Month,f ; FSR -> Year movlw .100 ; call CheckAndIncNext ; FSR -> Century movlw .100 ; subwf Century,w ; Century < 100 ? btfsc STATUS,C ; clrf Century ; bcf NewTime ; Clear new time flag return CheckAndIncNext subwf INDF,w ; test INDF value to w btfsc STATUS,C ; if INDF < w then c=0 (borrow) movwf INDF ; Reset value incf FSR,f ; Move to next unit btfsc STATUS,C ; If reseted, increment next unit incf INDF,f return ; C = 1, if carry was to next unit ;****************************************************************************** ; Routines to display Time and Date ;****************************************************************************** ;-------- Update Display memory with name of day LoadBCDDay movf digitindex,w ; Read names backward sublw .9 movwf Scratch swapf Language,w ; Multiply language code by 7 movwf BCD clrc rrf BCD,f movf Language,w subwf BCD,w addwf WDay,w ; Add day of week movwf BCD ; Multiply by 10 addwf BCD,f movf BCD,w rlf BCD,f rlf BCD_High,f rlf BCD,f rlf BCD_High,f addwf BCD,w btfsc STATUS,C incf BCD_High,f addwf Scratch,w ; Add 9-digitindex btfsc STATUS,C incf BCD_High,f movwf BCD movlw high(DayNames) ; Add address of table call CharPattern ; Get next character clrf PCLATH ; Use page 0 clrf BCD_High addlw -' ' ; Convert ascii code to internal clrc movwf BCD bcf BCD,7 rlf BCD,f ; Multiply by 6 rlf BCD,w rlf BCD_High,f AddBCD16 addwf BCD,f btfsc STATUS,C incf BCD_High,f return ; Store new code CharPatternTab btfsc BCD_High,7 goto CharPatternBin movlw high(CharTab) CharPattern ; If calls out from lower 2K, afther call PCLATH has to be set to 0 addwf BCD_High,w ; Get high byte movwf PCLATH ; Store it in PCLATH movf BCD,w ; Get low byte movwf PCL ; Get next character ;======================================================= ; No execution passes here CharPatternBin ; If calls out from lower 2K, afther call PCLATH has to be set to 0 movlw high(BinTab) ; Get high byte movwf PCLATH ; Store it in PCLATH , PCLATH has only 5 bits - bit7 of BCD removed movf BCD,w ; Get low byte addlw low(BinTab)-(6*('0'-' ')) ; Convert start address of character movwf PCL ; Get next character ;-------- Update Display memory with Digital Time Display DTime movf dpi,f ; btfss STATUS,Z ; goto l_DTime_1 ; display index > 0 clrf digitindex ; display index = 0, load start parameters for clrf dotindex ; digit displaying l_DTime_1 movlw .60 ; subwf dpi,w ; btfsc STATUS,C ; return ; display index >= 60 movf dotindex,w ; btfss STATUS,Z ; goto l_DTime_3 movlw .6 addwf dotindex,f clrf BCD_High btfsc ShowDTime call LoadBCDTime ; dotindex = 0, load new digit btfss ShowDTime call LoadBCDTest ; dotindex = 0, load new digit #ifdef VerticalLEDs movlw .4 ; Move index of character table call AddBCD16 #endif l_DTime_3 decfsz dotindex,f ; goto l_DTime_2 incf digitindex,f ; select next digit return ; to get a gap between digits l_DTime_2 call CharPatternTab ; get the dot pattern for this column clrf PCLATH #ifdef VerticalLEDs goto l_Date_3 #else iorwf tdd,f ; Load pixed data to temp disp data ; incf BCD,f ; Move index of character table ; btfsc STATUS,Z ; incf BCD_High,f ; return IncBCD16 ; movlw .1 goto AddBCD16 #endif ;-------- Update Display memory with Digital Date Display DDate movlw .60 subwf dpi,w ; btfss STATUS,C ; return ; display index < 60 btfss STATUS,Z ; goto l_DDate_1 ; display index > 60 clrf digitindex ; display index = 60, load start parameters for clrf dotindex ; digit displaying l_DDate_1 movf dotindex,w ; btfss STATUS,Z ; goto l_DDate_11 movlw .6 addwf dotindex,f clrf BCD_High btfss ShowDDate goto l_DDate_10 btfss ShowWDay goto l_DDate_9 btfss Second2,3 call LoadBCDWDay btfsc Second2,3 l_DDate_9 call LoadBCDDate ; dotindex = 0, load new digit l_DDate_10 movlw .4 ; Read dot patterns backward call AddBCD16 l_DDate_11 decfsz dotindex,f ; goto l_DDate_2 ; dotindex < 6, display data incf digitindex,f ; select next digit return ; to get a gap between digits l_DDate_2 call CharPatternTab ; get the dot pattern for this column clrf PCLATH l_Date_3 ; movwf Scratch2 ; Flip the dot pattern ; clrf Scratch ; Clear bit 8 ; movlw 7 ; Characters are 7 bit height ;l_Date_4 ; rrf Scratch2,f ; Rotate (right) next bit to C ; rlf Scratch,f ; Rotate (left ) next bit from C ; addlw -1 ; ; btfss STATUS,Z ; Loop for 7 dots ; goto l_Date_4 ; movf Scratch,w ; Get flipped dot pattern call FlipPattern iorwf tdd,f ; Load pixed data to temp disp data DecBCD16 decf BCD,f ; Move table pointer backward incf BCD,w btfsc STATUS,Z decf BCD_High,f return ; ;-------- FlipPattern movwf Scratch2 ; Flip the dot pattern clrf Scratch ; Clear bit 7 movlw 7 ; Characters are 7 bit height l_FlipPattern_1 rrf Scratch2,f ; Rotate (right) next bit to C rlf Scratch,f ; Rotate (left ) next bit from C addlw -1 ; btfss STATUS,Z ; Loop for 7 dots goto l_FlipPattern_1 movf Scratch,w ; Get flipped dot pattern return ;-------- Conv2BCD ; Convert w to BCD (0 <= w <= 99) clrf Scratch2 ; Clear high nibble l_bcd_1 addlw -.10 ; Sub .10 incf Scratch2,f ; Inc high nibble btfsc STATUS,C ; Loop if no carry goto l_bcd_1 addlw .10 ; Re add .10, C=1 decf Scratch2,f ; Correct high nibble swapf Scratch2,f ; Swap to high part of byte addwf Scratch2,w ; Add to low nibble, C=0 return ;-------- Update Display memory with Hands Hands movlw .30 ; difference in offset for hands and digital display subwf dpi,w ; scratch4 = dpi - 30 btfss STATUS,C ; if scratch4 < 0 then addlw .120 ; scratch4 = scratch4 + 120 movwf Scratch4 ; btfss ShowSecHand ; Second hand disabled goto MinuteHand movf Second2,w ; andlw 0xFE ; filter out 1/2 second subwf Scratch4,w ArcSec ; Draw arc if Scratch4<= (Second2 and 0xFE) btfss SecMode ; Set C if dot second hand mode bsf STATUS,C btfss STATUS,C bsf tdd,bSecLED ; Sec led - second hand SecHand btfsc STATUS,Z ; bsf tdd,bSecLED ; Sec led - second hand MinuteHand bcf STATUS,C ; rlf Minute,w ; Clears C, because Minute < .60 movwf Scratch ; Scratch = minute * 2 btfss ShowMinHand ; Minute hand disabled goto HourHand xorwf Scratch4,w ; btfss STATUS,Z ; goto HourHand ; Minute hand not in this dpi btfsc MinMode ; goto DotMinHand movlw B'00001111' ; iorwf tdd,f ; Turn on minute hand bsf tdd,bInnerLED ; Turn on analog innner Leds goto HourHand DotMinHand bsf tdd,bMinLED ; Turn on minute dot HourHand btfss ShowHourHand ; Hour hand disabled return rlf Hour,w ; Get hour movwf Scratch2 ; Scratch2 = hour * 2 rlf Scratch2,f ; Scratch2 = hour * 4 rlf Scratch2,f ; Scratch2 = hour * 8 addwf Scratch2,f ; Scratch2 = hour * 10 movlw .12 ; l_hour_div12 ; Divide (2 * minute) by 12 subwf Scratch,f ; btfss STATUS,C ; goto l_div12_done ; incf Scratch2,f ; advance hour a bit goto l_hour_div12 ; l_div12_done movlw .120 ; Scratch2 = (10 * Hour) + (2 * Minute / 12) btfsc AnalMode24h rrf Scratch2,f l_120 subwf Scratch2,f ; btfsc STATUS,C ; result > 0 ? goto l_120 addwf Scratch2,w ; result was negative, re-add 120 and load it in w xorwf Scratch4,w ; btfsc STATUS,Z ; ; Turn on hour hand bsf tdd,bInnerLED ; Turn on analog innner Leds return ; ;-------- Get character Digital Date Display ; For characters ' ' to 'J' Set BCD_High = 0 ; For characters 'K' to 'u' Set BCD_High = 1 ; For characters 'v' to 0x9F Set BCD_High = 2 ; ;-------- LoadBCDDate ; load BCD with date digits movlw high(DateTable0) movwf PCLATH movf digitindex,w ; addwf digitindex,w ; btfss ShowCentury addlw DF_YYMMDD-DF_YYYYMMDD btfsc DateFormat addlw DF_DDMMYYYY-DF_YYYYMMDD DateTable0 addwf PCL,f ; ; DateFormat: YYYY-MM-DD DF_YYYYMMDD ;0-- movf Day,w ; Day 1 digit goto l_convert ; ;1-- movf Day,w ; Day 10 digit goto l_convert_swap ; ;2-- movlw 6*('-'-' ') ; '-' goto l_dot ; ;3-- movf Month,w ; Month 1 digit goto l_convert ; ;4-- movf Month,w ; Month 10 digit goto l_convert_swap ; ;5-- movlw 6*('-'-' ') ; '-' goto l_dot ; ;6-- movf Year,w ; Year 1 digit goto l_convert ; ;7-- movf Year,w ; Year 10 digit goto l_convert_swap ;8-- movf Century,w ; Year 100 digit goto l_convert ; ;9-- movf Century,w ; Year 1000 digit goto l_convert_swap ; DateFormat: DD-MM-YYYY DF_DDMMYYYY ;10-- movf Year,w ; Year 1 digit goto l_convert ; ;11-- movf Year,w ; Year 10 digit goto l_convert_swap ; ;12-- movf Century,w ; Year 100 digit goto l_convert ; ;13-- movf Century,w ; Year 1000 digit goto l_convert_swap ; ;14-- movlw 6*('-'-' ') ; '-' goto l_dot ; ;15-- movf Month,w ; Month 1 digit goto l_convert ; ;16-- movf Month,w ; Month 10 digit goto l_convert_swap ; ;17-- movlw 6*('-'-' ') ; '-' goto l_dot ; ;18-- movf Day,w ; Day 1 digit goto l_convert ; ;19-- movf Day,w ; Day 10 digit goto l_convert_swap ; DateFormat: YY-MM-DD DF_YYMMDD ;20-- movlw 6*(' '-' ') ; ' ' goto l_dot ; ;21-- movf Day,w ; Day 1 digit goto l_convert ; ;22-- movf Day,w ; Day 10 digit goto l_convert_swap ; ;23-- movlw 6*('-'-' ') ; '-' goto l_dot ; ;24-- movf Month,w ; Month 1 digit goto l_convert ; ;25-- movf Month,w ; Month 10 digit goto l_convert_swap ; ;26-- movlw 6*('-'-' ') ; '-' goto l_dot ; ;27-- movf Year,w ; Year 1 digit goto l_convert ; ;28-- movf Year,w ; Year 10 digit goto l_convert_swap ;29-- movlw 6*(' '-' ') ; ' ' goto l_dot ; ; DateFormat: DD-MM-YY DF_DDMMYY ;30-- movlw 6*(' '-' ') ; ' ' goto l_dot ; ;31-- movf Year,w ; Year 1 digit goto l_convert ; ;32-- movf Year,w ; Year 10 digit goto l_convert_swap ; ;33-- movlw 6*('-'-' ') ; '-' goto l_dot ; ;34-- movf Month,w ; Month 1 digit goto l_convert ; ;35-- movf Month,w ; Month 10 digit goto l_convert_swap ; ;36-- movlw 6*('-'-' ') ; '-' goto l_dot ; ;37-- movf Day,w ; Day 1 digit goto l_convert ; ;38-- movf Day,w ; Day 10 digit goto l_convert_swap ;39-- goto l_Space ; DateTable1 IF high(DateTable0) != high(DateTable1) ERROR "Date jump table page error" ENDIF LoadBCDTime ; load BCD with time digits movf Hour,w btfss Digit12Mode ; 24 hour mode goto lBCDHour addlw -.12 ; Prepare to 12 hour mode btfss STATUS,C ; If hour > .12: add -.12 movf Hour,w ; Reload hour if hour btfsc STATUS,Z addlw .12 ; Correct 0 to 12 lBCDHour movwf BCD movlw high(TimeTable0) movwf PCLATH movf digitindex,w ; addwf digitindex,w ; Clears C TimeTable0 addwf PCL,f ; #ifdef VerticalLEDs ;0-- nop goto l_Space ; ;1-- rrf Second2,w ; Second 1 digit goto l_convert ; ;2-- rrf Second2,w ; Second 10 digit goto l_convert_swap ; ;3-- movlw 6*(':'-' ') ; ':' goto l_dot ; ;4-- movf Minute,w ; Minute 1 digit goto l_convert ; ;5-- movf Minute,w ; Minute 10 digit goto l_convert_swap ; ;6-- movlw 6*(':'-' ') ; ':' goto l_dot ; ;7-- movf BCD,w ; Hour 1 digit goto l_convert ; ;8-- movf BCD,w ; Hour 10 digit goto l_leading_swap_PM; ;9-- movf BCD,w goto l_PM ; #else ;0-- movf BCD,w goto l_PM ; ;1-- movf BCD,w ; Hour 10 digit goto l_leading_swap_PM; ;2-- movf BCD,w ; Hour 1 digit goto l_convert ; ;3-- movlw 6*(':'-' ') ; ':' goto l_dot ; ;4-- movf Minute,w ; Minute 10 digit goto l_convert_swap ; ;5-- movf Minute,w ; Minute 1 digit goto l_convert ; ;6-- movlw 6*(':'-' ') ; ':' goto l_dot ; ;7-- rrf Second2,w ; Second 10 digit goto l_convert_swap ; ;8-- rrf Second2,w ; Second 1 digit goto l_convert ; ;9-- goto l_Space ; #endif TimeTable1 IF high(TimeTable0) != high(TimeTable1) ERROR "Time jump table page error" ENDIF LoadBCDWDay btfsc ShowDayName goto LoadBCDDay movlw high(TempTable0) movwf PCLATH movf digitindex,w ; addwf digitindex,w ; Clears C TempTable0 addwf PCL,f ; ;0-- l_Space movlw 6*(' '-' ') ; ' ' goto l_dot_clr ; ;1-- movlw 6*(' '-' ') ; ' ' goto l_dot_clr ; ;2-- movlw 6*(' '-' ') ; ' ' goto l_dot_clr ; ;3-- movlw 6*(' '-' ') ; ' ' goto l_dot_clr ; ;4-- movlw 6*(' '-' ') ; ' ' goto l_dot_clr ; ;5-- incf WDay,w ; Day of week goto l_convert ; ;6-- movlw 6*(' '-' ') ; ' ' goto l_dot_clr ; ;7-- movlw 6*(' '-' ') ; ' ' goto l_dot_clr ; ;8-- movlw 6*(' '-' ') ; ' ' goto l_dot_clr ; ;9-- goto l_Space ; TempTable1 IF high(TempTable0) != high(TempTable1) ERROR "Temp jump table page error" ENDIF ;-------- Update Display memory with rotation measurement LoadBCDTest ; load BCD with test digits movlw high(TestTable0) movwf PCLATH movf digitindex,w ; addwf digitindex,w ; Clears C bsf STATUS,RP1 ; Bank2 TestTable0 addwf PCL,f ; #ifdef VerticalLEDs ;0-- movlw 6*(' '-' ') ; ' ' goto l_dot ; ;1-- movlw low(6*('m'-' ')); 'm' goto l_dot_BCDH_1 ; ;2-- movlw 6*('/'-' ') ; '/' goto l_dot ; ;3-- movlw low(6*('R'-' ')); 'R' goto l_dot_BCDH_1 ; ;4-- movf rot_bcd,w ; Rotation digit 1 goto l_BCD ; ;5-- movf rot_bcd+1,w ; Rotation digit 10 goto l_BCD ; ;6-- movf rot_bcd+2,w ; Rotation digit 100 goto l_BCD ; ;7-- movf rot_bcd+3,w ; Rotation digit 1000 goto l_BCD ; ;8-- movf rot_bcd+4,w ; Rotation digit 10000 goto l_BCD ; ;9-- movlw 6*(' '-' ') ; ' ' goto l_dot ; #else ;0-- movlw 6*(' '-' ') ; ' ' goto l_dot ; ;1-- movf rot_bcd+4,w ; Rotation digit 10000 goto l_BCD ; ;2-- movf rot_bcd+3,w ; Rotation digit 1000 goto l_BCD ; ;3-- movf rot_bcd+2,w ; Rotation digit 100 goto l_BCD ; ;4-- movf rot_bcd+1,w ; Rotation digit 10 goto l_BCD ; ;5-- movf rot_bcd,w ; Rotation digit 1 goto l_BCD ; ;6-- movlw low(6*('R'-' ')); 'R' goto l_dot_BCDH_1 ; ;7-- movlw 6*('/'-' ') ; '/' goto l_dot ; ;8-- movlw low(6*('m'-' ')); 'm' goto l_dot_BCDH_1 ; ;9-- goto l_Space ; #endif TestTable1 IF high(TestTable0) != high(TestTable1) ERROR "Test jump table page error" ENDIF ;======================================================= ; No execution passes here l_dot_BCDH_2 incf BCD_High,f l_dot_BCDH_1 incf BCD_High,f l_dot_clr bcf BCD_High,7 ; l_dot bcf STATUS,RP1 ; Bank 0 --- For entry from DrawTest movwf BCD ; return l_leading_swap_PM call l_convert_swap xorlw 6*('0'-' ') btfss STATUS,Z xorlw 6*('0'-' ') btfss STATUS,Z goto l_dot goto l_pm_ap l_leading_swap call l_convert_swap xorlw 6*('0'-' ') btfss STATUS,Z xorlw 6*('0'-' ') goto l_dot l_PM btfss Digit12Mode goto l_Space call Conv2BCD andlw 0xF0 btfsc STATUS,Z goto l_Space l_pm_ap movlw .12 subwf Hour,w btfss STATUS,C ; goto l_Space movlw 6*("'"-' ') ; ''' goto l_dot_clr l_convert_swap call Conv2BCD l_BCD_swap movwf BCD ; swapf BCD,w ; goto l_BCD l_convert call Conv2BCD l_BCD andlw 0x0F ; bcf STATUS,RP1 ; Bank0 ;l_mult6 movwf BCD ; addwf BCD,f ; BCD = BCD * 2 sublw .9 ; w = 9 - BCD btfss STATUS,C ; Check for decimal character (A..F converted to space) goto l_Space movf BCD,w ; W = BCD * 2 addwf BCD,w ; W = BCD * 4 addwf BCD,w ; W = BCD * 6 addlw 6*('0'-' ') ; Convert to character code, C=0 btfsc flags5,bBinMode ; Test for binary mode bsf BCD_High,7 goto l_dot ;-------- Update Display memory with Time Ticks every 5 minutes Ticks movf dpi,w xorwf Scratch3,w btfss STATUS,Z ; At a tick position return bsf tdd,bTickLED ; Turn on analog tick Leds btfsc STick12 goto Ticks10 ; If single tick at 12 always add .10 movf Scratch3,w ; Dpi=30 at 12 o clock xorlw .20 btfsc STATUS,Z ; Draw " at 12, ' at other goto Ticks9 xorlw .20^.29 btfsc STATUS,Z goto Ticks2 xorlw .29^.31 btfss STATUS,Z Ticks10 movlw .1 Ticks9 addlw .7 ; W = 0 if jump to here Ticks2 addlw .2 ; W = 0 if jump to here addwf Scratch3,f return ;****************************************************************************** ; Processing of RC5 command's, called by main program if a command is received ;****************************************************************************** ProcessRC5 ifdef RC5AddrCheck movf RC5_Addr,w ; xorlw RemoteAddr ; test if RC5_Addr = RemoteAddr andlw 0x1F ; Only 5 bits to test btfss STATUS,Z ; goto ProcessRC5Done ; endif ; movf RC5_Cmd,w ; ; andlw 0x7F ; movf RC5_Cmd,w ; xorwf RC5_Cmd2,f ; Compare command with toggle bit btfsc STATUS,Z ; goto ProcessRC5Done ; andlw 0x7F ; New command, remove toggle bit select_w case SEC_CL ; goto ClrSecond ; Adjust time : Clear Seconds case SEC_UP ; goto IncSecond ; Adjust time : Increment Seconds case SEC_DN ; goto DecSecond ; Adjust time : Decrement Seconds case MIN_UP ; goto IncMinute ; Adjust time : Increment Minutes case MIN_DN ; goto DecMinute ; Adjust time : Decrement Minutes case HOUR_UP ; goto IncHour ; Adjust time : Increment Hours case HOUR_DN ; goto DecHour ; Adjust time : Decrement Hours case DAY_UP ; goto IncDay ; Adjust Date : Increment Days case DAY_DN ; goto DecDay ; Adjust Date : Decrement Days case WDAY_UP ; goto IncWDay ; Adjust Date : Increment Day of week case WDAY_DN ; goto DecWDay ; Adjust Date : Decrement Day of week case MON_UP ; goto IncMonth ; Adjust Date : Increment Month case MON_DN ; goto DecMonth ; Adjust Date : Decrement Month case YEAR_UP ; goto IncYear ; Adjust Date : Increment Year case YEAR_DN goto DecYear ; Adjust Date : Decrement Year case INDEX_UP ; goto DecDispOffset ; Adjust index sensor Offset, rotate display left case INDEX_DN ; goto IncDispOffset ; Adjust index sensor Offset, rotate display right ; Toggle functions ; movf RC5_Cmd,w ; ; xorwf RC5_Cmd2,f ; ; btfsc STATUS,Z ; ; goto ProcessRC5Done ; ; andlw 0x7F ; ; select_w case TOuterLine ; goto ToggleOuterLed ; Outer Line case DigitalMode ; goto ChgDigitMode case AnalogueMode ; goto ChgAnalogMode case AnalogueHands ; goto ChgAnalogHands case DemoM ; goto ToggleDemo ; Demo Mode case TextMode ; goto ToggleText ; Scrolling Text Mode case TStaticText ; goto ToggleStaticText; Static Text Mode case TWDayName ; goto ToggleName ; Togge day name display mode case SET_SP ; goto SetScrollSp ; Set Scrolling speed case TBinMode ; goto ToggleBinMode ; Toggle binay mode case TLANGUAGE ; goto SetLanguage ; Set Language case TTicks ; goto ToggleTicks ; Toggle Ticks mode case STANDBY ; bcf fDspEnabled ; Disable LEDs ; default ; If none of listed codes goto ProcessRC5Done SetScrollSp movlw 0xF0 ; Set scrolling speed addwf CCPR1H,w iorlw 0x0F ; Speeds are 0x0F, 0x1F, ..., 0xEF, 0xFF movwf CCPR1H call ScrollSpToRtc ; Save it to RTC goto ProcessRC5Done ; SetLanguage ; Set language movlw RTC_Language movwf I2CWordAddr incf Language,w andlw 0x0F ; Only 16 language movwf Language goto WriteToRtc ; ToggleBinMode movlw RTC_flags5 movwf I2CWordAddr movlw 1 << bBinMode xorwf flags5,w andlw 0x30 movwf flags5 goto WriteToRtc ; ToggleName movlw 1 << bShowDayName ; addwf flags,w xorwf flags,w andlw (1< TimeCheck will correct incf Year,f TimeSetRC5Done bsf RC5_TimeSet bcf fTimeInv ProcessRC5Done movf RC5_Cmd,w ; movwf RC5_Cmd2 ; bsf NewTime ; force display update bcf RC5_DataReady ; SetOuterLed btfss fDspEnabled ; Disable LEDs goto OuterLedOff bsf CCPR1L,bOuterLED; btfss ShowOuter ; OuterLedOff bcf CCPR1L,bOuterLED; Set OuterLED as in flags bcf INTCON,GIE ; Disable interrupts call ToPortA ; Changing PORTA is a critical operation, PORTA and it's copy has to be the same everytime bsf INTCON,GIE ; Enable interrupts return ; ToggleStaticText ; Static text bcf fScrollOn ; Scrolling OFF movlw 1 << bText ; toggle Text flag xorwf flags2,f ; btfsc fText call PrintDisp ; Print static message goto ProcessRC5Done ; ToggleText call TextON_OFF ; Scrolling Text mode goto ProcessRC5Done ; ToggleDemo movlw 1 << bDemo ; xorwf flags2,f ; goto ProcessRC5Done ; ;****************************************************************************** ; Display mode routines DemoMode movf Second2,f ; btfss STATUS,Z ; return TextON_OFF bcf fScrollOn ; Scrolling OFF movlw 1 << bText ; toggle Text flag xorwf flags2,f ; btfss fText ; test Text flag return ; bsf fScrollOn ; Scrolling ON ClearDisplay bsf STATUS,RP1 ; jump to bank 2 movlw .120 movwf ch_dot_index movlw 0x20 ; set FSR to 0x20 cldisp movwf FSR ; clrf INDF ; clear display memory call CheckIncrementFSR; Increment and test pointer decfsz ch_dot_index,f ; Loop for .120 positions goto cldisp ; Loop clears ch_dot_index ; init stuff needed for Text Scroll function clrf ch_blanking ; clrf ch_flags btfsc fDemo bsf ch_flags,bDemo if (EEADR & 0x180)!=(ch_blanking & 0x180) banksel EEADR ; Bank1 endif movlw 0x80 ; If EEPROM_SIZE==256 there are two messages andwf EEADR,f ; Clear address for next read - keep message index RetBank0 clrf STATUS ; goto bank 0 return ;****************************************************************************** ; Main program ;****************************************************************************** main InitIO ; clrf PORTA ; clrf PORTB ; PortA, PortB, PortC cleared at reset vector ; clrf PORTC ; all LED OFF ifdef MotorDirFromCalibration btfsc PORTC,bCalibration ; Read Calibration input bsf MotorDirCC ; Set direction of motor rotation endif bsf STATUS,RP0 ; Bank1 movlw 0xFF^((1< .127 means invalid code, clrf ch_dot_point_L ; relpace with " " movlw .6 ; Called only when ch_dot_index == 0 addwf ch_dot_index,f ; indicate start of new char, clears C rlf ch_dot_point_L,f; ch_dot_point_L = (ascii_code - .32) * 2, C = 0 because ch_dot_point_L <.128 ;;; rlf ch_dot_point_H,f; Why rotate a 0x00 if C=0 [ max 0x7F<<1 == 0xFE , C = 0 ] ; C = 0 rlf ch_dot_point_L,w; W = (ascii_code - 32) * 4 rlf ch_dot_point_H,f; Save C to high part AddToCharOffset addwf ch_dot_point_L,f; ch_dot_point_L = (ascii_code - 32) * 6 btfsc STATUS,C ; If C==1, increment high part incf ch_dot_point_H,f; ch_dot_point (16bit) = (ascii_code - 32) * 6 return CharGenx ; Calls out of first 2K movlw high(CharTab) ; Add address of char gen table addwf ch_dot_point_H,w; Get high path of index movwf PCLATH ; PCHATH 5.4 = 01 movf ch_dot_point_L,w; IT routine MUST save PCLATH movwf PCL ;======================================================= ; No execution passes here ;-------- LoadChrData ; Must be called with Bank 2 active call CharGenx ; Get next dot pattern clrf PCLATH ; Call / Goto in first 2K incf ch_dot_point_L,f; pointer++ btfsc STATUS,Z ; If owerflow incf ch_dot_point_H,f; increment high part (J and u are on boundary) return ;-------- LoadChrDataBackward call CharGenx ; Get next dot pattern clrf PCLATH ; Call / Goto in first 2K movf ch_dot_point_L,f btfsc STATUS,Z ; If onderflow decf ch_dot_point_H,f; pointer-=256 decf ch_dot_point_L,f; pointer-- return ;-------- PrintDisp call ClearDisplay ; clear display content, clears ch_dot_index ; None of these controller has EEADR on Bank3 if (EEADR & 0x080)==0x80 bsf STATUS, RP0 ; Bank1 endif if (EEADR & 0x100)==0x100 bsf STATUS, RP1 ; Bank2 endif movlw 0x70 movwf EEADR ; start of static message in EEProm #ifdef VerticalLEDs movlw 0xE1-(.5*.6) #else movlw 0xE1 ; Start for first char a 7 o'clock #endif movwf FSR ; movlw .15 ; 15 char to display movwf Scratch3 ; pdisp_1 call EpromRead ; Read next character from EEProm, returns with Bank2 selected call CharOffset ; call character data pdisp_2 call LoadChrData ; load pixel data from CharGen #ifdef VerticalLEDs call FlipPattern #endif movwf INDF ; store char pixel data in display memory #ifdef VerticalLEDs call CheckDecrementFSR ; decrement FSR #else call CheckIncrementFSR ; increment FSR #endif movwf FSR ; decfsz ch_dot_index,f ; 6 dot's in one character, all displayed ? goto pdisp_2 ; decfsz Scratch3,f ; Loop for 15 characters goto pdisp_1 bcf STATUS,RP1 ; Bank0 return ;-------- ScrollText bsf STATUS,RP1 ; Bank2 movf ch_dot_index,w ; btfss STATUS,Z ; ch_dot_index == 0 ? goto Scroll_0 ; No - process next dot pattern movf ch_blanking,w ; ch_blanking == 0 ? btfsc STATUS,Z ; goto Scroll_read_ee ; Yes - Read character form EEProm decfsz ch_blanking,f ; goto Scroll_2 ; insert one more " " ; ch_blanking == 0 - End of message and leadout ; None of these controller has EEADR on Bank3 if (EEADR & 0x180)!=(ch_blanking & 0x180) banksel EEADR ; Bank1 endif if EEPROM_SIZE==256 movlw 0x80 ; On 16F684A, 16F87, 16F88 change message andwf EEADR,f xorwf EEADR,f else clrf EEADR ; On 16F628(A) Go to beginning of message endif if (EEADR & 0x180)!=(ch_blanking & 0x180) banksel ch_blanking ; Bank2 endif btfss ch_flags,bDemo ; in demo mode? goto Scroll_read_ee ; No: re-read char clrf STATUS ; Bank0 goto TextON_OFF ; at end of line, turn text off Scroll_read_ee call EpromRead ; Read next character from EEProm, returns with Bank2 selected btfss STATUS,Z ; 0x00 indicates end of line goto Scroll_1 ; movlw 0x0F ; insert 15 " " at end of string to clear display movwf ch_blanking ; Scroll_2 movlw " " ; w = " " Scroll_1 call CharOffset ; call character data Scroll_0 call LoadChrData ; load pixel data from CharGen decf ch_dot_index,f ; dec ch_dot_index #ifdef VerticalLEDs call FlipPattern #else movwf Scratch ; #endif ifdef ReversTextScroll movlw 0xE4 ; Start at begin of 7 o'clock character else movlw 0xBF ; Start at end of 5 o'clock character endif movwf FSR ; Init display memory pointer ScrollLoop movf INDF,w ; Get current display data = Previous_INDF xorwf Scratch,w ; W = (Previous_INDF ^ Scratch) xorwf INDF,f ; INDF = (Previous_INDF ^ Scratch) ^ INDF = Scratch xorwf Scratch,f ; Scratch = (Previous_INDF ^ Scratch) ^ Scratch = Previous_INDF ifdef ReversTextScroll call CheckIncrementFSR ; Move pointer movwf FSR ; xorlw 0xC0 ; check end of 5 o'clock character else call CheckDecrementFSR ; Move pointer movwf FSR ; xorlw 0xE3 ; check begin of 7 o'clock character endif btfss STATUS,Z ; Check for end of area: FSR = 0xE3 (or 0xC0) goto ScrollLoop ; goto RetBank0 ;****************************************************************************** ; Some general functions ;****************************************************************************** ;****************************************************************************** ; Reads next character from EEProm memory - returns with Bank2 selection EpromRead banksel EECON1 ; Select Bank of EEDATA, may be on Bank1 or Bank3 bsf EECON1, RD ; read character from EEProm if (EECON1 & 0x180)!=(EEADR & 0x180) bcf STATUS,RP0 ; Bank2 if EECON1 is on Bank3 endif ; If EECON1 is on Bank1 EEADR is there incf EEADR,f ; inc address for next read movf EEDATA,w ; Get next character if (EEDATA & 0x180)!=(ch_dot_index & 0x180) banksel ch_dot_index ; Bank2 if EEADR is on Bank1 endif ; Returns with Bank2 selected return ;- check correct decrement of Display memory pointer CheckDecrementFSR decf FSR,w CheckDecrement xorlw 0x1F ; btfsc STATUS,Z ; movlw 0xEF ^ 0x1F ; xorlw 0x9F ^ 0x1F ; btfsc STATUS,Z ; movlw 0x47 ^ 0x9F ; xorlw 0x9F ; return ; ;- check correct increment of Display memory pointer CheckIncrementFSR incf FSR,w CheckIncrement xorlw 0x48 ; btfsc STATUS,Z ; movlw 0xA0 ^ 0x48 ; xorlw 0xF0 ^ 0x48 ; btfsc STATUS,Z ; movlw 0x20 ^ 0xF0 ; xorlw 0xF0 ; return ; ;****************************************************************************** ; I2C RTC functions ;****************************************************************************** InitTime ; Init time from I2C RTC movlw RTC_Second ; Address of Seconds ; I2CWordAddr = 0x02 call I2CByteReadSetAddr; Read seconds call BcdToBin movwf Second2 addwf Second2,f ; I2CWordAddr = 0x03 call I2CByteRead ; Read minute call BcdToBin movwf Minute ; I2CWordAddr = 0x04 call I2CByteRead ; Read hour call BcdToBin3F ; Mask format and AM/PM movwf Hour ; I2CWordAddr = 0x05 call I2CByteRead ; Read day call BcdToBin3F movwf Day swapf tdd,f rrf tdd,f rrf tdd,w andlw 0x03 movwf dotindex ; Save year bit 1..0 ; I2CWordAddr = 0x06 call I2CByteRead ; Read Month andlw 0x1F call BcdToBin movwf Month swapf tdd,f rrf tdd,w andlw 0x07 movwf WDay ; I2CWordAddr = 0x07 movlw RTC_Year ; Address of Year call I2CByteReadSetAddr; Read year movwf Year ; I2CWordAddr = 0x11 call I2CByteRead ; Read year ^ 0xff xorlw 0xFF xorwf Year,w ; I2CWordAddr = 0x12 btfss STATUS,Z goto InitTimeDef ; Year not valid, init with default values call I2CByteRead ; Read Century movwf Century ; I2CWordAddr = 0x13 call I2CByteRead ; Read flags movwf flags ; I2CWordAddr = 0x14 call I2CByteRead ; Read DispOffset movwf DispOffset call ValidateOffs ; I2CWordAddr = 0x15 call I2CByteRead ; Read Scroll speed iorlw 0xCF movwf CCPR1H ; I2CWordAddr = 0x16 call I2CByteRead ; Read flags3 movwf flags3 ; I2CWordAddr = 0x17 call I2CByteRead ; Read flags4 andlw 0x30 movwf CCP1CON ; I2CWordAddr = 0x18 call I2CByteRead ; Read language code andlw 0x0F movwf Language ; I2CWordAddr = 0x19 call I2CByteRead ; Read flags4 andlw 0x30 movwf flags5 ; I2CWordAddr = 0x1A movf Year,w ; Has the RTC increment Year xorwf dotindex,w andlw 0x03 btfsc STATUS,Z return ; No, return incf Year,f ; Yes, increment Year call TimeCheck ; Will correct Year and Century goto StoreYear ValidateOffs movlw 0x20 subwf DispOffset,w btfss STATUS,C goto DifDispOffs movlw 0xF0 subwf DispOffset,w btfsc STATUS,C goto DifDispOffs movlw 0xA0 subwf DispOffset,w btfsc STATUS,C return movlw 0x48 subwf DispOffset,w btfss STATUS,C return goto DifDispOffs InitTimeDef movlw .12 ;why do clocks always start movwf Hour ;at 12:00 ? clrf Minute clrf Second2 movlw .1 movwf Day clrf WDay movwf Month movwf Year movlw .20 movwf Century ;;1 movlw (1< 1 ; 5us wait BCF STATUS,RP0 ; Bank 0 to read PORTA BCF STATUS, C ; clear carry BTFSC SDA ; test SDA bit BSF STATUS, C ; set carry if SDA == 1 BSF STATUS,RP0 ; Bank 1 to control TRISA RLF tdd,F ; tdd = (tdd << 1) | input bit CALL LOW_SCL ; SCL -> 0 ; 5us wait DECFSZ FSR, F ; decrement bit counter GOTO IN_BIT CALL NACK ; Clock out nack bit SDA must be high (input) ToStop CALL STOP ; Generate stop condition incf I2CWordAddr,f ; Increment word address movf tdd,W ; Return data just read bcf STATUS,RP0 ; Back to Bank 0 return ;****************************************************************************** ; Writes W to RTC in BCD format, address is in I2CWordAddr ; I2CWordAddr incremented afther execution I2CBcdWrite call Conv2BCD ; Convert w to BCD ; Writes W to RTC, address is in I2CWordAddr ; I2CWordAddr incremented afther execution I2CByteWrite movwf tdd ; Save data to be written to RTC CALL START ; Generate Start, returns with 10100000 RTCADDR in W CALL OUT_BYTE ; Send slave address byte + nack CALL OUT_BYTE_ADDR ; Send word address byte + nack MOVF tdd, W ; Get output data CALL OUT_BYTE ; Send data byte + nack goto ToStop ; Generate stop condition on I2C bus STOP: ; SDA 0 -> 1 while SCL == 1 ; SCL must be LOW afther (N)ACK's CLOCK_PULSE CALL LOW_SDA ; SDA -> 0 - NACK has done it CALL HIGH_SCL ; SCL -> 1 and make 5us stop setup time ; Make SDA high by making it input HIGH_SDA: ; high impedance by making SDA an input BSF SDA ; make SDA pin an input GOTO DELAY_5US ; SDA -> 1 and make 5us bus free time ; Generate start / repeated start condition on I2C bus START: ; SDA 1 -> 0 while SCL == 1, then SCL -> 0 BSF STATUS, RP0 ; Bank 1 - Access TRISA CALL HIGH_SDA ; SDA 0 -> 1 ; wait 5 us - For repeated start CALL HIGH_SCL ; SCL 0 -> 1 ; make 5 us Start setup time CALL LOW_SDA ; SDA 1 -> 0 ; make 5 us Start hold time GOTO LOW_SCL ; SCL 1 -> 0 ; wait 5 us ; Shift out a byte to I2C bus, clock in (n)ack, get data from I2CWordAddr OUT_BYTE_ADDR MOVF I2CWordAddr,w ; output address to be read ; Shift out a byte to I2C bus, clock in (n)ack, data is in w OUT_BYTE: ; send o_byte on I2C bus movwf BCD ; Store data to send MOVLW .8 MOVWF FSR ; Loop for 8 bits OUT_BIT: BTFSC BCD,7 ; if one, send a one CALL HIGH_SDA ; SDA at logic one BTFSS BCD,7 ; if zero, send a zero CALL LOW_SDA ; SDA at logic zero CALL CLOCK_PULSE ; SCL -> 1 ; 5 us wait, SCL -> 0 ; 5 us wait RLF BCD,F ; left shift, move mext bit to bit7 DECFSZ FSR,F ; decrement bit counter - Leaves with FSR = 0 GOTO OUT_BIT ; Clock in/out (n)ack NACK: ; bring SDA high and clock, SDA must be input CALL HIGH_SDA CALL CLOCK_PULSE ; SCL -> 1 ; 5 us wait, SCL -> 0 ; 5 us wait ; Make SDA low by making it output LOW_SDA: ; SDA -> 0 ; 5 us wait BCF SDA ; make SDA pin an output GOTO DELAY_5US ; Generate clock pulse ; SCL -> 1 ; 5 us wait, SCL -> 0 ; 5 us wait CLOCK_PULSE: ; SCL momentarily to logic one CALL HIGH_SCL ; SCL -> 1 ; 5 us wait ; Make SCL low by making it output LOW_SCL: ; SCL -> 0 ; 5 us wait BCF SCL ; make SCL pin an output GOTO DELAY_5US ; Make SCL high by making it input HIGH_SCL: ; SCL -> 1 ; wait 5 us BSF SCL ; make SCL pin an input ; Delay 5uS @ 20MHz DELAY_5US: ; provides nominal >5 us delay @20MHz ; 1 instuction takes 200ns MOVLW .8 ; 0.2 us MOVWF dpi ; 0.2 us DELAY_1: ; Loop of 3 inst. time DECFSZ dpi, F ; 7*0.2+0.4 us GOTO DELAY_1 ; 7*0.4 us RETLW RTC_ADDR ; Return address of RTC for OUT_BYTE 0.4 us else I2CByteReadSetAddr ; movwf I2CWordAddr ; Store address I2CByteRead I2CBcdWrite call Conv2BCD ; Convert w to BCD I2CByteWrite incf I2CWordAddr,f return endif ;****************************************************************************** ; Base conversion function for rotation displaying ;****************************************************************************** Word2Bcd5 ;Takes hex number in NumH:NumL Returns decimal in ;TenK:Thou:Hund:Tens:Ones ;written by John Payson ;input ;=A3*16^3 + A2*16^2+ A1*16^1+ A0*16^0 ;=A3*4096 + A2*256 + A1*16 + A0 NumH EQU rotation_H ;A3*16+A2 NumL EQU rotation_L ;A1*16+A0 ;=B4*104 + B3*103 + B2*102 + B1*101 + B0*100 ;=B4*10000 + B3*1000 + B2*100 + B1*10 + B0 TenK EQU rot_bcd+4 ;B4 Thou EQU rot_bcd+3 ;B3 Hund EQU rot_bcd+2 ;B2 Tens EQU rot_bcd+1 ;B1 Ones EQU rot_bcd ;B0 bsf STATUS,RP1 ; Bank2 swapf NumH,w ;w = A2*16+A3 iorlw 0xF0 ;w = A3-16 movwf Thou ;B3 = A3-16 addwf Thou,f ;B3 = 2*(A3-16) = 2A3 - 32 addlw .226 ;w = A3-16 - 30 = A3-46 movwf Hund ;B2 = A3-46 addlw .50 ;w = A3-46 + 50 = A3+4 movwf Ones ;B0 = A3+4 movf NumH,w ;w = A3*16+A2 andlw 0x0F ;w = A2 addwf Hund,f ;B2 = A3-46 + A2 = A3+A2-46 addwf Hund,f ;B2 = A3+A2-46 + A2 = A3+2A2-46 addwf Ones,f ;B0 = A3+4 + A2 = A3+A2+4 addlw .233 ;w = A2 - 23 movwf Tens ;B1 = A2-23 addwf Tens,f ;B1 = 2*(A2-23) addwf Tens,f ;B1 = 3*(A2-23) = 3A2-69 (Doh! thanks NG) swapf NumL,w ;w = A0*16+A1 andlw 0x0F ;w = A1 addwf Tens,f ;B1 = 3A2-69 + A1 = 3A2+A1-69 range -69...-9 addwf Ones,f ;B0 = A3+A2+4 + A1 = A3+A2+A1+4 and Carry = 0 (thanks NG) rlf Tens,f ;B1 = 2*(3A2+A1-69) + C = 6A2+2A1-138 and Carry is now 1 as tens register had to be negitive rlf Ones,f ;B0 = 2*(A3+A2+A1+4) + C = 2A3+2A2+2A1+9 (+9 not +8 due to the carry from prev line, Thanks NG) comf Ones,f ;B0 = ~(2A3+2A2+2A1+9) = -2A3-2A2-2A1-10 (ones complement plus 1 is twos complement. Thanks SD) ;;First two instructions make up negation. So, ;;Ones = -1 * Ones - 1 ;; = - 2 * (A3 + A2 + A1) - 9 - 1 ;; = - 2 * (A3 + A2 + A1) - 10 rlf Ones,f ;B0 = 2*(-2A3-2A2-2A1-10) = -4A3-4A2-4A1-20 movf NumL,w ;w = A1*16+A0 andlw 0x0F ;w = A0 addwf Ones,f ;B0 = -4A3-4A2-4A1-20 + A0 = A0-4(A3+A2+A1)-20 range -215...-5 Carry=0 rlf Thou,f ;B3 = 2*(2A3 - 32) = 4A3 - 64 movlw 0x07 ;w = 7 movwf TenK ;B4 = 7 ;B0 = A0-4(A3+A2+A1)-20 ;-5...-200 ;B1 = 6A2+2A1-138 ;-18...-138 ;B2 = A3+2A2-46 ;-1...-46 ;B3 = 4A3-64 ;-4...-64 ;B4 = 7 ;7 ; At this point, the original number is ; equal to TenK*10000+Thou*1000+Hund*100+Tens*10+Ones ; if those entities are regarded as two's compliment ; binary. To be precise, all of them are negative ; except TenK. Now the number needs to be normal- ; ized, but this can all be done with simple byte ; arithmetic. movlw .10 ;w = 10 Lb1: ;do addwf Ones,f ; B0 += 10 decf Tens,f ; B1 -= 1 btfss 3,0 goto Lb1 ; while B0 < 0 Lb2: ;do addwf Tens,f ; B1 += 10 decf Hund,f ; B2 -= 1 btfss 3,0 goto Lb2 ; while B1 < 0 Lb3: ;do addwf Hund,f ; B2 += 10 decf Thou,f ; B3 -= 1 btfss 3,0 goto Lb3 ; while B2 < 0 Lb4: ;do addwf Thou,f ; B3 += 10 decf TenK,f ; B4 -= 1 btfss 3,0 goto Lb4 ; while B3 < 0 movf TenK,w ; Remove leading zeros movwf FSR movlw 0x0F btfsc STATUS,Z movwf TenK movf Thou,w iorwf FSR,f movlw 0x0F btfsc STATUS,Z movwf Thou movf Hund,w iorwf FSR,f movlw 0x0F btfsc STATUS,Z movwf Hund movf Tens,w iorwf FSR,f movlw 0x0F btfsc STATUS,Z movwf Tens bcf STATUS,RP1 ; Bank0 bcf NewRot return END ; directive 'end of program'