MicroChip pic 16c84 16f84 asm code for propeller clock


;-----------------------------------------------------------------
; 3mode.asm
; Copyright Don Zehnder all rights reserved December 1997
;
; Code made for "the propeller clock"
; Copyright Bob Blick all rights reserved February 1997
;
;
; This code give the propeller 3 modes
;
; Analog clock mode by Don Zehnder
: Digital mode by Bob Blick
; Scrolling text mode by Don Zehnder
;
; mode 0:     digital
; mode 1:    analog
; mode 2:    message
;
; Assignments on portb. Say, here's a dumb way to hook things up!
; BIT    LED (from center=1 to edge=7)
; 0    5
; 1    1
; 2    2
; 3    3
; 4    rotation index input
; 5    4
; 6    7
; 7    6
; hours bit 1,2,3,5
; min bit 1,2,3,5,0,7
; seconds bit 6
; time set porta,4
; ~10 MHz crystal
;----------------------------------------------------------------------




    list    p=16f84
    include "p16f84.inc"
    __FUSES _CP_OFF & _WDT_ON & _HS_OSC & _PWRTE_OFF

    cblock    0x0c
;----unique to analog---       
      DIVISOR1    ;these are to count # of interrupts
      DIVISOR2    ;required to increment ZSEC, decrement DSEC
      DSEC        ;There are 256 DSECs in a minute (1 rev)
      ZSEC        ;When this rolls over, decrement DMIN
      DMIN        ;There are 256 DMINs in an hour (1 rev)
      ZMIN        ;When this rolls over, decrement DHOUR
      DHOUR        ;There are 256 DHOURs in 12 hours (1 rev)
      mulcnd           ; 8 bit multiplicand
      H_byte           ; High byte of the 16 bit result
      L_byte           ; Low byte of the 16 bit result - (unused result)
      TEMP        ; part of routine for setting hands when period=0
  
;----unique to digital---
          dot_index       ;which column is being displayed
          digit_index     ;which digit is being displayed
          hours           ;in display format, not hex(01-12)
          minutes         ;00 to 59
          bigtick_dbl     ;incremented each interrupt
          bigtick_hi
          bigtick_lo
          scratch         ;scratch value
          tick            ;used by delay
      COUNTER2      ;used as secondary counter to slow time set
      ADJUST      ;fine adjust on timing

;---unique to message--------------
      CT1        ;incremented each interrupt
      CT2        ;incremented each interrupt when CT1 rolls-over
      CNT        ;counter for display of columns in message
      MAX        ;
      OFFSET     ;counter for offsetting display. Scrolls by incrementing this
     
;----common--------------
      COUNTER    ;used to slow time set 
            PERIOD_CNT    ;incremented each interrupt
      PERIOD     ;stable period after hysteresis calc.
       safe_w    ;copy of W safe from interrupt
      safe_s    ;copy of STATUS safe from interrupt
      period_dup    ;copy of period_count safe from interrupt
      flags         ;b2=int, b1=minute, b0=prev_flag, b3=mode_bit,
b4=toggle
      mode       
    endc

;----ANALOG-----
#define divisor1_val      d'147'        ;roll-over at 109 (256-109)=147
#define divisor2_val      d'235'        ;roll-over at 21
#define zsec_val    d'196'        ;min hand moves 1/60 fast as sec hand
#define zmin_val    d'244'        ;hour hand moves 1/12 fast as min hand
#define offset_val    d'57'        ;offset is n/256 rev for analog.
                    ;needed for proper display position
                    ;since we want analog noon to be where
                    ;the digital colon is. Determined empirically.
#define nths_adj_div2    d'2'        ;these are adjusted to correct
#define nths_adj_zsec    d'8'        ;for observed crystal error in analog mode
#define    nths_adj_zmin    d'1'        ;time is kept similarly to AN590

;-----COMMON-------
#define    in_bit        PORTB,4        ;using phototransistor connect to ground
#define    time_set    PORTA,4        ;Hall-effect, externally pulled-up
#define prev_flag    flags,0
#define mode_bit    flags,3        ;1=analog, default=digital

;-----MESSAGE------
#define MAX_VAL        d'194'        ;# cols in message minus 4 (-4? why? it works!)
#define CNT_VAL        d'30'        ;# cols displayed in message at any time
#define CT2_VAL        d'253'        ;scroll rate: smaller=slower

    org    0x00        
    goto    Start

;---INTERRUPT ROUTINE--------------------------
    org     0x04            ;interrupt vector
Int      movwf   safe_w          ;save w
       swapf   STATUS,w        ;swap status, w
    movwf   safe_s          ;save status(nibble swap, remember)
   
test_mode           
    movf    mode,f
    btfsc    STATUS,Z
    goto    digital        ;mode=0
   
    movlw    d'1'
    xorwf    mode,w
    btfsc    STATUS,Z
    goto    analog        ;if mode = 1

    goto    message        ;else mode = 2
   
;--------------------------------
;ANALOG---main time keeping code
;--------------------------------
analog    incfsz    DIVISOR1,1
    goto    exit
    movlw    divisor1_val    ;reset DIVISOR1 every time it rolls over
    movwf    DIVISOR1       
    incfsz    DIVISOR2,1
    goto     exit
    movlw    divisor2_val    ;reset DIVISOR2 each time it rolls
    movwf    DIVISOR2
    movlw    nths_adj_div2    ;adj-roll later (plus)
    subwf    DIVISOR1,1   
    decf    DSEC,1        ;decrement: reverses direction of display
    incfsz    ZSEC,1
    goto    exit
    movlw    zsec_val    ;reset ZSEC when it rolls
    movwf    ZSEC
    movlw    nths_adj_zsec    ;earlier  (minus)
    addwf    DIVISOR1,1   
    decf    DMIN,1
    incfsz    ZMIN,1
    goto    exit
    movlw    zmin_val    ;reset ZMIN when it rolls
    movwf    ZMIN
    movlw    nths_adj_zmin
    subwf    DIVISOR1,1    ;later (plus)   
    decf    DHOUR,1
exit       

;start setting hands on the clock based on time in the registers   
;seconds--------

    movlw    b'11111111'    ;make all outputs high
    movwf    PORTB        ;to turn off all the LEDs

    movf    PERIOD,W    ;some junk is here to fix problem
    movwf    TEMP        ;where hands turn off when PERIOD_CNT is 0
    movlw    d'2'
    subwf    TEMP,1        ;TEMP=PERIOD-2

    movf    DSEC,W
    movwf    mulcnd        
    call    mpy_F        ;do the math->PERIOD x time

    movf    H_byte,W    ;if H_byte>PERIOD-2 and
    subwf    TEMP,W       
    btfsc    STATUS,C
    goto    ng
    movf    PERIOD_CNT,1    ;if PERIOD_CNT=0 turn on LED
    btfsc    STATUS,Z
    goto    sec_on
ng    movf    PERIOD_CNT,W    ;divide result by 256 then compare to PERIOD_CNT
    xorwf    H_byte,W   
    btfsc    STATUS,Z   

sec_on    bcf    PORTB,6    

;minutes-------------------------

    movf    DMIN,W
    movwf    mulcnd       
    call    mpy_F   

    movf    H_byte,W    ;if H_byte>PERIOD-2 and
    subwf    TEMP,W       
    btfsc    STATUS,C
    goto    ngm
    movf    PERIOD_CNT,1    ;if PERIOD_CNT=0 turn on LEDs
    btfsc    STATUS,Z
    goto    min_on

ngm    movf    PERIOD_CNT,W
    xorwf    H_byte,W   

    btfss    STATUS,Z    ;defacto divide by 256 then compare to PERIOD_CNT
    goto    skipm

min_on    bcf    PORTB,0        ;light up the minute hand
    bcf    PORTB,5
    bcf    PORTB,7
    bcf    PORTB,1
    bcf    PORTB,2
    bcf    PORTB,3
skipm                ;skipped to here means no turn on
;hours-------------------------

    movf    DHOUR,W
    movwf    mulcnd       
    call    mpy_F   

    movf    H_byte,W    ;if H_byte>PERIOD-2 and
    subwf    TEMP,W        ;if PERIOD_CNT=0 turn on
    btfsc    STATUS,C
    goto    ngh
    movf    PERIOD_CNT,1
    btfsc    STATUS,Z
    goto    hour_on
   
ngh    movf    PERIOD_CNT,W
    xorwf    H_byte,W   

    btfss    STATUS,Z    ;defacto divide by 256 then compare to PERIOD_CNT
    goto    skiph

hour_on    bcf    PORTB,1
    bcf    PORTB,2
    bcf    PORTB,3
    bcf    PORTB,5

skiph    goto    end_int
;-----------------------------
;--DIGITAL--------------------
;-----------------------------
digital      
; ?????????? interrupts every minute. Increment the bigtick each time.
                incf    bigtick_lo,f
                btfsc   STATUS,Z
                incf    bigtick_hi,f
                btfsc   STATUS,Z
                incfsz  bigtick_dbl,f
                goto    Bigtick_out
;--------
; here? bigtick has rolled over to zero and one minute has passed.
; reload bigtick and set a flag for the main counter
;--------
        movlw   0xf7            ;these timings for no prescale
                movwf   bigtick_dbl     ;and 10Mhz crystal
                movlw   0x0c        ;corrected for observed error
                movwf   bigtick_hi   
                movlw   0xf8       
                movwf   bigtick_lo
                bsf     flags,1         ;notify Keep_time

        incfsz    ADJUST        ;make correction every 256 minutes
        goto    Bigtick_out
        movlw    d'243'
        subwf    bigtick_lo,f
Bigtick_out    goto    end_int

;--------------------
;---message----------
;--------------------   
message        incfsz    CT1,1
        goto    end_int
        incfsz    CT2,1
        goto    end_int
        movlw    CT2_VAL        ;these counters cause action
        movwf    CT2        ;to occur after set # ints
                    ;controls scroll rate

        incf    OFFSET,1    ;OFFSET is the counter that
                    ;controls scrolling-indexes
                    ;through message
        movf    MAX,w
        xorwf    OFFSET,w   
        btfss    STATUS,Z    ;if OFFSET=MAX
        goto    end_int        ;clear OFFSET
        clrf    OFFSET        ;now at end of message, so reset to beginning
        goto    end_int
   
;restore and return--
end_int        btfsc    flags,4        ;increment PERIOD_CNT only every other
        goto    clear_flag    ;interrupt. Since hand position can only
        incf    PERIOD_CNT,1    ;be determined to the nearest interrupt,
        bsf    flags,4        ;this produces less hand jitter than using
        goto    go_there    ;tmr0 prescale of 2x
clear_flag
        bcf    flags,4        ;flags,4 is toggled each interrupt
go_there       
        clrwdt   
                swapf   safe_s,w        ;fetch status, reswap nibbles
                movwf   STATUS          ;restore status
                swapf   safe_w,f        ;swap nibbles in preparation
                swapf   safe_w,w        ;for the swap restoration of w
                bcf     INTCON,2        ;clear interrupt flag before
return
                retfie                  ;return from interrupt

;*******************************************************************
;Time, whether sec, min, or hour is kept as a value between 0 to 255.  
;To scale the time in order to compare to the current value of
PERIOD_CNT,
;multiply time*PERIOD and divide by 256, which is just the high
;byte of time*PERIOD result
;          ( Fast Version : Straight Line Code )
; Before calling the subroutine " mpy_F ", the multiplier is PERIOD
; multiplicand is " mulcnd " . The 16 bit result is stored in locations
; H_byte & L_byte.     
;*******************************************************************
;****   Define a macro for adding & right shifting  **

mult    MACRO   bit             ; Begin macro
    btfsc   PERIOD,bit
    addwf   H_byte,1
    rrf     H_byte,1
    rrf     L_byte,1
    ENDM                    ; End of macro

; *****************************         Begin Multiplier Routine
mpy_F   clrf    H_byte
    clrf    L_byte
    movf    mulcnd,W        ; move the multiplicand to W reg.
    bcf     STATUS,C        ; Clear the carry bit in the status Reg.
    mult    0
    mult    1
    mult    2
    mult    3
    mult    4
    mult    5
    mult    6
    mult    7
    retlw   0

;--initialize all RAM-digital and analog---------------------
;
Ram_init    ;***digital*****       
                movlw   0x12            ;why do clocks always start
                movwf   hours           ;at 12:00 ?
                clrf    minutes
                clrf    dot_index
                clrf    digit_index
                movlw   0xFB
                movwf   bigtick_dbl
        movlw    d'5'
        movwf    COUNTER2

        ;****analog*******
        movlw    divisor1_val    ;initialize DIVISOR1
        movwf    DIVISOR1
        movlw    divisor2_val    ;initialize DIVISOR2
        movwf    DIVISOR2
        movlw    zsec_val    ;and other timekeeping
        movwf    ZSEC        ;counters
        movlw    zmin_val
        movwf    ZMIN
        movlw    offset_val    ;initialize to start all hands at 12:00
        movwf    DHOUR
        movwf    DSEC
        movwf    DMIN
       
        ;******message******
        movlw    d'245'    ;control scroll speed- smaller=slower
        movwf    CT2
        movlw    MAX_VAL    ;must not be greater than # lines in
        movwf    MAX    ;message table
        clrf    OFFSET
        clrf    CNT

        ;****common*****
        clrf    mode

                  retlw   0
;-----------------------------------
Port_init       movlw   b'00010000'     ;all but bit 4 are output
                tris    PORTB           ;portb 4 is index sensor
                movlw   b'00010000'     ;porta 4 is timeset
                tris    PORTA          
                retlw   0
;-----------------------------------
Timer_init      bcf     INTCON,2        ;clear TMR0 int flag
              bsf     INTCON,7        ;enable global interrupts
                bsf     INTCON,5        ;enable TMR0 int
                clrf    TMR0            ;clear timer
                clrwdt                  ;why is this needed? just do
it..
                movlw   b'01011000'       ;set up timer. no prescale
                    ;portb pullups enabled
                option                  ;send w to option. generate
warning.
                clrf    TMR0            ;start timer
        retlw   0

;------------------------------------
    org     100    
;---------------------------------------
;Check_index-  common to all modes
;        Bob Blick's original code also works. This method
;        stabilizes period faster.
;--------------------------------------
Check_index    bcf    flags,5        ;bit 5 is for message
        btfss    in_bit                   
        goto    no_phase
        btfsc    prev_flag
        goto    cont
        bsf    prev_flag
        movf    PERIOD_CNT,w    ;make a working copy
        movwf    period_dup    ;called period dup
        clrf    PERIOD_CNT    ;a fresh start for next rotation
        clrf    digit_index     ;set to first digit
                clrf    dot_index       ;first column
        bsf    flags,5
        bcf    flags,4        ;clear period count toggle bit. PERIOD_CNT
        goto    cont        ;will be incremented on the next interrupt
no_phase    bcf    prev_flag
; calculate a period that does not dither or jitter
; period will not be changed unless new period is really different
cont        movf    PERIOD,W
        subwf    period_dup,W    ;find difference
        btfss    STATUS,C    ;carry flag set means no borrow
        goto    Calc_neg    ;must be other way
        sublw    2        ;allowable deviation = 3
        btfss    STATUS,C    ;borrow won't skip
        incf    PERIOD        ;new value much larger than calc
        retlw    0
Calc_neg    addlw    2        ;allowable deviation = 3
        btfss    STATUS,C    ;carry will skip
        decf    PERIOD        ;no carry means it must be change
        retlw    0
;-------------------       
;set_analog  for adjusting time
;-------------------
Set_analog
    btfsc    time_set    ;input uses pull-up- normally set
    retlw    0        ;exit if no magnet near

    decfsz    COUNTER,1    ;COUNTER used to take action only every 256 loops
    retlw    0        ;this slows down time setting
   
    movlw    offset_val    ;reset seconds hand to 12:00 position
    movwf    DSEC        ;to make it pretty

    decf    DMIN,1        ;adjust time one minute
    incfsz    ZMIN,1            ;this one goes the other way
    retlw    0            ;if ZMIN rolls, reset it and decrement DHOUR
    movlw    zmin_val
    movwf    ZMIN
    decf    DHOUR,1       
    retlw    0

;****************************************
;---Digital Code--subs below here
;****************************************
;--------
; CHARACTER LOOKUP TABLE
; ignore bit 4. set=LED off, clear=LED on
;-flipped digits upside down-pattern modified for my crazy LED wiring

Char_tbl
       addwf   PCL,f
    dt    0x42    ,0xAD    ,0xAD    ,0xAD    ,0x42
    dt    0xEF    ,0xED    ,0x0    ,0x6D    ,0xEF
    dt    0x6C    ,0x8D    ,0xA5    ,0xA9    ,0x6D
    dt    0xA3    ,0xD    ,0xAC    ,0xAD    ,0xAB
    dt    0xE7    ,0x0    ,0x67    ,0xE6    ,0xC7
    dt    0x83    ,0xAC    ,0xAC    ,0xAC    ,0x2A
    dt    0xE3    ,0x8D    ,0x8D    ,0x4D    ,0xC2
    dt    0x2F    ,0xAE    ,0x8F    ,0xA1    ,0xAF
    dt    0x62    ,0x8D    ,0x8D    ,0x8D    ,0x62
    dt    0x46    ,0x8B    ,0x8D    ,0x8D    ,0x6E
    dt    0xEF    ,0xEF    ,0x62    ,0xEF    ,0xEF
Char_tbl_end

;--------
; change LED pattern based on state of digit_index and dot_index
;--------
Display_now     movlw   0x05
                xorwf   dot_index,w     ;test for end of digit
                movlw   0xFF            ;pattern for blank column
                btfsc   STATUS,Z
                goto    D_lookup_3      ;it needs a blank
                bcf     STATUS,C        ;clear carry before a rotate
                rlf     digit_index,w   ;double the index because each
       
        bcf    PCLATH,1
        bsf    PCLATH,0

                addwf   PCL,f           ;takes two instructions

D_1min          movf    minutes,w    ;DZ-reversed order of display
                goto    D_lookup    ;since my motor is counterclockwise
D_10min         swapf   minutes,w
                goto    D_lookup
D_colon         movlw   0x0A
                goto    D_lookup
D_1hr           movf    hours,w
                goto    D_lookup       
D_10hr          swapf   hours,w
                goto    D_lookup       
D_nothing       retlw   0

D_lookup        andlw   b'00001111'     ;strip off hi bits
                movwf   scratch         ;multiply this by 5 for lookup
                addwf   scratch,f       ;table base position
                addwf   scratch,f       ;is this cheating?
                addwf   scratch,f       ;I think not.
                addwf   scratch,f       ;I think it is conserving
energy!
                btfss   STATUS,Z        ;test for zero
                goto    D_lookup_2      ;not a zero
        movlw    0x04           ;-DZ changes- 10 hours is now digit 5
        xorwf    digit_index,w  ;-if digit 5 is 0, display blank
        movlw   0xFF            ;this makes a blank LED pattern
                btfsc   STATUS,Z        ;test if it is 10 hrs digit
                goto    D_lookup_3      ;it's a trailing zero
D_lookup_2      movf    dot_index,w     ;get column
                addwf   scratch,w       ;add it to digit base

        bcf    PCLATH,1    ;set to page 0x100 for lookup
        bsf    PCLATH,0   
                call    Char_tbl        ;get the dot pattern for this
column
D_lookup_3      movwf   PORTB           ;send it to the LEDs
        movf    PERIOD,w       ;made delay longer than original program
                call    Delay           ;width of digits with this delay
                incf    dot_index,f     ;increment to the next column
                movlw   0x06            ;6 columns is a digit plus space
                xorwf   dot_index,w     ;next digit test
                btfss   STATUS,Z
                retlw   0               ;not a new digit
                clrf    dot_index       ;new digit time
                incf    digit_index,f
                retlw   0               ;Display_now done.
;--------
; a short delay routine
;--------
Delay           movwf   tick
Delay_loop         nop                   ;added NOPs to increase delay
        nop            ;to make really wide display
        nop
        nop
        decfsz  tick,f
                goto    Delay_loop      ;w is not damaged, so Delay can
                return                  ;be recalled without reloading
;--------
; test for magnet and adjust time if needed
;--------   
Set_digital     btfsc    time_set    ;input uses pull-up- normally set
        retlw    0        ;exit if no magnet near
        decfsz    COUNTER,1    ;COUNTER used to take action only every 256 loops
        retlw    0        ;this slows down time setting
        decfsz    COUNTER2,1    ;COUNTER2 slows things even more
        retlw    0       
        movlw    d'8'        ;adjust to suit for setting speed
        movwf    COUNTER2    ;reset COUNTER2 when it rolls
        call    Inc_mins    ;got to here, now adjust time
        retlw    0
;--------
; increment one hour
;--------
Inc_hours       movlw   0x12
                xorwf   hours,w
                btfsc   STATUS,Z
                goto    Inc_hours_12
                movlw   0x07            ;this part gets a little sloppy
                addwf   hours,w
                movlw   0x07
                btfss   STATUS,DC
                movlw   1
                addwf   hours,f
                retlw   0
Inc_hours_12    movlw   0x01
                movwf   hours
                retlw   0
;--------
; increment the time based on flags,1 as sent by interrupt routine
; Inc_mins also used by time setting routine
;--------
Keep_time       btfss   flags,1         ;the minutes flag
                retlw   0               ;not this time
                bcf     flags,1         ;clear the minutes flag
Inc_mins        movlw   0x07            ;start incrementing time
                addwf   minutes,w       ;add 7 minutes into w
                btfsc   STATUS,DC       ;did adding 7 cause digit carry?
                goto    Sixty_mins      ;then test for an hour change
                incf    minutes         ;otherwise add 1 for real
                retlw   0               ;and go back
Sixty_mins      movwf   minutes         ;save the minutes
                movlw   0x60            ;test for 60
                xorwf   minutes,w       ;are minutes at 60?
                btfss   STATUS,Z
                retlw   0               ;no? go back
                clrf    minutes         ;otherwise zero minutes
                goto    Inc_hours       ;and increment hours

;--------------------
;message stuff
;
;bit mapped data table generated using BASIC program
;--------------------
    org    0x200
mess_data
    addwf    PCL,f
    dt    0xEF    ;blank
    dt    0xEF    ;blank
    dt    0xEF    ;blank

    dt   0xEB    ;image of christmas tree    13 columns
    dt   0xE3
    dt   0xC3   
    dt   0xC2
    dt   0x40
    dt   0x00
    dt   0x40
    dt   0xC2
    dt   0xC3
    dt   0xE3
    dt   0xEB
    dt   0xEF
    dt   0xEF

;"merry christmas from don pam and philip"
;182 columns
    dt    0x00,0x6F,0xEE,0x6F,0x00,0xEF
    dt    0x00,0x8D,0x8D,0xAD,0xEF
    dt    0x00,0x87,0x8B,0x6C,0xEF
    dt    0x00,0x87,0x8B,0x6C,0xEF
    dt    0x2F,0xEE,0xC1,0xEE,0x2F,0xEF
    dt    0xEF
    dt    0x42,0xAD,0xAD,0x6B,0xEF
    dt    0x00,0xCF,0xCF,0xCF,0x00,0xEF
    dt    0x00,0x87,0x8B,0x6C,0xEF
    dt    0xAD,0x00,0xAD,0xEF
    dt    0x6A,0x8D,0x8D,0x63,0xEF
    dt    0xAF,0xAF,0x00,0xAF,0xAF,0xEF
    dt    0x00,0x6F,0xEE,0x6F,0x00,0xEF
    dt    0x40,0x8F,0x8F,0x40,0xEF
    dt    0x6A,0x8D,0x8D,0x63,0xEF
    dt    0xEF
    dt    0x00,0x8F,0x8F,0xAF,0xEF
    dt    0x00,0x87,0x8B,0x6C,0xEF
    dt    0x42,0xAD,0xAD,0x42,0xEF
    dt    0x00,0x6F,0xEE,0x6F,0x00,0xEF
    dt    0xEF
    dt    0x00,0xAD,0xAD,0x42,0xEF
    dt    0x42,0xAD,0xAD,0x42,0xEF
    dt    0x00,0x6F,0xCE,0xE7,0x00,0xEF
    dt    0xEF
    dt    0xEF
    dt    0x00,0x8F,0x8F,0x6E,0xEF
    dt    0x40,0x8F,0x8F,0x40,0xEF
    dt    0x00,0x6F,0xEE,0x6F,0x00,0xEF
    dt    0xEF
    dt    0x40,0x8F,0x8F,0x40,0xEF
    dt    0x00,0x6F,0xCE,0xE7,0x00,0xEF
    dt    0x00,0xAD,0xAD,0x42,0xEF
    dt    0xEF
    dt    0x00,0x8F,0x8F,0x6E,0xEF
    dt    0x00,0xCF,0xCF,0xCF,0x00,0xEF
    dt    0xAD,0x00,0xAD,0xEF
    dt    0x00,0xED,0xED,0xED,0xEF
    dt    0xAD,0x00,0xAD,0xEF
    dt    0x00,0x8F,0x8F,0x6E,0xEF
;could add blanks to either end
   
;--------------   
Message

top    call    Check_index
    btfss    flags,5
    goto    top        ;wait until index occurs
   
loops    movlw    0xFF   
    movwf    PORTB        ;turn everything off
   
    movlw    CNT_VAL        ;if CNT==CNT_VAL, clear CNT and goto top
    xorwf    CNT,w        ;CNT_VAL is # cols displayed at any time
    btfss    STATUS,Z   
    goto    cont_message    ;else continue and display next column
    clrf    CNT
    goto    top

cont_message
    movf    OFFSET,w        ;if offset>cnt
    subwf    CNT,w           
    btfss    STATUS,C
    goto    no_wrap            ;goto no_wrap
    goto    wrap            ;else wrap
   
no_wrap    movf    CNT,w
    subwf    OFFSET,w        ;W=OFFSET-CNT
    goto    look           
       
wrap    subwf    MAX,w            ;w=MAX-(CNT-OFFSET)

look    bcf    PCLATH,0        ;lookup at page 0x200
    bsf    PCLATH,1
    call    mess_data        ;lookup bit pattern in table
    movwf    PORTB            ;and send to LEDs
    movlw    d'100'             
    call    Delay            ;leave "ON" for a while
    call    Delay
    incf    CNT,f            ;get next column to display
    goto    loops

    retlw    0        ;can't ever get here- endless loop

;--------------------
; Program starts here
;--------------------
Start                call    Ram_init        ;set variables to nice values
                call    Port_init       ;set port directions   

read_ee        bcf    STATUS,RP0    ;EEPROM read from
        movlw    0x00        ;first location
        movwf    EEADR
        bsf    STATUS,RP0
        bsf    EECON1,RD
        bcf    STATUS,RP0

test_m        movf    EEDATA,f    ;read EEPROM and test mode
        btfsc    STATUS,Z
        goto    zero        ;mode=0   
        movlw    d'1'
        xorwf    EEDATA,w
        btfsc    STATUS,Z
        goto    one        ;mode = 1
        goto    two        ;else mode = 2

zero        movf    EEDATA,w
        movwf    mode        ;set mode to 0
        movlw    0x01        ;write 1 for next time
        call    write_ee
        goto    run_dig        ;start machine in digital mode

one        movf    EEDATA,w
        movwf    mode
        movlw    0x02
        call    write_ee
        goto    run_ana        ;start machine in analog mode

two        movf    EEDATA,w
        movwf    mode
        movlw    0x00
        call    write_ee
        goto    run_mess    ;start machine in message mode

;---write_ee-------------
write_ee    movwf    EEDATA
        movlw    0x00        ;
        movwf    EEADR        ;
        bsf    STATUS,RP0    ;bank 1
        bcf    INTCON,GIE    ;don't need
        bsf    EECON1,WREN    ;write enable
        movlw    0x55        ;mchip required section
        movwf    EECON2        ;""
        movlw    0xAA        ;""
        movwf    EECON2        ;""
        bsf    EECON1,WR    ;""
        bsf    INTCON,GIE    ;don't need
write_wait    btfsc    EECON1,WR    ;wait for finish writing
        goto    write_wait
        bcf    STATUS,RP0    ;back to bank 0

        call    Timer_init      ;start timer based interrupt

        retlw    0
;--------------

run_ana
Analog_circle    call    Check_index       
        call    Set_analog
        goto    Analog_circle

run_dig
Digital_circle  call    Check_index
                call    Display_now
                call    Set_digital
                call    Keep_time
                goto    Digital_circle

run_mess    call     Message

                end