 title  "Universal timer"
;
;
;
;
; This program makes use of the built-in AD converter of the
; PIC16F873.
; The program implements a universal timer to switch electrical applicances
; on and off, like you can buy in a supermarket for a few euros these
; days. So why built one myself? Because this timer provides features that
; are usually not found on these commercial devices. These features are:
; light and dark events with programmable sensitivity, random switching
; with programmable duty cycle, combination of these with the
; normal time switching, all programmable data stored in EEPROM so the
; backup battery found in commercial devices and which fails after
; a few years anyway is not needed.
; The only drawback compared with commercial devices is that after a
; power failure the clock must be set again (only the clock, not the
; programmed timer settings). I will later try to solve this with
; a DCF radio clock receiver added, so a battery is still not needed!
;
; There is one sensor, an LDR resistance, which is connected to port
; A0 in a voltage-divider configuration (see schematic diagram). This
; is to measure the light in an analog way.
; There are three LEDS connected to port A2, A3 and A4 (the last one
; is open collector, so the LED comes from +Vdd). Colors of these LEDs
; are (in my setup) respectively yellow, blue and red.
; The relay is connected to port A5 (via an NPN transistor).
;
; All buttons (4 pieces) are connected to PORTB.
;
; The LCD display (1 x 16 characters) is connected to PORTC.
;
; The code is designed to run only with a 4 Mhz chrystal.
;
;
;
; (C) Geert Van Espen 2002
;
;
#define NODEBUG

  LIST P=16F873, R=DEC          ;  16F873 Runs at 4 MHz
  errorlevel 0, -302, -307
  INCLUDE "p16f873.inc"

;**********************************************************************
;*                                                                    *
;*     Variables for bank 0                                           *
;*                                                                    *
;**********************************************************************
;  Register Usage
 CBLOCK 0x020                   ; Start Registers at End of the Values.

Dlay                            ; 8 Bit Delay Variable.
DlayTemp                        ;
Temp:3                          ; Temporary Value Used When Sending Out Data.
NOTemp                          ; Temporary Value to "NybbleOutput".
keys                            ; Contains pressed key(s). Bit0 = button0 etc.
offset                          ; Used for dealing with page switching.


;***********************************************************************
; variables for the application

; parameters


; local variables
dispZone:16                     ; String of 16 chars to be output to LCD.
mode                            ; 0=time 1..14=prog 15,16=lightprog 17=set.
timeSetting                     ; 0 or 1
cursor                          ; 0..15
relay                           ; 0=off 1=on 2=random.
prevRelay                       ; Previous value of relay.
snooze                          ; 0..9

progDataH                       ; Value for hours.
progDataM                       ; Value for minutes.
progDataD                       ; Value for day of week.
progDataA                       ; Value for action.

hourVar                         ; Variable to scroll hours.
minuteVar                       ; Variable to scroll minutes.
dayVar                          ; Variable to scroll days
                                ; 0=Monday, 1=Tuesday ... 6=Sunday H'FF' = any day.
actionVar                       ; Variable to scroll action.
                                ; 0=relay off 1=relay on 2=relay random 3=no action
dutyVar                         ; 10 .. 90.

light                           ; 0=donker 1=licht.
prevLight                       ; Previous value of light.
cuLight                         ; 0..9 current light measurement.
Hours1516                       ; Number of hours left for the light programs.
yellowLED                       ; 0=off 1=on.
atLeastOne                      ; These are the 'at least one' flags:
                                ; bit0: 0 = no active timer program
                                ;       1 = at least one active timer program
                                ; bit1: 0 = no active light program
                                ;       1 = at least one active light program
bKbdControlFlags                ; bit 0: 0=not busy 1=keyboard busy processing.
                                ; bit 1-4: time since start of hold in nr of 1/4 secs.
                                ; bit 7: key has been hold long enough.
MinutesCounter                  ; Seperate minutes counter for the light programs.

;***********************************************************************
; variables for randomizer

; parameters
ranl                            ; 8-bit random number.

; local variables
dutyOn                          ; Duty cycle for on-time.
dutyOff                         ; Duty cycle for off time (= 100 - dutyOn).
randomSwitch                    ; 0=random off 1=random on.
ranOffTime                      ; Random off-time teller.
ranOnTime                       ; Random on-time teller.

;***********************************************************************
; variables for timer service

; parameters
Seconds                         ; Seconds of the clock.
Minutes                         ; Minutes of the clock.
Hours                           ; Hours of the clock.
Days                            ; Days of the clock
                                ; 0=Monday, 1=Tuesday ... 6=Sunday.

bShowFlags                      ; bit0 = 4 secs
                                ; bit1 = 2 secs
                                ; bit2 = 1 sec
                                ; bit3 = 0.5 secs
                                ; bit4 = 30 secs
                                ; bit5 = 0.25 secs
                                ; bit7 = 1 minute
bEepromBoost                    ; 0 = no boost needed.
                                ; 1 = boost clock the next second.

; local variables
TimerL                          ; 16-bit low level counter for the clock.
TimerH                          ;

;***********************************************************************
; variables for ByteToDec and AD2Voltage

; parameters
Asc0                            ;
Asc1                            ;
Asc2                            ;
Asc3                            ;

; local variables               ;
TempByte                        ;

;***********************************************************************
; variables for Div4823, Mul1616 and Mul2424

; parameters
Divisor:3                       ; msb first
Dividend:6                      ; msb first

Product:6                       ; msb first
Product32:4                     ; msb first
Multipland:3                    ; msb first


; local variables
BitCount                        ;


 ENDC


Multiplier EQU Product + 3      ; 3 bytes shared with Product's
                                ; less significant bytes (+3..5).



;**********************************************************************
;*                                                                    *
;*     Variables for context saving in interrupt handlers, both in    *
;*     bank 0 and 1.                                                  *
;*                                                                    *
;**********************************************************************
 CBLOCK 0x07D                   ; Start Registers at last piece.

;***********************************************************************
; variables for context saving during interrupt service
status_safe                     ;
pclath_safe                     ;
w_safe                          ;

 ENDC




;**********************************************************************
;*                                                                    *
;*     Variables for bank 1                                           *
;*                                                                    *
;**********************************************************************
;  Register Usage
 CBLOCK 0x0A0                   ;

Hours01                         ;
Minutes01                       ;
Day01                           ;
Action01                        ;

Hours02                         ;
Minutes02                       ;
Day02                           ;
Action02                        ;

Hours03                         ;
Minutes03                       ;
Day03                           ;
Action03                        ;

Hours04                         ;
Minutes04                       ;
Day04                           ;
Action04                        ;

Hours05                         ;
Minutes05                       ;
Day05                           ;
Action05                        ;

Hours06                         ;
Minutes06                       ;
Day06                           ;
Action06                        ;

Hours07                         ;
Minutes07                       ;
Day07                           ;
Action07                        ;

Hours08                         ;
Minutes08                       ;
Day08                           ;
Action08                        ;

Hours09                         ;
Minutes09                       ;
Day09                           ;
Action09                        ;

Hours10                         ;
Minutes10                       ;
Day10                           ;
Action10                        ;

Hours11                         ;
Minutes11                       ;
Day11                           ;
Action11                        ;

Hours12                         ;
Minutes12                       ;
Day12                           ;
Action12                        ;

Hours13                         ;
Minutes13                       ;
Day13                           ;
Action13                        ;

Hours14                         ;
Minutes14                       ;
Day14                           ;
Action14                        ;

OnDarkH                         ;
dummy1                          ;
dummy2                          ;
OnDarkA                         ;

OnLightH                        ;
dummy3                          ;
dummy4                          ;
OnLightA                        ;

Duty                            ;
dummy5                          ;
dummy6                          ;
Sensi                           ;



 ENDC


;**********************************************************************
;*                                                                    *
;*     Definitions                                                    *
;*                                                                    *
;**********************************************************************
#DEFINE LCD     PORTC           ;
#DEFINE E       2               ;
#DEFINE RS      3               ;

z       equ     2               ;
RBPU    equ     7               ;

#DEFINE KLOK    0x43            ;
#DEFINE MYBIRTHYEAR D'61'       ;



;**********************************************************************
;*                                                                    *
;*     Macros                                                         *
;*                                                                    *
;**********************************************************************

EStrobe MACRO                   ;  Strobe the "E" Bit
  bsf    LCD, E
  bcf    LCD, E
 ENDM

fcall   macro subroutine_name
        local here
        lcall subroutine_name
        pagesel here
here:
        endm


bank0 macro
  bcf   STATUS, RP0             ;
  bcf   STATUS, RP1             ; Bank 0.
  endm

bank1 macro
  bsf   STATUS, RP0             ;
  bcf   STATUS, RP1             ; Bank 1.
  endm

bank2 macro
  bsf   STATUS, RP1             ;
  bcf   STATUS, RP0             ; Bank 2.
  endm

magic   macro                   ; magic EEPROM write sequence
  movlw   55H                   ;
  movwf   EECON2^80H            ;
  movlw   0AAH                  ;
  movwf   EECON2^80H            ;
  endm







 PAGE
 __CONFIG _CP_OFF & _XT_OSC & _PWRTE_ON  & _WDT_OFF & _LVP_OFF
                                ;  Note that the WatchDog Timer is OFF
;

;
  org    0                      ;
  goto   init                   ;
;

  org    4                      ;
;**********************************************************************
;*                                                                    *
;*     01  Interrupts                  parent = xx  none              *
;*                                                                    *
;**********************************************************************
;
  movwf  w_safe                 ; Save context.
  swapf  STATUS, w              ;
  clrf   STATUS                 ;
  movwf  status_safe            ;
  movf   PCLATH, w              ;
  movwf  pclath_safe            ;
  clrf   PCLATH                 ; Jump to page 0.

  btfsc  INTCON, T0IF           ; Timer int?
  goto   TimerService           ; Yes, then service.

; we are now on INT interrupt
  bcf    INTCON, INTE           ; Clear int mask.
  bcf    INTCON, INTF           ; Clear flag.
  retfie                        ; Return from int interrupt.

;**********************************************************************
;*                                                                    *
;*     TIMER interrupt                                                *
;*                                                                    *
;**********************************************************************
TimerService
; We are now on timer interrupt: indicate that we did service!
  bcf    INTCON, T0IE           ; Clear TMR0 int mask.
  bcf    INTCON, T0IF           ; Clear flag.


; The timer interrupt is mainly to advance the clock. There is a low-
; level 16 bit counter (TimerH:TimerL) that advances by 1 at every
; interrupt and that must count until
; about 3907 before one second has gone by. The clock can be fine
; tuned by playing with this value. Sometimes special tricks have
; to be done, i.e. boost TimerH:TimerL every x seconds, to get an
; exact clock.
;
  incf   TimerL, f              ; Increment 16-bit low level counter.
  btfsc  STATUS, Z              ;
  incf   TimerH, f              ;


  movlw  0xa1                   ; Test for half second break.
  subwf  TimerL, w              ;
  btfss  STATUS, Z              ;
  goto   HalfSecTest_af         ;
  movlw  0x07                   ;
  subwf  TimerH, w              ;
  btfss  STATUS, Z              ;
  goto   HalfSecTest_af         ;
  bsf    bShowFlags, 3          ; Mark 1/2 second interval.
  bsf    bShowFlags, 5          ; Mark 1/4 second interval.
HalfSecTest_af

  movlw  0xd0                   ; Test for quarter second break.
  subwf  TimerL, w              ;
  btfss  STATUS, Z              ;
  goto   QSecTest_af            ;
  movlw  3                      ;
  subwf  TimerH, w              ;
  btfss  STATUS, Z              ;
  goto   QSecTest_af            ;
  bsf    bShowFlags, 5          ; Mark 1/4 second interval.
QSecTest_af

  movlw  0x71                   ; Test for quarter second break.
  subwf  TimerL, w              ;
  btfss  STATUS, Z              ;
  goto   Q2SecTest_af           ;
  movlw  H'0B'                  ;
  subwf  TimerH, w              ;
  btfss  STATUS, Z              ;
  goto   Q2SecTest_af           ;
  bsf    bShowFlags, 5          ; Mark 1/4 second interval.
Q2SecTest_af


;
; PortA.1 is used for test purposes: you can test on this pin
; with a frequency counter, it should measure about 3.906 kiloHertz.
  bsf    PORTA, 1               ;
  nop                           ;
  nop                           ;
  nop                           ;
  nop                           ;
  nop                           ;
  nop                           ;
  nop                           ;
  nop                           ;
  nop                           ;
  nop                           ;
  bcf    PORTA, 1               ;

;
  movlw  0x44                   ;
  subwf  TimerL, w              ;
  btfss  STATUS, Z              ;
  goto   SecTest_af             ;
  movlw  0x0f                   ;
  subwf  TimerH, w              ;
  btfss  STATUS, Z              ;
  goto   SecTest_af             ;
  clrf   TimerL                 ;
  clrf   TimerH                 ;
  incf   Seconds, f             ; Another second has gone by.

; Compensate for EEPROM write: boost with 300 millisecs if an
; EEPROM write has occurred.
  btfss  bEepromBoost, 0        ;
  goto   boostAf                ;
  movlw  4                      ; Put a starting value of H'530'
  movwf  TimerH                 ; to the clock.
  movlw  H'94'                  ;
  movwf  TimerL                 ;
  bcf    bEepromBoost, 0        ; Mark boost is done.
boostAf



; Test for 30 secs intervals.
  movlw  D'30'                  ;
  subwf  Seconds, w             ;
  btfsc  STATUS, Z              ;
  bsf    bShowFlags, 4          ; Mark 30 second interval.



  bsf    bShowFlags, 2          ; Mark 1 second interval.
  bsf    bShowFlags, 3          ; Mark 1/2 second interval.
  bsf    bShowFlags, 5          ; Mark 1/4 second interval.

  btfsc  Seconds, 0             ; Are bit 0 and 1 of seconds cleared?
  goto   Correctie_af           ;
  bsf    bShowFlags, 1          ; Mark 2 second interval.
  movlw  1                      ; Boot low counter with 1.
  addwf  TimerL, f              ;
  btfsc  Seconds, 1             ;
  goto   Correctie_af           ;
  movlw  2                      ; Boot low counter with 2.
  addwf  TimerL, f              ;
  bsf    bShowFlags, 0          ; Mark 4 second interval.

Correctie_af
  movlw  D'60'                  ; Now increment the Minutes and Hours if
  subwf  Seconds, w             ; necessary.
  btfss  STATUS, Z              ;
  goto   SecTest_af             ;
  clrf   Seconds                ;
  incf   Minutes, f             ;
  bsf    bShowFlags, 7          ; Mark 1 minute interval.
  bsf    bShowFlags, 4          ; Mark 30 second interval.


  movlw  D'6'                   ;
;  movwf  TimerL                 ;
  movlw  D'60'                  ;
  subwf  Minutes, w             ;
  btfss  STATUS, Z              ;
  goto   SecTest_af             ;
  clrf   Minutes                ;
  incf   Hours, f               ;
  movlw  D'24'                  ;
  subwf  Hours, w               ;
  btfss  STATUS, Z              ;
  goto   SecTest_af             ;
  clrf   Hours                  ;
  incf   Days, f                ;
  movlw  D'7'                   ;
  subwf  Days, w                ;
  btfss  STATUS, Z              ;
  goto   SecTest_af             ;
  clrf   Days                   ;
SecTest_af


  movf   pclath_safe, w         ; Restore context.
  movwf  PCLATH                 ;
  swapf  status_safe, w         ;
  movwf  STATUS                 ;
  swapf  w_safe, f              ;
  swapf  w_safe, w              ;


  bsf    INTCON, T0IE           ; Set TMR0 int mask.

  retfie                        ; Return from timer interrupt.


;**********************************************************************
;*                                                                    *
;*     01  Program                     parent = xx  none              *
;*                                                                    *
;*     Function: main program.                                        *
;*                                                                    *
;*     params: none                                                   *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
init

  clrf   PORTA                  ; Do some initializations.
  clrf   PORTB                  ;
  clrf   PORTC                  ;

  clrf   TimerL                 ;
  clrf   TimerH                 ;
  clrf   Seconds                ;
  clrf   Minutes                ;
  clrf   Hours                  ;
  clrf   Days                   ;
  clrf   MinutesCounter         ;
  clrf   bKbdControlFlags       ;
  clrf   mode                   ;
  clrf   cursor                 ;
  clrf   relay                  ;
  clrf   prevRelay              ;
  clrf   atLeastOne             ;
  clrf   snooze                 ;
  clrf   cuLight                ;
  movlw  H'FE'                  ;
  movwf  Hours1516              ;
  clrf   keys                   ;
  clrf   bEepromBoost           ;
  clrf   progDataH              ;
  clrf   progDataM              ;
  clrf   progDataD              ;
  clrf   progDataA              ;

  clrf   hourVar                ;
  clrf   minuteVar              ;
  clrf   dayVar                 ;
  clrf   actionVar              ;
  clrf   dutyVar                ;

  clrf   light                  ;
  movlw  H'FF'                  ;
  movwf  prevLight              ;
  clrf   yellowLED              ;
  clrf   dutyOn                 ;
  clrf   dutyOff                ;
  clrf   randomSwitch           ;
  clrf   ranOnTime              ;
  clrf   ranOffTime             ;
  movlw  1                      ;
  movwf  timeSetting            ;

  movlw  A' '                   ; Move all spaces to the dispZone.
  movwf  dispZone               ;
  movwf  dispZone + 1           ;
  movwf  dispZone + 2           ;
  movwf  dispZone + 3           ;
  movwf  dispZone + 4           ;
  movwf  dispZone + 5           ;
  movwf  dispZone + 6           ;
  movwf  dispZone + 7           ;
  movwf  dispZone + 8           ;
  movwf  dispZone + 9           ;
  movwf  dispZone + 10          ;
  movwf  dispZone + 11          ;
  movwf  dispZone + 12          ;
  movwf  dispZone + 13          ;
  movwf  dispZone + 14          ;
  movwf  dispZone + 15          ;


  bsf    STATUS, RP0            ; PortA all digital.
  movlw  0xD6                   ;
  movwf  ADCON1                 ;
  bcf    STATUS, RP0            ;

  movlw  B'00000001'            ; Enable RA1..5 output, rest input.
  bsf    STATUS, RP0            ;
  movwf  TRISA ^ 0x080          ;
  bcf    STATUS, RP0            ;

  movlw  B'11111111'            ; Enable RB for input.
  bsf    STATUS, RP0            ;
  movwf  TRISB ^ 0x080          ;
  bcf    STATUS, RP0            ;

  movlw  B'00000000'            ; Enable RC for output.
  bsf    STATUS, RP0            ;
  movwf  TRISC ^ 0x080          ;
  bcf    STATUS, RP0            ;


  call   Dlay250                ; Power on self test: this is
  bcf    PORTC, 0               ; just a little flickering of the
  bsf    PORTA, 4               ; LED, just to show that the
  call   Dlay250                ; processor is up and running.
  bsf    PORTC, 0               ;
  bcf    PORTA, 4               ;
  call   Dlay250                ;
  bcf    PORTC, 0               ;
  bsf    PORTA, 4               ;
  call   Dlay250                ;
  bsf    PORTC, 0               ;
  bcf    PORTA, 4               ;
  call   Dlay250                ;
  bcf    PORTC, 0               ;
  bsf    PORTA, 4               ;
post_af



; Set the timer.
  bsf    STATUS, RP0            ; Select page 1.
#ifdef DEBUG
  movlw  B'00000000'            ; Prescaler 1:2.
#else
  movlw  B'00001000'            ; No prescaler.
#endif
  movwf  OPTION_REG             ;
  bcf    OPTION_REG, RBPU       ; Enable pull up on RB port.
  bcf    STATUS, RP0            ; Select page 0.






;  clrf   TMR0                   ;
  bsf    INTCON, GIE            ; Globally allow interrupts.
  bsf    INTCON, T0IE           ; Enable timer interrupt.


;
;
  movlw  H'F6'                  ; Initialize the randomizer.
  movwf  ranl                   ;


; Initialise settings.
  call   loadFromEeprom         ; Load settings from Eeprom.
  call   calculateDutyCycles    ; From the one duty-cycle setting,
                                ; calculate two duty cycles (one for
                                ; off and one for one) for the randomizer.
  fcall  ReadAD0                ; Read light sensor.
  call   calculateCuLight       ; Move to a 1 digit variable.
  call   evaluateLight          ;

;
;
; Initialise the LCD.
  bcf    STATUS, C              ; Clear Carry (Instruction Out).
  movlw  0x03                   ; Reset Command.
  call   NybbleOut              ; Send the Nybble.
  call	 Dlay5                  ; Wait 5 msecs before sending again.

  EStrobe                       ;
  call   Dlay160                ; Wait 160 usecs before Sending the Third Time.

  EStrobe                       ;
  call   Dlay160                ; Wait 160 usecs before Sending the Fourth Time.

  bcf    STATUS, C              ;
  movlw  0x02                   ; Set 4 Bit Mode.
  call   NybbleOut              ;
  call	 Dlay160                ;


  movlw	 0x028                  ; Note that it is a 2 Line Display.
  call   SendINS                ;
  call   Dlay160                ;

  movlw  0x008                  ; Turn off the Display.
  call   SendINS                ;
  call   Dlay160                ;

  movlw	 0x001                  ; Clear the Display RAM.
  call   SendINS                ;
  call   Dlay5                  ; Note, can take up to 4.1 msecs.

  movlw	 0x006                  ; Enable Cursor Move Direction.
  call	 SendINS                ;
  call	 Dlay160                ;

  movlw	 0x00C                  ; Turn the LCD Back On.
  call	 SendINS                ;
  call	 Dlay160                ;





  movlw  0x001                  ; Clear the Display RAM.
  call   SendINS                ;
  call   Dlay5                  ; Note, can take up to 4.1 msecs.



  clrf   FSR                    ; Output the Message.
; Send demo string to the LCD.
OutLoop
  movf   FSR, w                 ;
  movwf  offset                 ;
  incf   offset, f              ;
  movlw  LOW Message            ; Get low bits of table.
  addwf  offset, f              ; Do an 8-bit add operation.
  movlw  HIGH Message           ; Get high 5 bits of table.
  btfsc  STATUS, C              ; Page crossed?
  addlw  1                      ; Yes: then increment high address.
  movwf  PCLATH                 ; Load high address in latch.
  movf   offset, w              ; Load computed offset in w register.
  incf   FSR, f                 ;
  call   Message                ;
  iorlw  0                      ; At the End of the Message?
  btfsc  STATUS, Z              ;
  goto   OutLoop_af             ; Yes - Equal to Zero.
  call   SendCHAR               ; Output the ASCII Character.
  goto   OutLoop                ;
OutLoop_af
  goto   skippy                 ;

Message                         ; Message to Output.
  movwf  PCL                    ; Output the Characters.
  dt    "Timer", 0


skippy

; Load font.
  fcall  LoadFont               ;

randomIni
  movlw  TimerL                 ; Random seed.
  movwf  ranl                   ;

  movf   TMR0, w                ;
  addwf  ranl, f                ;




; Leave the message for a short while on the screen.
  call   Dlay250                ;
  call   Dlay250                ;
  call   Dlay250                ;
  call   Dlay250                ;
leavemessage_af

;

MAIN_LOOP
; Now the main loop is coming...
;
  movlw  0x001                  ; Clear the Display RAM.
  call   SendINS                ;
  call   Dlay5                  ; Note, can take up to 4.1 msecs.

  bcf    PORTA, 2               ; Put off LEDs and relay.
  bcf    PORTA, 3               ;
  bsf    PORTA, 4               ;
  bcf    PORTA, 5               ;

Loop                            ; Loop Forever.


  fcall  ReadAD0                ; Read light sensor.
  call   calculateCuLight       ; Move to a 1 digit variable.

  call   keyboardRead           ; Read the buttons.

  movf   keys, f                ; Test if any key is pressed.
  btfsc  STATUS, Z              ; No buttons pressed, so
  clrf   bKbdControlFlags       ; clear the busy and other flags.

  btfss  bKbdControlFlags, 0    ; Test if still processing the
                                ; previous button action.
  call   verwerkKbd             ; No, so process keyboard.

  movf   mode, f                ; Test in wich mode we are.
  btfss  STATUS, Z              ;
  goto   LoopM1                 ; Not zero, so we are in prog mode.
  call   showDispTime           ; Mode zero, so display the time.
  goto   LoopMode_af            ;

LoopM1                          ; Prog mode.
  movlw  D'17'                  ; Test which prog mode, i.e. timer,
  subwf  mode, w                ; light or settings.
  btfss  STATUS, Z              ;
  goto   LoopM2                 ; Not in settings.
  call   showDispSet            ; Mode is 17, so this is settings.
                                ; Display these.
  goto   LoopMode_af            ;

LoopM2                          ; Prog mode, either timer or light.
  movlw  D'15'                  ; Test which of these two.
  subwf  mode, w                ;
  btfss  STATUS, C              ;
  goto   LoopM3                 ; Mode < 15, so this is timer.
  call   showDispProgLight      ; Else, must be light prog, so show this.
  goto   LoopMode_af            ;

LoopM3
  call   showDispProg           ; Show timer settings.

LoopMode_af


  call   onMinute               ; Do actions to be done every minute.
  call   onHalfMinute           ; Do actions to be done every half minute.
  call   onQuarterSecond        ; Do actions to be done every 1/4 second.
  call   evaluate               ; Evaluate to see if relay needs to be changed.
  call   showRelay              ; Output the relay variable to the relay.
  call   showDispZone           ; Output the temporary dispZone to the LCD.

  btfss  yellowLED, 0           ; Translate the yellowLED variable
  bcf    PORTA, 2               ; to the yellow LED which is
  btfsc  yellowLED, 0           ; on port A2.
  bsf    PORTA, 2               ;

  goto   Loop                   ; End loop.




; Subroutines


; SendCHAR, SendINS and NybbleOut are all LCD related functions.
;

;**********************************************************************
;*                                                                    *
;*     SendCHAR                                                       *
;*                                                                    *
;*     Function: sends char to LCD.                                   *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
SendCHAR                        ; Send the Character to the LCD.
  movwf  Temp                   ; Save the Temporary Value.

  swapf  Temp, w                ; Send the High Nybble.
  bsf    STATUS, C              ; RS = 1.
  call   NybbleOut              ;

  movf   Temp, w                ; Send the Low Nybble.
  bsf    STATUS, C              ; RS = 1.
  call   NybbleOut              ;


  return                        ;


;**********************************************************************
;*                                                                    *
;*     SendINS                                                        *
;*                                                                    *
;*     Function: sends instruction to LCD.                            *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
SendINS                         ; Send the Instruction to the LCD.
  movwf  Temp                   ; Save the Temporary Value.

  swapf  Temp, w                ; Send the High Nybble.
  bcf    STATUS, C              ; RS = 0.
  call   NybbleOut              ;

  movf   Temp, w                ; Send the Low Nybble.
  bcf    STATUS, C              ; RS = 0.
  call   NybbleOut              ;

  return                        ;

;**********************************************************************
;*                                                                    *
;*     NybbleOut                                                      *
;*                                                                    *
;*     Function: send a Nybble to the LCD.                            *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
NybbleOut                       ; Send a Nybble to the LCD.

  movwf  NOTemp                 ; Save the Nybble to Shift Out.
  swapf  NOTemp, f              ; Setup to Output to the High Part of the Byte.

#ifdef BLABLABLA
  bcf    NOTemp, 0              ;
  bcf    NOTemp, 1              ;
  btfsc  STATUS, C              ; Put out the RS bit.
  bsf    NOTemp, RS             ;
  btfss  STATUS, C              ;
  bcf    NOTemp, RS             ;
  bcf    NOTemp, E              ;

  movf   NOTemp, w              ;
  movwf  LCD                    ;
#endif


  bcf    LCD, E
  bcf    LCD, RS

  bcf    LCD, 4
  bcf    LCD, 5
  bcf    LCD, 6
  bcf    LCD, 7

  btfsc  STATUS, C
  bsf    LCD, RS

  btfsc  NOTemp, 4
  bsf    LCD, 4
  btfsc  NOTemp, 5
  bsf    LCD, 5
  btfsc  NOTemp, 6
  bsf    LCD, 6
  btfsc  NOTemp, 7
  bsf    LCD, 7




  
  EStrobe                       ; Strobe out the LCD data.

  movlw  256 - ( 20  / 4 )      ; Loop until carry set.
dlayNO
  addlw  1                      ;
  btfss  STATUS, C              ;
  goto   dlayNO                 ;
  return                        ;



;**********************************************************************
;*                                                                    *
;*     showDispZone                                                   *
;*                                                                    *
;*     Function: moves the contents of the 16-byte array 'dispZone'   *
;*               to the LCD display                                   *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
showDispZone

  btfsc  timeSetting, 0         ; If we are setting the time (which
  goto   sdz1                   ; occurs only at startup), we might need
                                ; to blink some chars, so go to sdz1

  movf   mode, f                ; Not setting time, so test if we are
  btfss  STATUS, Z              ; in normal time display or in one of
  goto   sdz1                   ; the prog modes.

  goto   sdz2                   ; Normal time display, so no blinking.

sdz1                            ; Blinking code.
  btfss  bShowFlags, 3          ; Half second interval or not?
  goto   sdz2                   ; No, so output the dispZone as it is.
  btfsc  bKbdControlFlags, 7    ; Hold key or not?
  goto   sdz2                   ; Yes, so don't blink.
;
; Following piece of code is to make one or more characters 'blink'
; so the user nows he is in a setting mode.
  movlw  dispZone               ; Get address of the start of the
  addwf  cursor, w              ; dispZone and add value of cursor to it.
  movwf  FSR                    ; Use indirect addressing because this is easier.
  movlw  A' '                   ; Change the value to space at the
  movwf  INDF                   ; position of the cursor.
  incf   FSR, f                 ; Prepare for a second char to be changed to ' '.
  btfsc  cursor, 3              ; Only do this second one at some cursor
  movwf  INDF                   ; positions.
  movf   timeSetting, w         ;
  addwf  cursor, w              ;
  sublw  7                      ;
  movlw  A' '                   ;
  btfsc  STATUS, Z              ;
  movwf  INDF                   ;
  incf   FSR, f                 ; Now prepare for a possible third char.
  movlw  D'12'                  ; This must also only occur at some
  subwf  cursor, w              ; particular cursor positions like 12.
  movlw  A' '                   ;
  btfsc  STATUS, Z              ;
  movwf  INDF                   ;
  movlw  8                      ; There is also a third position to be blanked
  subwf  cursor, w              ; when the cursor is exactly 8.
  movlw  A' '                   ;
  btfsc  STATUS, Z              ;
  movwf  INDF                   ;

  btfss  bShowFlags, 2          ; Keep blinking until next Second interval.
  goto   sdz2                   ; Not yet a second, so keep blinking.

  bcf    bShowFlags, 2          ; Second reached, so clear the interval flags
  bcf    bShowFlags, 3          ; to stop blinking (note: interval flags
                                ; are all set in interrupt handling routine).

;
;
sdz2
  movlw	 0x080			; Move cursor.
  call	 SendINS                ;
  call	 Dlay160                ; Note, can take up to 160 usecs.

  movf   dispZone, w            ; Form one line of display.
  call   SendCHAR               ;
  movf   dispZone + 1, w        ;
  call   SendCHAR               ;
  movf   dispZone + 2, w        ;
  call   SendCHAR               ;
  movf   dispZone + 3, w        ;
  call   SendCHAR               ;
  movf   dispZone + 4, w        ;
  call   SendCHAR               ;
  movf   dispZone + 5, w        ;
  call   SendCHAR               ;
  movf   dispZone + 6, w        ;
  call   SendCHAR               ;
  movf   dispZone + 7, w        ;
  call   SendCHAR               ;

  movlw	 0x0c0			; Move cursor.
  call	 SendINS                ;
  call	 Dlay160                ; Note, can take up to 160 usecs.

  movf   dispZone + 8, w        ; Form second line (which is actually
  call   SendCHAR               ; a continuation of the first line) of
  movf   dispZone + 9, w        ; display.
  call   SendCHAR               ;
  movf   dispZone + 10, w       ;
  call   SendCHAR               ;
  movf   dispZone + 11, w       ;
  call   SendCHAR               ;
  movf   dispZone + 12, w       ;
  call   SendCHAR               ;
  movf   dispZone + 13, w       ;
  call   SendCHAR               ;
  movf   dispZone + 14, w       ;
  call   SendCHAR               ;
  movf   dispZone + 15, w       ;
  call   SendCHAR               ;



  return                        ;


;**********************************************************************
;*                                                                    *
;*     store2Eeprom                                                   *
;*                                                                    *
;*     Function: stores settings in EEPROM                            *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
store2Eeprom

; Interrupts must be disabled during eeprom write.
;
  bcf    INTCON, T0IE           ; Disable timer interrupt.
  bcf    INTCON, GIE            ; Globally disallow interrupts.

  bank2                         ;
  movlw  100                    ;
  movwf  EEADR                  ; Address 100 has to marked with my
  bank0                         ; year of birth to indicate a valid
  movlw  MYBIRTHYEAR            ; write is now present.
  bank2                         ;
  movwf  EEDATA                 ;
  bank0                         ;
  call   ee_wr                  ; write to EEPROM.


; Store 68 bytes to EEPROM.

  movlw  D'68'                  ; 17 programs * 4 bytes = 68.
  movwf  Temp                   ; Counter.
  clrf   Temp + 1               ; Temp + 1 is an address keeper for EEPROM.
  movlw  Hours01                ; Hours01 is the starting address of the
                                ; 68-byte range to be written to eeprom.
  movwf  FSR                    ; FSR contains address for data.

storeLoop                       ; Loop to store all 68 bytes to Eeprom.

  movf   Temp + 1, w            ; Form the address where to write to in
  bank2                         ; eeprom.
  movwf  EEADR                  ;
  bank0                         ;
  movf   INDF, w                ; Get the value to be written and put this in W.
  bank2                         ;
  movwf  EEDATA                 ;
  bank0                         ;
  call   ee_wr                  ; Write one byte.

  incf   Temp + 1, f            ; Next address for eeprom.
  incf   FSR, f                 ; Next address for data.
  decfsz Temp, f                ; Complete the loop.
  goto   storeLoop              ;


  bsf    bEepromBoost, 0        ; Request a boost of the clock because
                                ; we delayed interrupts during our
                                ; eeprom write business.

  bsf    INTCON, GIE            ; Globally allow interrupts.
  bsf    INTCON, T0IE           ; Enable timer interrupt.


  movlw  H'FF'                  ; Force catch of new light measurement
  movwf  prevLight              ; after settings have changed.

  call   calculateDutyCycles    ;

  return                        ;

;**********************************************************************
;*                                                                    *
;*     loadFromEeprom                                                 *
;*                                                                    *
;*     Function: loads settings from EEPROM. If no valid settings are *
;*               present, loads default settings.                     *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
loadFromEeprom

; First, test if EEPROM has been used before.
  bank2                         ;
  movlw  D'100'                 ;
  movwf  EEADR                  ; Read address 100.
  bank0                         ;
  call   ee_rd                  ; Read one byte.
  bank2                         ;
  movf   EEDATA, w              ; Put value read in w register.
  bank0                         ;

  sublw  MYBIRTHYEAR            ; If address 100 contains my year of
  btfss  STATUS, Z              ; birth, the EEPROM has been used before.
  goto   notUsedBefore          ; If not, eeprom does not contain valid
                                ; data, so fix this.

usedBefore
; Load 68 bytes from EEPROM.

  movlw  D'68'                  ; 68 bytes cause we have 17 x 4 settings.
  movwf  Temp                   ; Counter.
  clrf   Temp + 1               ; Temp + 1 is an address keeper for EEPROM.
  movlw  Hours01                ; Hours01 in bank1 is starting point.
  movwf  FSR                    ; FSR contains address for result.

loadLoop

  movf   Temp + 1, w            ; Form the eeprom address to be read.
  bank2                         ;
  movwf  EEADR                  ;
  bank0                         ;
  call   ee_rd                  ; Read one byte.
  bank2                         ;
  movf   EEDATA, w              ; Put byte that was read in w register.
  bank0                         ;
  movwf  INDF                   ; Move to destination.

  incf   Temp + 1, f            ; Next address in eeprom.
  incf   FSR, f                 ; Next destination address in bank 1.
  decfsz Temp, f                ; Complete the loop.
  goto   loadLoop               ;


  return                        ;



notUsedBefore
; Initialise 17 settings to default values.

  movlw  D'17'                  ; There are 17 settings.
  movwf  Temp                   ; Counter.
  movlw  Hours01                ; Hours01 in bank 1 is the first one.
  movwf  FSR                    ; FSR contains address for result.

defaultLoop

  clrf   INDF                   ; Hours.
  incf   FSR, f                 ;
  clrf   INDF                   ; Minutes.
  incf   FSR, f                 ;
  movlw  H'FF'                  ;
  movwf  INDF                   ; Days.
  incf   FSR, f                 ;
  movlw  3                      ;
  movwf  INDF                   ; Action.
  incf   FSR, f                 ;

  decfsz Temp, f                ; Complete the loop.
  goto   defaultLoop            ;

  bank1                         ; Some special values are needed
  movlw  D'50'                  ; for the light programs and set
  movwf  Duty                   ; programs.
  movlw  5                      ;
  movwf  Sensi                  ;
  movlw  3                      ;
  movwf  OnDarkA                ;
  movwf  OnLightA               ;
  movlw  H'FF'                  ;
  movwf  OnDarkH                ;
  movwf  OnLightH               ;
  bank0                         ;

; Since we now have valid data, store this to eeprom.
  call   store2Eeprom           ; Store values in EEPROM.

  return                        ;

;**********************************************************************
;*                                                                    *
;*     ee_wr                                                          *
;*                                                                    *
;*     Function: writes one byte to EEPROM                            *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
; ee_wr
;
; Writes byte in EEDATA to EEPROM location at EEADR.  Interrupts
; should be disabled before calling ee_wr.

ee_wr
  bsf   STATUS, RP1             ;
  bsf   STATUS, RP0             ; Bank 3.
  btfsc EECON1, WR              ; Wait for
  goto  $-1                     ; write to finish.
  bcf   EECON1, EEPGD           ; Point to Data memory.
  bsf   EECON1, WREN            ; Enable writes.
  movlw 0x55                    ; Write 55h to
  movwf EECON2                  ; EECON2.
  movlw 0xaa                    ; Write AAh to
  movwf EECON2                  ; EECON2.
  bsf   EECON1, WR              ; Start write operation.

  btfsc EECON1, WR              ; Wait for
  goto  $ - 1                   ; write to finish.

  bcf   EECON1, WREN            ; Disable writes.

  bank0                         ;
  return                        ;


;**********************************************************************
;*                                                                    *
;*     ee_rd                                                          *
;*                                                                    *
;*     Function: reads one byte from EEPROM                           *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
; ee_rd 
; 
; Reads EEPROM byte at EEPROM location EEADR into EEDATA

ee_rd
  bsf   STATUS, RP1             ;
  bsf   STATUS, RP0             ; Bank 3.
  bcf   EECON1, EEPGD           ; Point to Data memory.
  bsf   EECON1, RD              ; Start read operation.
  bank0                         ;
  return                        ;
  

;**********************************************************************
;*                                                                    *
;*     keyboardRead                                                   *
;*                                                                    *
;*     Function: reads keys pressed.                                  *
;*                                                                    *
;*     return: keys                                                   *
;*                                                                    *
;**********************************************************************
keyboardRead

  btfsc  PORTB, 0               ;
  goto   kb0_off                ;
  call   Dlay20                 ;
  btfsc  PORTB, 0               ;
  goto   kb0_off                ;
  bsf    keys, 0                ;
  return                        ;
kb0_off
  bcf    keys, 0                ;

  btfsc  PORTB, 1               ;
  goto   kb1_off                ;
  call   Dlay20                 ;
  btfsc  PORTB, 1               ;
  goto   kb1_off                ;
  bsf    keys, 1                ;
  return                        ;
kb1_off
  bcf    keys, 1                ;

  btfsc  PORTB, 2               ;
  goto   kb2_off                ;
  call   Dlay20                 ;
  btfsc  PORTB, 2               ;
  goto   kb2_off                ;
  bsf    keys, 2                ;
  return                        ;
kb2_off
  bcf    keys, 2                ;

  btfsc  PORTB, 3               ;
  goto   kb3_off                ;
  call   Dlay20                 ;
  btfsc  PORTB, 3               ;
  goto   kb3_off                ;
  bsf    keys, 3                ;
  return                        ;
kb3_off
  bcf    keys, 3                ;



  return                        ;




;**********************************************************************
;*                                                                    *
;*     verwerkKbd                                                     *
;*                                                                    *
;*     Function: processes keys.                                      *
;*                                                                    *
;*     input: keys                                                    *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
verwerkKbd

  movf   keys, f                ; Trap to make sure something is in
  btfsc  STATUS, Z              ; keys.
  return                        ; Nothing to do so return.

  bsf    bKbdControlFlags, 0    ; Indicate that we are processing a
                                ; key. This makes sure that the user
                                ; must first release the key before
                                ; we process again.

;
; Yellow LED flickering stuff.
;
  btfsc  yellowLED, 0           ; Flicker the yellow LED each time
  goto   vwkKbdRev              ; a key is pressed (I thought I just
  bsf    PORTA, 2               ; liked this).
  call   Dlay20                 ; Leave LED on for a while.
  bcf    PORTA, 2               ; Back off.
  goto   vwkKbdSam              ; Two ways coming together.
vwkKbdRev
  bcf    PORTA, 2               ; If yellow LED was already on (it also
  call   Dlay20                 ; has other functions) reverse the
  bsf    PORTA, 2               ; operation (off it for a while).
vwkKbdSam

  movf   mode, f                ;
  btfss  STATUS, Z              ;
  goto   VerwerkKbdM1           ;
  call   kbdTime                ;
  goto   VerwerkKbdMode_af      ;


;
; Now the actual key processing is going to take place.
;
VerwerkKbdM1
  movlw  D'17'                  ; Depending on current mode, the
  subwf  mode, w                ; key presses must be dispatched
  btfss  STATUS, Z              ; further to either kbdProg,
  goto   VerwerkKbdM2           ; kbdProgLight or kbdSet.
  call   kbdSet                 ; This is the kbdSet case.
  goto   VerwerkKbdMode_af      ; Together again.

VerwerkKbdM2
  movlw  D'15'                  ; At this stage, we are left with
  subwf  mode, w                ; either kbdProg or kbdProgLight.
  btfss  STATUS, C              ;
  goto   VerwerkKbdM3           ;
  call   kbdProgLight           ; This is the kdbProgLight case.
  goto   VerwerkKbdMode_af      ;

VerwerkKbdM3
  call   kbdProg                ; Only possibility left.

VerwerkKbdMode_af

  return                        ;


;**********************************************************************
;*                                                                    *
;*     kbdTime                                                        *
;*                                                                    *
;*     Function: dispatcher for key presses for time setting mode     *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
kbdTime

  btfsc  keys, 0                ; Four keys possible, this is a case
  goto   kbdT0                  ; statement that goes either to
  btfsc  keys, 1                ; kbdT0, kbdT1, kbdT2 or kbdT4.
  goto   kbdT1                  ;
  btfsc  keys, 2                ;
  goto   kbdT2                  ;
  btfsc  keys, 3                ;
  goto   kbdT3                  ;
  return                        ; Wrong case, so nothing to do.

kbdT0                           ; 'Time' key pressed.
  btfss  timeSetting, 0         ; If this key pressed while not setting
  goto   kbdT0_1                ; the time, may be it is a hold key.
  clrf   timeSetting            ; Else (setting the time) finish the
  return                        ; time setting mode and return.

kbdT0_1                         ; Test for hold key.
  btfsc  bKbdControlFlags, 7    ; If key is hold,
  bsf    timeSetting, 0         ; invoke time setting mode.
  btfsc  timeSetting, 0         ; Reset cursor when invoking
  clrf   cursor                 ; the time setting mode.
  return                        ;

kbdT1                           ; 'Prog' key pressed.
  btfsc  timeSetting, 0         ; If this key pressed while still in
  return                        ; time setting mode, do nothing.
  call   incrMode               ; Else (normal time display) increment
  return                        ; the mode to 1 and return.

kbdT2                           ; '->' (move cursor) key pressed.
  btfsc  timeSetting, 0         ; If we are in time setting mode,
  goto   kbdT2_2                ; than increment the cursor.
  call   incrRelay              ; Else (normal time display) the
  clrf   snooze                 ; function is "manual", also
  return                        ; snooze has to be disabled.

kbdT2_2
  call   incrCursor             ; Increment the cursor.
  return                        ;


kbdT3                           ; '^' (up) key pressed.
  btfsc  timeSetting, 0         ; If we are in time setting mode,
  goto   kbdT3_2                ; do the 'up' function.
  movlw  9                      ; Else the function is 'snooze',
  movwf  snooze                 ; so initialize the snooze value with
  return                        ; 9 minutes and return.
kbdT3_2                         ; Do the 'up' function.
  movf   cursor, f              ; Test if cursor is zero.
  btfsc  STATUS, Z              ;
  goto   kbdT3_C0               ; Yes: do cursor 0.
  movlw  1                      ; No: next test.
  subwf  cursor, w              ; Test if cursor is one.
  btfsc  STATUS, Z              ;
  goto   kbdT3_C1               ; Yes: do cursor 1.
  movlw  3                      ; No: next test.
  subwf  cursor, w              ; Test if cursor is three.
  btfsc  STATUS, Z              ;
  goto   kbdT3_C3               ; Yes: do cursor 3.
  movlw  4                      ; No: next test.
  subwf  cursor, w              ; Test if cursor is four.
  btfsc  STATUS, Z              ;
  goto   kbdT3_C4               ; Yes: do cursor 4.
  movlw  6                      ; No: next test.
  subwf  cursor, w              ; Test if cursor is six.
  btfsc  STATUS, Z              ;
  goto   kbdT3_C6               ; Yes: do cursor 6.
  movlw  9                      ; No: next test.
  subwf  cursor, w              ; Test if cursor is nine.
  btfsc  STATUS, Z              ;
  goto   kbdT3_C9               ; Yes: do cursor 9.
  return                        ; No: wrong cursor value so return.

kbdT3_C0
  movf   Hours, w               ; When cursor is on position zero
  movwf  hourVar                ; the hours must be advanced by
  call   addHour10              ; ten.
  movwf  Hours                  ; Result to Hours of the clock.
  return                        ;

kbdT3_C1
  btfsc  bKbdControlFlags, 7    ; If key is hold, keep on adding.
  bcf    bKbdControlFlags, 0    ;
  btfsc  bKbdControlFlags, 7    ;
  call   Dlay50                 ;

  movf   Hours, w               ; When cursor is on position one
  movwf  hourVar                ; the hours must be advanced by
  call   addHour1               ; one.
  movwf  Hours                  ; Result to Hours of the clock.
  return                        ;

kbdT3_C3
  movf   Minutes, w             ; When cursor is on position three
  movwf  minuteVar              ; the minutes must be advanced by
  call   addMinute10            ; ten.
  movwf  Minutes                ; Result to Minutes of the clock.
  return                        ;

kbdT3_C4
  btfsc  bKbdControlFlags, 7    ; If key is hold, keep on adding.
  bcf    bKbdControlFlags, 0    ;
  btfsc  bKbdControlFlags, 7    ;
  call   Dlay50                 ;

  movf   Minutes, w             ; When cursor is on position four
  movwf  minuteVar              ; the minutes must be advanced by
  call   addMinute1             ; one.
  movwf  Minutes                ; Result to Minutes of the clock.
  return                        ;

kbdT3_C6
  clrf   Seconds                ; When cursor is on position six
  clrf   TimerL                 ; the seconds must be zeroed out.
  clrf   TimerH                 ;
  return                        ;
  
kbdT3_C9
  incf   Days, f                ; When cursor is on position nine
  movlw  7                      ; the days must be advanced by
  subwf  Days, w                ; one.
  btfsc  STATUS, Z              ; Wrap around if necessary.
  clrf   Days                   ;
  return                        ;

kbdTimeAf
  return                        ;

;**********************************************************************
;*                                                                    *
;*     onMinute                                                       *
;*                                                                    *
;*     Function: do actions that have to be done every minute.        *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
onMinute
  btfss  bShowFlags, 7          ; Are we on a complete minute event?
  return                        ; No, so there is nothing to do.

  bcf    bShowFlags, 7          ; Yes: mark that minute event processing
                                ; will now occur.

; Handle snooze time.
  movf   snooze, f              ; Every minute, the remaining snooze
  btfss  STATUS, Z              ; time has to be lowered by one unless
  decf   snooze, f              ; it was already zero.

; Handle random on time.
  movf   ranOnTime, f           ; Every minute, the remaining on time
  btfss  STATUS, Z              ; for the randomizer has to be decremented
  decf   ranOnTime, f           ; unless it was already zero.

; Handle random off time.
  movf   ranOffTime, f          ; Every minute, the remaining off time
  btfss  STATUS, Z              ; for the randomizer has to be lowered
  decf   ranOffTime, f          ; unless if already zero (in practice,
                                ; only the ON time OR the OFF time will
                                ; be higher than zero).

  incf   MinutesCounter, f      ; This is to see if an hour has gone by
  movlw  D'60'                  ; since a certain time (not necessary a
  subwf  MinutesCounter, w      ; complete hour of the clock).
  btfss  STATUS, Z              ;
  return                        ;
  clrf   MinutesCounter         ;
; do hour stuff
  btfsc  Hours1516, 7           ;
  goto   om_HourAf              ;
  movf   Hours1516, f           ; Every hour, decrement the remaining hours
  btfss  STATUS, Z              ; for the light programs (program 15 and 16),
  decf   Hours1516, f           ; unless already zero.
om_HourAf


  return                        ;



;**********************************************************************
;*                                                                    *
;*     onHalfMinute                                                   *
;*                                                                    *
;*     Function: do actions that have to be done every half minute.   *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
onHalfMinute
  btfss  bShowFlags, 4          ; Are we on a half minute interval?
  return                        ; No: there is nothing to do.

  bcf    bShowFlags, 4          ; Yes: mark that we processed this
                                ; event.

; One of the things to do is to compare the current value of the light
; sensor with the sensitivity setting, this must only be done once
; per half minute to compensate for small fluctuations in light (i.e.
; to establish some kind of 'hysteresis').
;
; evaluate light
  call   evaluateLight          ;



  return                        ;


;**********************************************************************
;*                                                                    *
;*     onQuarterSecond                                                *
;*                                                                    *
;*     Function: do actions that have to be done every 1/4 second.    *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
onQuarterSecond
  btfss  bShowFlags, 5          ; Are we on a 1/4 second interval?
  return                        ; No: there is nothing to do.

  bcf    bShowFlags, 5          ; Yes: mark that we processed this
                                ; event.

; What we do in the code below is this: if the user is keeping a
; key pressed, we increment a counter of 1/4 seconds (bit 1-4 of
; bKbdControlFlags). When this counter reaches 8 (= 2 secs), a flag
; (bit 7 of bKbdControlFlags) is set and the keyboard-busy flag
; (bit 0 of bKbdControlFlags) is cleared to force the key to be
; processed. This way, the other functions can distinguise between
; normal key presses and 'hold' keys by checking bit 7 of
; bKbdControlFlags (if set, it means a 'hold' key).
;
  btfss  bKbdControlFlags, 0    ; If keyboard not busy,
  return                        ; there is nothing to do.

  btfsc  bKbdControlFlags, 7    ; If the key is already in 'hold' state,
  return                        ; there is also nothing to do.

  bcf    STATUS, C              ; For next rrf we must shift in a 0.
  rrf    bKbdControlFlags, f    ; Scroll variable to have the counter
                                ; right aligned.
  incf   bKbdControlFlags, f    ; Increment the counter.
  bsf    STATUS, C              ; Carry 1 to restore bit 0 which was
                                ; high anyway.
  rlf    bKbdControlFlags, f    ; Put everything back to its original
                                ; position.
  movlw  B'10000000'            ;
  btfsc  bKbdControlFlags, 4    ; If counter reaches the value of 8,
  movwf  bKbdControlFlags       ; set 'hold' bit, clear busy bit and
                                ; counter.

  return                        ;



;**********************************************************************
;*                                                                    *
;*     evaluateLight                                                  *
;*                                                                    *
;*     Function: evaluate light                                       *
;*                                                                    *
;*     return: light                                                  *
;*                                                                    *
;**********************************************************************
evaluateLight
; evaluate light

  bank1                         ;
  movf   Sensi, w               ; Get sensitivity value and store in w.
  bank0                         ;
  subwf  cuLight, w             ; Compare with current value of sensor.
  btfss  STATUS, C              ;
  bcf    light, 0               ; Higher: so it must be dark now.
  btfsc  STATUS, C              ;
  bsf    light, 0               ; Lower: so it must be light now.

  return                        ;

;**********************************************************************
;*                                                                    *
;*     incrMode                                                       *
;*                                                                    *
;*     Function: increment the mode                                   *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
incrMode
  incf   mode, f                ; Increment the current mode.
  movlw  D'18'                  ; Wrap around
  subwf  mode, w                ; if necessary.
  movlw  1                      ; Wrapping back to 1, not zero.
  btfsc  STATUS, C              ;
  movwf  mode                   ;

  clrf   cursor                 ; At each incrementation of mode, the
  movf   mode, f                ; cursor goes back to square one.
  btfss  STATUS, Z              ;
  call   incrCursor             ; From cursor 0, go to the first
                                ; valid 'settable' cursor position for
                                ; this (new) mode.
  
  return                        ;

;**********************************************************************
;*                                                                    *
;*     incrCursor                                                     *
;*                                                                    *
;*     Function: increment the cursor                                 *
;*                                                                    *
;*     return: cursor                                                 *
;*                                                                    *
;**********************************************************************
incrCursor
;
; Moves to the next cursor position that is a valid 'settable' position.
; I.e. a position that shows the ':' of the clock is not considered
; 'settable'.
;
; Note that the incrementation of the cursor is dependent of the
; mode we are in.
;
  btfsc  timeSetting, 0         ; Are we in time setting mode?
  goto   incrCursorTime         ; Yes: increment cursor for time setting.

  movf   mode, f                ; Is the mode zero (normal time keeping)?
  btfsc  STATUS, Z              ;
  return                        ; Yes: there is nothing to do.

  movlw  D'15'                  ; Is the mode lower than 15?
  subwf  mode, w                ;
  btfss  STATUS, C              ;
  goto   incrCursorProg         ; Yes: increment cursor for timer progs.

  movlw  D'17'                  ; Is the mode lower than 17?
  subwf  mode, w                ;
  btfss  STATUS, C              ;
  goto   incrCursorProgLight    ; Yes: increment cursor for light progs.
  goto   incrCursorSet          ; No: increment cursor for set prog.
  

incrCursorTime
; 13:34:09 Su []  ......Normal time display
; 0123456789111111
;           012345

  incf   cursor, f              ; Increment cursor.

  movlw  1                      ; Sequentially test the cursor
  subwf  cursor, w              ; for invalid positions
  btfsc  STATUS, Z              ; and at when we encounter a
  return                        ; valid position, return with that one.
  movlw  2                      ; If invalid, test the next position.
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  incf   cursor, f              ;
  movlw  3                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  return                        ;
  movlw  4                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  return                        ;
  movlw  5                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  incf   cursor, f              ;
  movlw  6                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  return                        ;
  movlw  7                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  incf   cursor, f              ;
  movlw  8                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  incf   cursor, f              ;
  movlw  9                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  return                        ;
  clrf   cursor                 ;
  return                        ;



incrCursorProg
; 01 14:56 Tu Off ......EXAMPLE Program 1: relay off all Tuesdays at 14h56
; 0123456789111111
;           012345
; 3 4 6 7 9 12

  incf   cursor, f              ;

  movlw  1                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  incf   cursor, f              ;
  movlw  2                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  incf   cursor, f              ;
  movlw  3                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  return                        ;
  movlw  4                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  return                        ;
  movlw  5                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  incf   cursor, f              ;
  movlw  6                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  return                        ;
  movlw  7                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  return                        ;
  movlw  8                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  incf   cursor, f              ;
  movlw  9                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  return                        ;
  movlw  D'10'                  ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  incf   cursor, f              ;
  movlw  D'11'                  ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  incf   cursor, f              ;
  movlw  D'12'                  ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  return                        ;
  movlw  3                      ;
  movwf  cursor                 ;
  return                        ;

incrCursorProgLight
; OnDark  On  6   ......EXAMPLE OnDark program: relay on as soon as it gets dark, for six hours
; 0123456789111111
;           012345
; 8 12         
  incf   cursor, f              ;

  movlw  8                      ;
  subwf  cursor, w              ;
  movlw  8                      ;
  btfss  STATUS, C              ;
  movwf  cursor                 ;
  movlw  8                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  return                        ;
  movlw  12                     ;
  subwf  cursor, w              ;
  movlw  12                     ;
  btfss  STATUS, C              ;
  movwf  cursor                 ;
  movlw  12                     ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  return                        ;
  movlw  8                      ;
  movwf  cursor                 ;
  return                        ;



incrCursorSet
; Set Se4 Cu7 Du70......Set mode program: Se followed by current light sensitivity setting,
;                                         Cu followed by current light measurement,
;                                         Du followed by current random duty cycle setting
; 0123456789111111
;           012345
; 6 14         

  incf   cursor, f              ;

  movlw  6                      ;
  subwf  cursor, w              ;
  movlw  6                      ;
  btfss  STATUS, C              ;
  movwf  cursor                 ;
  movlw  6                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  return                        ;
  movlw  14                     ;
  subwf  cursor, w              ;
  movlw  14                     ;
  btfss  STATUS, C              ;
  movwf  cursor                 ;
  movlw  14                     ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  return                        ;
  movlw  6                      ;
  movwf  cursor                 ;
  return                        ;



;**********************************************************************
;*                                                                    *
;*     incrRelay                                                      *
;*                                                                    *
;*     Function: increment the relay status                           *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
incrRelay
;
; This function can be called when the manual button is pressed to go
; to the next relay status, but it can also be called from other
; code (or maybe not, I don't remember).
;
  incf   relay, f               ;
  movlw  3                      ; Wrap around when higher than 2.
  subwf  relay, w               ;
  btfsc  STATUS, Z              ;
  clrf   relay                  ; Wrap to zero.

  return                        ;

;**********************************************************************
;*                                                                    *
;*     addHour1                                                       *
;*                                                                    *
;*     Function: add 1 to the hourVar variable                        *
;*                                                                    *
;*     return: w                                                      *
;*                                                                    *
;**********************************************************************
addHour1

  btfsc  hourVar, 7             ; If starting from H'FF', first change
  clrf   hourVar                ; this to zero.

  incf   hourVar, f             ;
  movlw  D'24'                  ;
  subwf  hourVar, w             ;
  btfss  STATUS, Z              ;
  goto   addHour1corr_af        ;
  movlw  D'20'                  ;
  movwf  hourVar                ;

addHour1corr_af
  movf   hourVar, w             ;

  return                        ;

;**********************************************************************
;*                                                                    *
;*     addHour10                                                      *
;*                                                                    *
;*     Function: add 10 to the hourVar variable                       *
;*                                                                    *
;*     return: w                                                      *
;*                                                                    *
;**********************************************************************
addHour10

  btfsc  hourVar, 7             ; If starting from H'FF', first change
  clrf   hourVar                ; this to zero.

  movlw  D'10'                  ; Add 10 to
  addwf  hourVar, f             ; the hours.

; If bit 3 and 4 are both set, we are to high! Also if bit 5 is set!
  btfsc  hourVar, 5             ;
  goto   addHour10corr          ;
  btfss  hourVar, 3             ;
  goto   addHour10corr_af       ;
  btfss  hourVar, 4             ;
  goto   addHour10corr_af       ;

addHour10corr

; 14 +10=24   15 +10=25   16 +10=26   17 +10=27   18 +10=28   19 +10=29   20 +10=30
; 20          20          20          20          20          20          00
; 21 +10=31   22 +10=32   23 +10=33
; 01          02          03

  movlw  D'30'                  ;
  subwf  hourVar, w             ;
  btfsc  STATUS, C              ;
  goto   ah10c1                 ;
  movlw  D'20'                  ;
  movwf  hourVar                ;
  goto   addHour10corr_af       ;

ah10c1
  movlw  D'30'                  ;
  subwf  hourVar, f             ;


addHour10corr_af
  movf   hourVar, w             ;
  return                        ;

;**********************************************************************
;*                                                                    *
;*     addMinute1                                                     *
;*                                                                    *
;*     Function: add 1 to the minuteVar variable                      *
;*                                                                    *
;*     return: w                                                      *
;*                                                                    *
;**********************************************************************
addMinute1

  incf   minuteVar, f           ;
  movlw  D'60'                  ;
  subwf  minuteVar, w           ;
  btfsc  STATUS, Z              ;
  clrf   minuteVar              ;

  movf   minuteVar, w           ;
  return                        ;

;**********************************************************************
;*                                                                    *
;*     addDuty10                                                      *
;*                                                                    *
;*     Function: add 10 to the dutyVar variable                       *
;*                                                                    *
;*     return: w                                                      *
;*                                                                    *
;**********************************************************************
addDuty10

  movlw  D'10'                  ;
  addwf  dutyVar, f             ;
  movlw  D'100'                 ;
  subwf  dutyVar, w             ;
  movlw  D'10'                  ;
  btfsc  STATUS, Z              ;
  movwf  dutyVar                ;

  movf   dutyVar, w             ;
  return                        ;

;**********************************************************************
;*                                                                    *
;*     addMinute10                                                    *
;*                                                                    *
;*     Function: add 10 to the minuteVar variable                     *
;*                                                                    *
;*     return: w                                                      *
;*                                                                    *
;**********************************************************************
addMinute10
;
; 50 + 10 = 60   51 + 10 = 61   52 + 10 = 62   53 + 10 = 63   54 + 10 = 64
; 00             01             02             03             04
; 55 + 10 = 65   56 + 10 = 66   57 + 10 = 67   58 + 10 = 68   59 + 10 = 69
; 05             06             07             08             09

  movlw  D'10'                  ;
  addwf  minuteVar, f           ;

  movlw  D'60'                  ;
  subwf  minuteVar, w           ;
  btfss  STATUS, C              ;
  goto   am10corr_af            ;

  movlw  D'60'                  ;
  subwf  minuteVar, f           ;

am10corr_af
  movf   minuteVar, w           ;
  return                        ;


;**********************************************************************
;*                                                                    *
;*     addDay1                                                        *
;*                                                                    *
;*     Function: add 1 to the dayVar variable. Possible values:       *
;*                                                                    *
;*               0   1   2   3   4   5   6    F4   F5   F6    FF      *
;*               Mo  Tu  We  Th  Fr  Sa  Su   WK   WKSa WkEnd --      *
;*                                                                    *
;*                                                                    *
;*               WK: week of 5 days (Mo - Fr)                         *
;*               WKSa: week of 6 days (Mo - Sa)                       *
;*               WkEnd: weekend (Sa - Su)                             *
;*                                                                    *
;*     return: w                                                      *
;*                                                                    *
;**********************************************************************
addDay1

  incf   dayVar, f              ;
  movlw  7                      ;
  subwf  dayVar, w              ;
  movlw  H'F4'                  ;
  btfsc  STATUS, Z              ;
  movwf  dayVar                 ;

  movlw  H'F7'                  ;
  subwf  dayVar, w              ;
  movlw  H'FF'                  ;
  btfsc  STATUS, Z              ;
  movwf  dayVar                 ;

  movf   dayVar, w              ;
  return                        ;

;**********************************************************************
;*                                                                    *
;*     addAction1                                                     *
;*                                                                    *
;*     Function: add 1 to the actionVar variable                      *
;*                                                                    *
;*     return: w                                                      *
;*                                                                    *
;**********************************************************************
addAction1

  incf   actionVar, f           ;
  movlw  4                      ;
  subwf  actionVar, w           ;
  btfsc  STATUS, C              ;
  clrf   actionVar              ;

  movf   actionVar, w           ;
  return                        ;

;**********************************************************************
;*                                                                    *
;*     addHour1to9                                                    *
;*                                                                    *
;*     Function: add 1 to the hourVar variable with possible values   *
;*               1..9 and h'FF'. This is meant for light programs to  *
;*               set the number of hours for the light program to be  *
;*               active.                                              *
;*                                                                    *
;*     return: w                                                      *
;*                                                                    *
;**********************************************************************
addHour1to9
  incf   hourVar, f             ;
  movf   hourVar, f             ;
  btfsc  STATUS, Z              ;
  incf   hourVar, f             ;
  movlw  D'10'                  ;
  subwf  hourVar, w             ;
  movlw  H'FF'                  ;
  btfsc  STATUS, Z              ;
  movwf  hourVar                ;

  movf   hourVar, w             ;
  return                        ;

;**********************************************************************
;*                                                                    *
;*     kbdProg                                                        *
;*                                                                    *
;*     Function: dispatcher for key presses for time prog setting     *
;*               mode (progs 1 to 14).                                *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
kbdProg

  btfsc  keys, 0                ;
  goto   kbdP0                  ;
  btfsc  keys, 1                ;
  goto   kbdP1                  ;
  btfsc  keys, 2                ;
  goto   kbdP2                  ;
  btfsc  keys, 3                ;
  goto   kbdP3                  ;
  return                        ;

kbdP0
  clrf   mode                   ; When time button pressed, go back
  call   store2Eeprom           ; to time keeping mode (leaving any prog
  return                        ; mode) but don't forget to save settings.

kbdP1
  call   incrMode               ;
  clrf   cursor                 ;
  call   incrCursor             ;
  return                        ;

kbdP2
  call   incrCursor             ;
  return                        ;

kbdP3
  call   fetchProgData          ; Get program data from bank 1 to bank 0,
                                ; that's because I'm too lazy to do lots
                                ; of bank switching.

; 01 14:56 Tu Off ......EXAMPLE Program 1: relay off all Tuesdays at 14h56
; 0123456789111111
;           012345
; 3 4 6 7 9 12 are valid positions.

  movlw  3                      ; Test cursor position and jump to
  subwf  cursor, w              ; appropriate keyboard handling code.
  btfsc  STATUS, Z              ;
  goto   kbdP3_C3               ;
  movlw  4                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  goto   kbdP3_C4               ;
  movlw  6                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  goto   kbdP3_C6               ;
  movlw  7                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  goto   kbdP3_C7               ;
  movlw  9                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  goto   kbdP3_C9               ;
  movlw  D'12'                  ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  goto   kbdP3_C12              ;
  return                        ;

kbdP3_C3                        ; Cursor 3.
  movf   progDataH, w           ;
  movwf  hourVar                ;
  call   addHour10              ;
  movwf  progDataH              ;
  movlw  H'FF'                  ; Special case: if key '^' is hold,
  btfsc  bKbdControlFlags, 7    ; set hours to H'FF'.
  movwf  progDataH              ;
  goto   kbdP_sam               ;
kbdP3_C4                        ; Cursor 4.
  btfsc  bKbdControlFlags, 7    ; If key is hold, keep on adding.
  bcf    bKbdControlFlags, 0    ;
  btfsc  bKbdControlFlags, 7    ;
  call   Dlay50                 ;

  movf   progDataH, w           ;
  movwf  hourVar                ;
  call   addHour1               ;
  movwf  progDataH              ;
  goto   kbdP_sam               ;
kbdP3_C6                        ; Cursor 6.
  movf   progDataM, w           ;
  movwf  minuteVar              ;
  call   addMinute10            ;
  movwf  progDataM              ;
  goto   kbdP_sam               ;
kbdP3_C7                        ; Cursor 7.
  btfsc  bKbdControlFlags, 7    ; If key is hold, keep on adding.
  bcf    bKbdControlFlags, 0    ;
  btfsc  bKbdControlFlags, 7    ;
  call   Dlay50                 ;

  movf   progDataM, w           ;
  movwf  minuteVar              ;
  call   addMinute1             ;
  movwf  progDataM              ;
  goto   kbdP_sam               ;
kbdP3_C9                        ; Cursor 9.
  movf   progDataD, w           ;
  movwf  dayVar                 ;
  call   addDay1                ;
  movwf  progDataD              ;
  goto   kbdP_sam               ;
kbdP3_C12                       ; Cursor 12.
  movf   progDataA, w           ;
  movwf  actionVar              ;
  call   addAction1             ;
  movwf  progDataA              ;
  goto   kbdP_sam               ;

kbdP_sam
  call   storeProgData          ;

  return                        ;


;**********************************************************************
;*                                                                    *
;*     kbdProgLight                                                   *
;*                                                                    *
;*     Function: dispatcher for key presses for light prog setting    *
;*               mode                                                 *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
kbdProgLight
  btfsc  keys, 0                ;
  goto   kbdPL0                 ;
  btfsc  keys, 1                ;
  goto   kbdPL1                 ;
  btfsc  keys, 2                ;
  goto   kbdPL2                 ;
  btfsc  keys, 3                ;
  goto   kbdPL3                 ;
  return                        ;

kbdPL0
  clrf   mode                   ;
  call   store2Eeprom           ;
  return                        ;

kbdPL1
  call   incrMode               ;
  clrf   cursor                 ;
  call   incrCursor             ;
  return                        ;

kbdPL2
  call   incrCursor             ;
  return                        ;

kbdPL3
  call   fetchProgData          ;

; OnDark  On  6   ......EXAMPLE OnDark program: relay on as soon as it gets dark, for six hours
; 0123456789111111
;           012345
; 8 12         

  movlw  8                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  goto   kbdPL3_C8              ;
  movlw  D'12'                  ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  goto   kbdPL3_C12             ;
  return                        ;

kbdPL3_C12
  movf   progDataH, w           ;
  movwf  hourVar                ;
  call   addHour1to9            ;
  movwf  progDataH              ;
  goto   kbdPL_sam              ;
kbdPL3_C8
  movf   progDataA, w           ;
  movwf  actionVar              ;
  call   addAction1             ;
  movwf  progDataA              ;
  goto   kbdPL_sam              ;

kbdPL_sam
  call   storeProgData          ;

  return                        ;


;**********************************************************************
;*                                                                    *
;*     kbdSet                                                         *
;*                                                                    *
;*     Function: dispatcher for key presses for setting mode          *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
kbdSet
  btfsc  keys, 0                ;
  goto   kbdS0                  ;
  btfsc  keys, 1                ;
  goto   kbdS1                  ;
  btfsc  keys, 2                ;
  goto   kbdS2                  ;
  btfsc  keys, 3                ;
  goto   kbdS3                  ;
  return                        ;

kbdS0
  clrf   mode                   ;
  call   store2Eeprom           ;
  return                        ;

kbdS1
  call   incrMode               ;
  clrf   cursor                 ;
  call   incrCursor             ;
  return                        ;

kbdS2
  call   incrCursor             ;
  return                        ;

kbdS3
  call   fetchProgData          ;

; Set Se4 Cu7 Du70......Set mode program: Se followed by current light sensitivity setting,
;                                         Cu followed by current light measurement,
;                                         Du followed by current random duty cycle setting
; 0123456789111111
;           012345
; 6 14         

  movlw  6                      ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  goto   kbdS3_C6               ;
  movlw  D'14'                  ;
  subwf  cursor, w              ;
  btfsc  STATUS, Z              ;
  goto   kbdS3_C14              ;
  return                        ;

kbdS3_C6
  movf   progDataA, w           ;
  movwf  hourVar                ;
  call   addHour1               ;
  movwf  progDataA              ;
  movlw  D'10'                  ;
  subwf  progDataA, w           ;
  btfsc  STATUS, C              ;
  clrf   progDataA              ;
  movf   progDataA, f           ;
  btfsc  STATUS, Z              ;
  incf   progDataA, f           ;
  goto   kbdS_sam               ;
kbdS3_C14
  movf   progDataH, w           ;
  movwf  dutyVar                ;
  call   addDuty10              ;
  movwf  progDataH              ;
  goto   kbdS_sam               ;

kbdS_sam
  call   storeProgData          ;
;  call   calculateDutyCycles    ;

  return                        ;




  return                        ;

;**********************************************************************
;*                                                                    *
;*     fetchProgData                                                  *
;*                                                                    *
;*     Function: fetches the program settings from bank1 for the      *
;*               wanted program (in the mode variable) and stores     *
;*               them in the variables progDataH, progDataM,          *
;*               progDataD and progDataA.                             *
;*                                                                    *
;*     input: mode                                                    *
;*                                                                    *
;*     return: progDataH, progDataM, progDataD, progDataA             *
;*                                                                    *
;**********************************************************************
fetchProgData

  movf   mode, f                ;
  btfsc  STATUS, Z              ;
  return                        ;

  movf   mode, w                ;
  movwf  Temp                   ;
  decf   Temp, f                ;
  bcf    STATUS, C              ;
  rlf    Temp, f                ;
  rlf    Temp, f                ; Temp is now = (mode - 1) * 4.

  movlw  Hours01                ;
  movwf  FSR                    ; FSR contains address for data.
  movf   Temp, w                ;
  addwf  FSR, f                 ;

  movf   INDF, w                ;
  movwf  progDataH              ;
  incf   FSR, f                 ;
  movf   INDF, w                ;
  movwf  progDataM              ;
  incf   FSR, f                 ;
  movf   INDF, w                ;
  movwf  progDataD              ;
  incf   FSR, f                 ;
  movf   INDF, w                ;
  movwf  progDataA              ;


  return                        ;


;**********************************************************************
;*                                                                    *
;*     storeProgData                                                  *
;*                                                                    *
;*     Function: stores the program settings in variables progDataH,  *
;*               progDataM, progDataD and progDataA to the correct    *
;*               storage in bank1 (according to the selected program  *
;*               which must be in mode).                              *
;*                                                                    *
;*     input: mode                                                    *
;*                                                                    *
;*     return: bank1 settings array                                   *
;*                                                                    *
;**********************************************************************
storeProgData

  movf   mode, f                ;
  btfsc  STATUS, Z              ;
  return                        ;

  movf   mode, w                ;
  movwf  Temp                   ;
  decf   Temp, f                ;
  bcf    STATUS, C              ;
  rlf    Temp, f                ;
  rlf    Temp, f                ; Temp is now = (mode - 1) * 4.

  movlw  Hours01                ;
  movwf  FSR                    ; FSR contains address for data.
  movf   Temp, w                ;
  addwf  FSR, f                 ;

  movf   progDataH, w           ;
  movwf  INDF                   ;
  incf   FSR, f                 ;
  movf   progDataM, w           ;
  movwf  INDF                   ;
  incf   FSR, f                 ;
  movf   progDataD, w           ;
  movwf  INDF                   ;
  incf   FSR, f                 ;
  movf   progDataA, w           ;
  movwf  INDF                   ;


  return                        ;


;**********************************************************************
;*                                                                    *
;*     calculateDutyCycles                                            *
;*                                                                    *
;*     Function: from the settable duty cycle which is stored in      *
;*               bank1, calculate the duty cycles for the ON and for  *
;*               the OFF time.                                        *
;*                                                                    *
;*     input: Duty                                                    *
;*                                                                    *
;*     return: dutyOn and dutyOff                                     *
;*                                                                    *
;**********************************************************************
calculateDutyCycles
  bank1                         ;
  movf   Duty, w                ;
  bank0                         ;
  movwf  dutyOn                 ;
  sublw  D'100'                 ;
  movwf  dutyOff                ;

  clrf   ranOnTime              ; Force new duty cycles to get fetched
  clrf   ranOffTime             ; by clearing the time counters for the
                                ; randomizer.

  return                        ;



;**********************************************************************
;*                                                                    *
;*     calculateNewRandomOnT                                          *
;*                                                                    *
;*     Function: from the dutyOn variable, calculate the next number  *
;*               of minutes for the on time of the randomizer.        *
;*                                                                    *
;*     input: dutyOn                                                  *
;*                                                                    *
;*     return: ranOnTime                                              *
;*                                                                    *
;**********************************************************************
calculateNewRandomOnT

  fcall  random                 ;
  movf   ranl, w                ;
  movwf  Multipland + 2         ;
  clrf   Multipland + 1         ;
  clrf   Multipland             ;
  movlw  D'100'                 ;
  addwf  Multipland + 2, f      ;
  btfsc  STATUS, C              ;
  incf   Multipland + 1, f      ;

  clrf   Multiplier             ;
  clrf   Multiplier + 1         ;
  movf   dutyOn, w              ;
  movwf  Multiplier + 2         ;

  fcall  Mul2424                ;

  movf   Product, w             ;
  movwf  Dividend               ;
  movf   Product + 1, w         ;
  movwf  Dividend + 1           ;
  movf   Product + 2, w         ;
  movwf  Dividend + 2           ;
  movf   Product + 3, w         ;
  movwf  Dividend + 3           ;
  movf   Product + 4, w         ;
  movwf  Dividend + 4           ;
  movf   Product + 5, w         ;
  movwf  Dividend + 5           ;

  clrf   Divisor                ;
  movlw  0x3                    ;
  movwf  Divisor + 1            ;
  movlw  0xe8                   ;
  movwf  Divisor + 2            ;

  fcall  Div4823                ;

  movf   Dividend + 5, w        ;
  movwf  ranOnTime              ;

  movf   ranOnTime, f           ;
  btfsc  STATUS, Z              ;
  incf   ranOnTime, f           ;


  return                        ;



;**********************************************************************
;*                                                                    *
;*     calculateNewRandomOffT                                         *
;*                                                                    *
;*     Function: from the dutyOff variable, calculate the next number *
;*               of minutes for the off time of the randomizer.       *
;*                                                                    *
;*     input: dutyOff                                                 *
;*                                                                    *
;*     return: ranOffTime                                             *
;*                                                                    *
;**********************************************************************
calculateNewRandomOffT

  fcall  random                 ;
  movf   ranl, w                ;
  movwf  Multipland + 2         ;
  clrf   Multipland + 1         ;
  clrf   Multipland             ;
  movlw  D'100'                 ;
  addwf  Multipland + 2, f      ;
  btfsc  STATUS, C              ;
  incf   Multipland + 1, f      ;

  clrf   Multiplier             ;
  clrf   Multiplier + 1         ;
  movf   dutyOff, w             ;
  movwf  Multiplier + 2         ;

  fcall  Mul2424                ;

  movf   Product, w             ;
  movwf  Dividend               ;
  movf   Product + 1, w         ;
  movwf  Dividend + 1           ;
  movf   Product + 2, w         ;
  movwf  Dividend + 2           ;
  movf   Product + 3, w         ;
  movwf  Dividend + 3           ;
  movf   Product + 4, w         ;
  movwf  Dividend + 4           ;
  movf   Product + 5, w         ;
  movwf  Dividend + 5           ;

  clrf   Divisor                ;
  movlw  0x3                    ;
  movwf  Divisor + 1            ;
  movlw  0xe8                   ;
  movwf  Divisor + 2            ;

  fcall  Div4823                ;

  movf   Dividend + 5, w        ;
  movwf  ranOffTime             ;

  movf   ranOffTime, f          ;
  btfsc  STATUS, Z              ;
  incf   ranOffTime, f          ;


  return                        ;

;**********************************************************************
;*                                                                    *
;*     showDispTime                                                   *
;*                                                                    *
;*     Function: show display of time.                                *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
showDispTime

  movf   Hours, w               ;
  call   ByteToDec              ;
  movf   Asc1, w                ;
  movwf  dispZone               ;
  movf   Asc0, w                ;
  movwf  dispZone + 1           ;

  movlw  7                      ; Custom character.
  movwf  dispZone + 2           ;

  movf   Minutes, w             ;
  call   ByteToDec              ;
  movf   Asc1, w                ;
  movwf  dispZone + 3           ;
  movf   Asc0, w                ;
  movwf  dispZone + 4           ;

  movlw  7                      ; Custom character.
  movwf  dispZone + 5           ;

  movf   Seconds, w             ;
  call   ByteToDec              ;
  movf   Asc1, w                ;
  movwf  dispZone + 6           ;
  movf   Asc0, w                ;
  movwf  dispZone + 7           ;

  movlw  A' '                   ;
  movwf  dispZone + 8           ;

  movf   Days, w                ;
  call   ByteToDay              ;
  movf   Asc1, w                ;
  movwf  dispZone + 9           ;
  movf   Asc0, w                ;
  movwf  dispZone + 10          ;

  movlw  A' '                   ;
  movwf  dispZone + 11          ;
  movlw  A'T'                   ;
  movwf  dispZone + 12          ;
  movlw  A's'                   ;
  movwf  dispZone + 13          ;
  movlw  A'e'                   ;
  movwf  dispZone + 14          ;
  movlw  A't'                   ;
  movwf  dispZone + 15          ;

  btfsc  timeSetting, 0         ;
  return                        ;

  movlw  A' '                   ;
  movwf  dispZone + 12          ;
  movwf  dispZone + 13          ;
  movwf  dispZone + 14          ;
  movwf  dispZone + 15          ;
  btfsc  atLeastOne, 0          ;
  movlw  5                      ; At least one active timer program.
  btfsc  atLeastOne, 1          ;
  movlw  6                      ; At least one active light program.
  movwf  dispZone + 12          ;

  movlw  3                      ;
  subwf  atLeastOne, w          ;
  btfsc  STATUS, Z              ;
  movwf  dispZone + 12          ; Both an active timer and light program.

  movf   snooze, f              ;
  btfsc  STATUS, Z              ;
  return                        ;

  movlw  A'S'                   ;
  movwf  dispZone + 14          ;
  movf   snooze, w              ;
  call   ByteToDec              ;
  movf   Asc0, w                ;
  movwf  dispZone + 15          ;



  return                        ;

;**********************************************************************
;*                                                                    *
;*     showDispProg                                                   *
;*                                                                    *
;*     Function: show display of a timer program.                     *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
showDispProg
; 01 14:56 Tu Off ......Example prog display.
; 0123456789111111
;           012345
;
  call   fetchProgData          ;

  movf   mode, w                ;
  call   ByteToDec              ;
  movf   Asc1, w                ;
  movwf  dispZone               ;
  movf   Asc0, w                ;
  movwf  dispZone + 1           ;

  movlw  A' '                   ;
  movwf  dispZone + 2           ;

  movf   progDataH, w           ;
  call   ByteToDec              ;
  movlw  A'-'                   ;
  btfsc  progDataH, 7           ;
  movwf  Asc1                   ;
  btfsc  progDataH, 7           ;
  movwf  Asc0                   ;
  movf   Asc1, w                ;
  movwf  dispZone + 3           ;
  movf   Asc0, w                ;
  movwf  dispZone + 4           ;

  movlw  7                      ; Custom character.
  movwf  dispZone + 5           ;

  movf   progDataM, w           ;
  call   ByteToDec              ;
  movf   Asc1, w                ;
  movwf  dispZone + 6           ;
  movf   Asc0, w                ;
  movwf  dispZone + 7           ;

  movlw  A' '                   ;
  movwf  dispZone + 8           ;

  movf   progDataD, w           ;
  call   ByteToDay              ;
  movf   Asc1, w                ;
  movwf  dispZone + 9           ;
  movf   Asc0, w                ;
  movwf  dispZone + 10          ;

  movlw  A' '                   ;
  movwf  dispZone + 11          ;

  movf   progDataA, w           ;
  call   ByteToAction           ;
  movf   Asc2, w                ;
  movwf  dispZone + 12          ;
  movf   Asc1, w                ;
  movwf  dispZone + 13          ;
  movf   Asc0, w                ;
  movwf  dispZone + 14          ;

  movlw  A' '                   ;
  movwf  dispZone + 15          ;


  return                        ;

;**********************************************************************
;*                                                                    *
;*     showDispProgLight                                              *
;*                                                                    *
;*     Function: show display of a light timer program.               *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
showDispProgLight

; OnDark  Ran 4   ......Example light program.
; 0123456789111111
;           012345
;
  call   fetchProgData          ;

  movlw  A'O'                   ;
  movwf  dispZone               ;
  movlw  A'n'                   ;
  movwf  dispZone + 1           ;

  movlw  D'15'                  ;
  subwf  mode, w                ;
  btfsc  STATUS, Z              ;
  goto   sdpl15                 ;
  movlw  A'L'                   ;
  movwf  dispZone + 2           ;
  movlw  A'i'                   ;
  movwf  dispZone + 3           ;
  movlw  A'g'                   ;
  movwf  dispZone + 4           ;
  movlw  A'h'                   ;
  movwf  dispZone + 5           ;
  movlw  A't'                   ;
  movwf  dispZone + 6           ;
  goto   sdpl_sam               ;
sdpl15
  movlw  A'D'                   ;
  movwf  dispZone + 2           ;
  movlw  A'a'                   ;
  movwf  dispZone + 3           ;
  movlw  A'r'                   ;
  movwf  dispZone + 4           ;
  movlw  A'k'                   ;
  movwf  dispZone + 5           ;
  movlw  A' '                   ;
  movwf  dispZone + 6           ;
sdpl_sam

  movlw  A' '                   ;
  movwf  dispZone + 7           ;

  movf   progDataA, w           ;
  call   ByteToAction           ;
  movf   Asc2, w                ;
  movwf  dispZone + 8           ;
  movf   Asc1, w                ;
  movwf  dispZone + 9           ;
  movf   Asc0, w                ;
  movwf  dispZone + 10          ;

  movlw  A' '                   ;
  movwf  dispZone + 11          ;

  movf   progDataH, w           ;
  call   ByteToDec              ;
  movf   Asc0, w                ;
  movf   progDataH, f           ;
  btfsc  STATUS, Z              ;
  movlw  A'-'                   ;
  btfsc  progDataH, 7           ;
  movlw  A'-'                   ;
  movwf  dispZone + 12          ;

  movlw  A' '                   ;
  movwf  dispZone + 13          ;
  movwf  dispZone + 14          ;
  movwf  dispZone + 15          ;


  return                        ;

;**********************************************************************
;*                                                                    *
;*     showDispSet                                                    *
;*                                                                    *
;*     Function: show display of the settings.                        *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
showDispSet

; Set Se4 Cu7 Du70......Set mode program.
; 0123456789111111
;           012345
;
  movlw  A'S'                   ;
  movwf  dispZone               ;
  movlw  A'e'                   ;
  movwf  dispZone + 1           ;
  movlw  A't'                   ;
  movwf  dispZone + 2           ;
  movlw  A' '                   ;
  movwf  dispZone + 3           ;
  movlw  A'S'                   ;
  movwf  dispZone + 4           ;
  movlw  A'e'                   ;
  movwf  dispZone + 5           ;

  bank1                         ;
  movf   Sensi, w               ;
  bank0                         ;
  call   ByteToDec              ;
  movf   Asc0, w                ;
  movwf  dispZone + 6           ;

  movlw  A' '                   ;
  movwf  dispZone + 7           ;
  movlw  A'C'                   ;
  movwf  dispZone + 8           ;
  movlw  A'u'                   ;
  movwf  dispZone + 9           ;

  call   calculateCuLight       ;

  movf   cuLight, w             ;
  call   ByteToDec              ;
  movf   Asc0, w                ;
  movwf  dispZone + 10          ;

  movlw  A' '                   ;
  movwf  dispZone + 11          ;

  movlw  A'D'                   ;
  movwf  dispZone + 12          ;
  movlw  A'u'                   ;
  movwf  dispZone + 13          ;

;  movf   dutyOn, w              ;
  bank1                         ;
  movf   Duty, w                ;
  bank0                         ;
  call   ByteToDec              ;

  movf   Asc1, w                ;
  movwf  dispZone + 14          ;
  movf   Asc0, w                ;
  movwf  dispZone + 15          ;



  return                        ;

;**********************************************************************
;*                                                                    *
;*     calculateCuLight                                               *
;*                                                                    *
;*     Function: calculate from ADRESH:ADRESL the current light       *
;*               value as a 1 digit value and store this in cuLight.  *
;*                                                                    *
;*     return: cuLight                                                *
;*                                                                    *
;**********************************************************************
calculateCuLight
  bank1                         ;
  movf   ADRESL, w              ;
  bank0                         ;
  movwf  Dividend + 5           ;
  movf   ADRESH, w              ;
  movwf  Dividend + 4           ;
  clrf   Dividend + 3           ;
  clrf   Dividend + 2           ;
  clrf   Dividend + 1           ;
  clrf   Dividend               ;

  movlw  D'100'                 ;
  movwf  Divisor + 2            ;
  clrf   Divisor + 1            ;
  clrf   Divisor                ;

  fcall  Div4823                ;

  movf   Dividend + 5, w        ;
  movwf  cuLight                ;

  movlw  D'10'                  ;
  subwf  cuLight, w             ;
  movlw  9                      ;
  btfsc  STATUS, Z              ;
  movwf  cuLight                ;

  return                        ;

;**********************************************************************
;*                                                                    *
;*     evaluate                                                       *
;*                                                                    *
;*     Function: evaluate all programs and set relay accordingly.     *
;*                                                                    *
;*     return: relay, atLeastOne, Hours1516                           *
;*                                                                    *
;**********************************************************************
evaluate

  movlw  D'14'                  ;
  movwf  Temp                   ; Temp is counter.

  movlw  Hours01                ;
  movwf  FSR                    ;

  bcf    atLeastOne, 0          ;

atLeastLoop
  incf   FSR, f                 ;
  incf   FSR, f                 ;
  incf   FSR, f                 ;
  movf   INDF, w                ;
  sublw  3                      ;
  btfss  STATUS, Z              ;
  bsf    atLeastOne, 0          ;
  incf   FSR, f                 ;
  decfsz Temp, f                ;
  goto   atLeastLoop            ;

  bcf    atLeastOne, 1          ;
  bank1                         ;
  movf   OnLightA, w            ;
  bank0                         ;
  sublw  3                      ;
  btfss  STATUS, Z              ;
  bsf    atLeastOne, 1          ; At least one light program.

  bank1                         ;
  movf   OnDarkA, w             ;
  bank0                         ;
  sublw  3                      ;
  btfss  STATUS, Z              ;
  bsf    atLeastOne, 1          ; At least one dark program.


  clrf   Temp                   ; Initialize counter.
  movf   Seconds, f             ; Timer progs must only be checked
  btfss  STATUS, Z              ; at the first second of every minute,
  goto   evalLoopAf             ; at the other seconds skip test (with 0 in counter).

  movlw  D'14'                  ; There are 14 timer progs.
  movwf  Temp                   ; Temp is counter.

  clrf   Temp + 1               ; Temp + 1 is counter from 0.

evalLoop
  movlw  Hours01                ;
  addwf  Temp + 1, w            ;
  addwf  Temp + 1, w            ;
  addwf  Temp + 1, w            ;
  addwf  Temp + 1, w            ;
  movwf  FSR                    ; FSR first points to Hours01.

  btfsc  INDF, 7                ;
  goto   hourAf                 ;
  movf   INDF, w                ;
  subwf  Hours, w               ;
  btfss  STATUS, Z              ;
  goto   evalSkippy             ;
hourAf
  incf   FSR, f                 ; FSR now points to minutes.
  movf   INDF, w                ;
  subwf  Minutes, w             ;
  btfss  STATUS, Z              ;
  goto   evalSkippy             ;

  incf   FSR, f                 ; FSR now points to day.
  movlw  H'FF'                  ;
  subwf  INDF, w                ;
  btfsc  STATUS, Z              ;
  goto   daySkip                ;

  btfss  INDF, 7                ;
  goto   normalDayTest          ;

  movlw  H'F6'                  ;
  subwf  INDF, w                ;
  btfsc  STATUS, Z              ;
  goto   weekendTest            ;

weekTest
  movf   INDF, w                ;
  andlw  B'00000111'            ;
  addlw  1                      ;
  subwf  Days, w                ;
  btfss  STATUS, C              ;
  goto   daySkip                ;
  goto   evalSkippy             ;

weekendTest
  movlw  5                      ;
  subwf  Days, w                ;
  btfsc  STATUS, C              ;
  goto   daySkip                ;
  goto   evalSkippy             ;

normalDayTest
  movf   INDF, w                ;
  subwf  Days, w                ;
  btfss  STATUS, Z              ;
  goto   evalSkippy             ;
daySkip
  incf   FSR, f                 ; FSR now points to action.
  movf   INDF, w                ;
  sublw  3                      ; Action 3 (no action) will not be
  btfsc  STATUS, Z              ; considered.
  goto   evalSkippy             ;

; We have a match!
  movf   INDF, w                ;
  movwf  relay                  ;
  goto   evalLoopAf             ;

evalSkippy
  incf   Temp + 1, f            ;
  decfsz Temp, f                ;
  goto   evalLoop               ;

evalLoopAf
  movf   Temp, f                ; If no match from the timer progs,
  btfss  STATUS, Z              ; try the light progs,
  return                        ; else (if match) return.

  movf   Hours1516, f           ;
  btfss  STATUS, Z              ;
  goto   evalLightCompWithBefore;

  movf   prevRelay, w           ;
  movwf  relay                  ;
  movlw  H'FE'                  ;
  movwf  Hours1516              ;
  return                        ;

evalLightCompWithBefore
  movf   light, w               ;
  subwf  prevLight, w           ;
  btfss  STATUS, Z              ;
  goto   eval_notEqual          ;

; equal
eval_Equal
  movlw  H'FE'                  ;
  subwf  Hours1516, w           ;
  btfsc  STATUS, Z              ;
  return                        ;

  return

schemerSchakelaar
  bank1                         ;
  movf   OnDarkA, w             ;
  bank0                         ;
  movwf  Temp                   ;
  bank1                         ;
  movf   OnLightA, w            ;
  bank0                         ;
  movwf  Temp + 1               ;

  movf   Temp, w                ;
  btfsc  light, 0               ;
  movf   Temp + 1, w            ;
  movwf  Temp                   ; Temp now contains action.
  movlw  3                      ;
  subwf  Temp, w                ;
  movwf  Temp + 1               ; Temp + 1 contains w != 3.
  movf   Temp, w                ; w now contains action.
  movf   Temp + 1, f            ; Test w != 3.
  btfss  STATUS, Z              ;
  movwf  relay                  ;
  return                        ;


eval_notEqual
; not equal
  movf   light, w               ;
  movwf  prevLight              ;

  movf   relay, w               ;
  movwf  prevRelay              ;

  btfsc  light, 0               ;
  goto   eval_ne_Light          ;

eval_ne_NoLight
  bsf    yellowLED, 0           ;
  bank1                         ;
  movf   OnDarkH, w             ;
  bank0                         ;
  movwf  Hours1516              ;

  movlw  3                      ;
  bank1                         ;
  subwf  OnDarkA, w             ;
  bank0                         ;
  movlw  H'FE'                  ;
  btfsc  STATUS, Z              ;
  movwf  Hours1516              ;

  goto   eval_ne_Sam            ;

eval_ne_Light
  bcf    yellowLED, 0           ;
  bank1                         ;
  movf   OnLightH, w            ;
  bank0                         ;
  movwf  Hours1516              ;

  movlw  3                      ;
  bank1                         ;
  subwf  OnLightA, w            ;
  bank0                         ;
  movlw  H'FE'                  ;
  btfsc  STATUS, Z              ;
  movwf  Hours1516              ;

eval_ne_Sam
  clrf   MinutesCounter         ;
  goto   schemerSchakelaar      ;

  return                        ;

;**********************************************************************
;*                                                                    *
;*     showRelay                                                      *
;*                                                                    *
;*     Function: puts relay on or off according to relay and snooze   *
;*               variables                                            *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
showRelay

  movf   snooze, f              ;
  btfss  STATUS, Z              ;
  goto   shR0                   ;

  movlw  2                      ; Put RANDOM LED on or off.
  subwf  relay, w               ;
  btfsc  STATUS, Z              ;
  bsf    PORTA, 3               ;
  btfss  STATUS, Z              ;
  bcf    PORTA, 3               ;

  movf   relay, f               ;
  btfsc  STATUS, Z              ;
  goto   shR0                   ;

  movlw  1                      ;
  subwf  relay, w               ;
  btfsc  STATUS, Z              ;
  goto   shR1                   ;

shR2
  btfss  randomSwitch, 0        ;
  goto   shR2_off               ;
  goto   shR2_on                ;

shR2_on
  movf   ranOnTime, f           ;
  btfsc  STATUS, Z              ;
  bcf    randomSwitch, 0        ;
  btfsc  STATUS, Z              ;
  call   calculateNewRandomOffT ;
  goto   shR1                   ;
  return                        ;

shR2_off
  movf   ranOffTime, f          ;
  btfsc  STATUS, Z              ;
  bsf    randomSwitch, 0        ;
  btfsc  STATUS, Z              ;
  call   calculateNewRandomOnT  ;
  goto   shR0                   ;
  return                        ;


shR0
  bsf    PORTA, 4               ; Red LED off.
  bcf    PORTA, 5               ; Relay off.
  return                        ;

shR1
  bcf    PORTA, 4               ; Red LED on.
  bsf    PORTA, 5               ; Relay on.
  return                        ;

;**********************************************************************
;*                                                                    *
;*     Dlay160                                                        *
;*                                                                    *
;*     Function: delays 160 usecs.                                    *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
Dlay160                         ;  Delay 160 usecs

;  movlw  D'40'                  ;
  movlw  D'40'                  ;
  movwf  Dlay                   ;

dlay_160
#ifdef DEBUG
  movlw  255                    ; Loop Until Carry Set.
#else
;  movlw  256 - ( 160 / 4 )      ; Loop Until Carry Set.
  movlw  256 - ( 50  / 4 )      ; Loop Until Carry Set.
#endif
dlay160
  addlw  1                      ;
  btfss  STATUS, C              ;
  goto   dlay160                ;

  return                        ;




;**********************************************************************
;*                                                                    *
;*     Dlay5                                                          *
;*                                                                    *
;*     Function: delays 5 msecs.                                      *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************

Dlay5                           ; Delay 5 msecs.
#ifdef DEBUG
  movlw  1                      ; Set up the Delay.
#else
  movlw  4                      ; Set up the Delay.
#endif
  movwf  Dlay                   ;

#ifdef DEBUG
  movlw  255                    ;
#else
  movlw  256 - 0x0E8            ;
#endif

  addlw  1                      ;
  btfsc  STATUS, Z              ;
  decfsz Dlay, f                ;
  goto   $-3                    ;

  return                        ;

;**********************************************************************
;*                                                                    *
;*     Dlay20                                                         *
;*                                                                    *
;*     Function: delays 20 msecs.                                     *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************

Dlay20                          ;  Delay 20 msecs

  call   Dlay5                  ;
  call   Dlay5                  ;
  call   Dlay5                  ;
  call   Dlay5                  ;

  return

;**********************************************************************
;*                                                                    *
;*     Dlay50                                                         *
;*                                                                    *
;*     Function: delays 50 msecs.                                     *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************

Dlay50                          ;  Delay 50 msecs

  call   Dlay20                 ;
  call   Dlay20                 ;
  call   Dlay5                  ;
  call   Dlay5                  ;

  return


;**********************************************************************
;*                                                                    *
;*     Dlay250                                                        *
;*                                                                    *
;*     Function: delays 250 msecs.                                    *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************
Dlay250
#ifdef DEBUG
  movlw  D'1'                   ;  Set up the Delay
#else
  movlw  D'50'                  ;  Set up the Delay
#endif
  movwf  DlayTemp               ;
Dlay250Lus
  call   Dlay5                  ;
  decfsz DlayTemp, f            ;
  goto   Dlay250Lus             ;

  return                        ;
  





;**********************************************************************
;*                                                                    *
;*     ByteToDay                                                      *
;*                                                                    *
;*     Function: converts byte in w to ascii day string like Mo, Tu   *
;*               etc.                                                 *
;*                                                                    *
;*     return: Asc0, Asc1                                             *
;*                                                                    *
;**********************************************************************
ByteToDay
;
;Input: w
;Output Asc1:Asc0
;
  movwf  FSR                    ;
  btfss  FSR, 7                 ;
  goto   btdCorrAf              ;
  andlw  B'00000111'            ;
  sublw  D'14'                  ;
  movwf  FSR                    ;
btdCorrAf
  addwf  FSR, f                 ; Double FSR because each string is 2
                                ; bytes long.

btdLoop
  movf   FSR, w                 ;
  movwf  offset                 ;
  incf   offset, f              ;
  movlw  LOW dayTable           ; Get low 8 bits of table.
  addwf  offset, f              ; Do an 8-bit add operation.
  movlw  HIGH dayTable          ; Get high 5 bits of table.
  btfsc  STATUS, C              ; Page crossed?
  addlw  1                      ; Yes: then increment high address.
  movwf  PCLATH                 ; Load high address in latch.
  movf   offset, w              ; Load computed offset in w register.
  incf   FSR, f                 ;
  call   dayTable               ;
  movwf  Asc0                   ; Chars at odd offsets go to Asc0
  btfsc  FSR, 0                 ; and chars at even offsets go to
  movwf  Asc1                   ; Asc1.
  btfsc  FSR, 0                 ; Leave the loop when passed twice.
  goto   btdLoop                ;

  return                        ;


dayTable
  movwf  PCL                    ;
  DT A'M', A'o', A'T', A'u'     ;
  DT A'W', A'e', A'T', A'h'     ;
  DT A'F', A'r', A'S', A'a'     ;
  DT A'S', A'u', A'-', A'-'     ;
  DT A'S', 4   , 1   , 3        ;
  DT 1   , 2                    ;




;**********************************************************************
;*                                                                    *
;*     ByteToAction                                                   *
;*                                                                    *
;*     Function: converts byte in w to ascii action string like       *
;*               'Off'   'On '   'Ran'   '---'                        *
;*                                                                    *
;*     return: Asc0, Asc1, Asc2                                       *
;*                                                                    *
;**********************************************************************
ByteToAction
;
;Input: w
;Output Asc2:Asc1:Asc0
;
  movwf  TempByte               ;

  movlw  0                      ;
  subwf  TempByte, w            ;
  btfsc  STATUS, Z              ;
  goto   bta0                   ;
  movlw  1                      ;
  subwf  TempByte, w            ;
  btfsc  STATUS, Z              ;
  goto   bta1                   ;
  movlw  2                      ;
  subwf  TempByte, w            ;
  btfsc  STATUS, Z              ;
  goto   bta2                   ;

  movlw  A'-'                   ;
  movwf  Asc2                   ;
  movlw  A'-'                   ;
  movwf  Asc1                   ;
  movlw  A'-'                   ;
  movwf  Asc0                   ;
  return                        ;

bta0
  movlw  A'O'                   ;
  movwf  Asc2                   ;
  movlw  A'f'                   ;
  movwf  Asc1                   ;
  movlw  A'f'                   ;
  movwf  Asc0                   ;
  return                        ;
bta1
  movlw  A'O'                   ;
  movwf  Asc2                   ;
  movlw  A'n'                   ;
  movwf  Asc1                   ;
  movlw  A' '                   ;
  movwf  Asc0                   ;
  return                        ;
bta2
  movlw  A'R'                   ;
  movwf  Asc2                   ;
  movlw  A'a'                   ;
  movwf  Asc1                   ;
  movlw  A'n'                   ;
  movwf  Asc0                   ;
  return                        ;




;**********************************************************************
;*                                                                    *
;*     ByteToDec                                                      *
;*                                                                    *
;*     Function: converts byte in w to ascii decimal value            *
;*                                                                    *
;*     return: Asc0, Asc1 and Asc2                                    *
;*                                                                    *
;**********************************************************************
ByteToDec
;Binary to decimal conversion (0..255)
;
;Input: w
;Output Asc2:Asc1:Asc0
;
  movwf  TempByte               ;

  clrw                          ;
  addlw  241                    ;
  movwf  Asc2                   ; b_2 = 2a_2 - 15

  addwf  Asc2, w                ;
  addwf  Asc2, w                ;
  addlw  253                    ;
  movwf  Asc1                   ;
  swapf  TempByte, w            ;
  andlw  0x0F                   ;
  addwf  Asc1, f                ;
  addwf  Asc1, f                ; b_1 = 6a_2 + 2a_1 - 48

  sublw  251                    ;
  movwf  Asc0                   ;
  addwf  Asc0, f                ;
  addwf  Asc0, f                ;
  addwf  Asc0, f                ;
  movf   TempByte, w            ;
  andlw  0x0F                   ;
  addwf  Asc0, f                ; b_0 = a_0 - 4(a_2 + a_1) - 20

  movlw  10                     ;
bin2dec999a                     ; 9 cycles max
  addwf  Asc0, f                ;
  decf   Asc1, f                ;
  skpc                          ;
  goto   bin2dec999a            ;

bin2dec999b                     ; 6 cycles max
  addwf  Asc1, f                ;
  decf   Asc2, f                ;
  skpc                          ;
  goto   bin2dec999b            ;

bin2dec999c                     ; 3 cycles max
  addwf  Asc2, f                ;
  skpc                          ;
  goto   bin2dec999c            ;

  movlw  0x30                   ;
  addwf  Asc2, f                ;
  addwf  Asc1, f                ;
  addwf  Asc0, f                ;

  return                        ;




  ORG  0x800



;**********************************************************************
;*                                                                    *
;*     ReadAD0                                                        *
;*                                                                    *
;*     Function: reads the voltage from the RA port 0 (battery)       *
;*                                                                    *
;*     return: ADRESH:ADRESL                                          *
;*                                                                    *
;*     Assumptions: Vref- at Vss                                      *
;*                  Vref+ at Vdd                                      *
;*                                                                    *
;**********************************************************************
ReadAD0

  clrf   ADRESH                 ;
  bsf    STATUS, RP0            ; Select bank 1.
  clrf   ADRESL                 ;
  bcf    STATUS, RP0            ; Select bank 0.




  bsf    STATUS, RP0            ; Select bank 1.
  movlw  B'10001110'            ; Configure analog pins.
  movwf  ADCON1                 ;
  bcf    STATUS, RP0            ; Select bank 0.

  bcf    ADCON0, 3              ; Select AD channel 0.
  bcf    ADCON0, 4              ;
  bcf    ADCON0, 5              ;

  bsf    ADCON0, 6              ; Select conversion clock.
  bcf    ADCON0, 7              ;

  bsf    ADCON0, 0              ; Turn on AD module.


  fcall  Dlay160                ; Wait the required acquisition time.


  bsf    ADCON0, 2              ; Start the conversion.

  nop                           ;
  btfsc  ADCON0, 2              ; Conversion done?
  goto   $-2                    ; No, keep polling.
  fcall  Dlay160                ; Yes, make ready for next time.
  fcall  Dlay160                ;
  bcf    ADCON0, 0              ; Turn off AD module.

ReadAD0A

  return                        ;


;**********************************************************************
;*                                                                    *
;*     LoadFont                                                       *
;*                                                                    *
;*     Function: loads some custom characters to CGRAM                *
;*                                                                    *
;*     return: none                                                   *
;*                                                                    *
;**********************************************************************

LoadFont

  movlw	 B'01000000'            ; Load font.
  fcall	 SendINS                ;
  fcall	 Dlay5                  ;



; Load font.
  clrf   FSR                    ;
FontLoop
  movf   FSR, w                 ;
  movwf  offset                 ;
  incf   offset, f              ;
  movlw  LOW Font               ; Get low 8 bits of table.
  addwf  offset, f              ; Do an 8-bit add operation.
  movlw  HIGH Font              ; Get high 5 bits of table.
  btfsc  STATUS, C              ; Page crossed?
  addlw  1                      ; Yes: then increment high address.
  movwf  PCLATH                 ; Load high address in latch.
  movf   offset, w              ; Load computed offset in w register.
  incf   FSR, f                 ;
  call   Font                   ;
  fcall  SendCHAR               ; Output the Font data.
#ifdef DEBUG
  movlw  1                      ; At the End of the Font?
#else
  movlw  D'64'                  ; At the End of the Font?
#endif
  subwf  FSR, w                 ;
  btfss  STATUS, Z              ;
  goto   FontLoop               ;

  return                        ;




Font
  movwf  PCL                    ;
  DT 0x0e, 0x1f, 0x1f, 0x1f, 0x1f, 0x1f, 0x0e, 0; bolleke
  DT 0x11, 0x11, 0x15, 0x1b, 0x11, 0, 0x15, 0; W of week
  DT 0x12, 0x14, 0x18, 0x14, 0x12, 0, 0x14, 0; K of week
  DT 0x12, 0x14, 0x18, 0x14, 0x12, 0, 0x15, 0; K of week-with-6-days
  DT 0x0e, 0x12, 0x0e, 0, 0x12, 0x12, 0x1e, 0; a and u combined
  DT 0x0e, 0x1f, 0x1f, 0x1f, 0, 0, 0, 0; bolleke boven
  DT 0, 0, 0, 0x1f, 0x1f, 0x1f, 0x0e, 0; bolleke beneden
  DT 0, 4, 0x0e, 0x1f, 0x0e, 4, 0, 0; separator



;**********************************************************************
;*                                                                    *
;*     random                                                         *
;*                                                                    *
;*     Function: generate pseudo-random number between 0 .. 255       *
;*                                                                    *
;*     return: ranl                                                   *
;*                                                                    *
;**********************************************************************
;
; Generate pseudo-random number into ranl
;
;
#ifdef OLD_CODE
random                          ; Find next random number.
  movf   ranl, w                ; Check total value zero.
  iorwf  ranh, w                ;
  btfsc  STATUS, Z              ;
  comf   ranh, f                ; Invert some bits if so.
  movlw  0x80                   ;
  btfsc  ranh, 6                ;
  xorwf  ranh, f                ;
  btfsc  ranh, 4                ;
  xorwf  ranh, f                ;
  btfsc  ranl, 3                ;
  xorwf  ranh, f                ;
  bcf    STATUS, C              ; Clear the carry flag.
  btfsc  ranh, 7                ; If the high bit is set ...
  bsf    STATUS, C              ; ... set also the carry flag.
  rlf    ranl, f                ;
  rlf    ranm, f                ;
  rlf    ranh, f                ;
  retlw  0                      ;

#endif

;
;


; Just to see if the random generator works as expected:

testertje
  call random
  goto testertje



random

; Routine supplied by Robert LaBudde and Nikolai Golovchenko.

; Rnew = Rold * 221 + 53
; 221 = 256 - 32 - 4 + 1
; 256 can be eliminated
; so we need to calculate Rnew = Rold * (1 - 32 - 4) + 53 using
; truncating arithmetic
; or Rnew = Rold * (-32 - 3) + 53

  movf   TMR0, w                ; Help randomizer to more randomize.
  addwf  ranl, f                ;

  clrc                          ;
  rlf    ranl, 1                ;
  swapf  ranl, 0                ;
  andlw  0xE0                   ;
  rrf    ranl, 1                ;
  addwf  ranl, 0                ;
  addwf  ranl, 0                ;
  addwf  ranl, 0                ;
  sublw  0x35                   ;
  movwf  ranl                   ;
  retlw  0                      ;




;**********************************************************************
;*                                                                    *
;*     Mul1616                                                        *
;*                                                                    *
;*     Function: multiplies a 16 bit number with a 16 bit number.     *
;*                                                                    *
;*     Product = Multipland * Multiplier                              *
;*                                                                    *
;*     input: Multipland 2 bytes, msb first                           *
;*            Multiplier 2 bytes, msb first                           *
;*                                                                    *
;*                                                                    *
;*                                                                    *
;*     return: Product32 - 4 bytes, msb first                         *
;*                                                                    *
;**********************************************************************
Mul1616

  
  clrf   Product32              ;
  clrf   Product32 + 1          ;
  clrf   Product32 + 2          ;
  clrf   Product32 + 3          ;
  bsf    Product32 + 2, 7       ;

m1
  rrf    Multipland, f          ;
  rrf    Multipland + 1, f      ;
  skpc                          ;
  goto   m2                     ;
  movf   Multiplier + 1, w      ;
  addwf  Product32 + 1, f       ;
  movf   Multiplier, w          ;
  skpnc                         ;
  incfsz Multiplier, w          ;
  addwf  Product32, f           ;

m2
  rrf    Product32, f           ;
  rrf    Product32 + 1, f       ;
  rrf    Product32 + 2, f       ;
  rrf    Product32 + 3, f       ;
  skpc                          ;
  goto   m1                     ;


  return                        ;



;**********************************************************************
;*                                                                    *
;*     Mul2424                                                        *
;*                                                                    *
;*     Function: multiplies a 24 bit number with a 24 bit number      *
;*               resulting in a 48 bit number.                        *
;*                                                                    *
;*     Product = Multipland * Multiplier                              *
;*                                                                    *
;*     Input: Multiplier - 3 bytes (shared with Product)              *
;*            Multipland - 3 bytes (not modified)                     *
;*                                                                    *
;*                                                                    *
;*                                                                    *
;*                                                                    *
;*     return: Product - 6 bytes                                      *
;*                                                                    *
;**********************************************************************
Mul2424

  clrf   Product                ; Clear destination.
  clrf   Product + 1            ;
  clrf   Product + 2            ;

        
  movlw  D'24'
  movwf  BitCount               ; Number of bits.

  rrf    Product + 3, f         ; Shift out to carry.
  rrf    Product + 4, f         ; Next multiplier bit.
  rrf    Product + 5, f         ;

ADD_LOOP_24x24

  btfss  STATUS, C              ; If carry is set we must add multipland
                                ; to the product.
  goto   SKIP_LOOP_24x24        ; Nope, skip this bit.
                
  movf   Multipland + 2, w      ; Get LSB of multiplicand.
  addwf  Product + 2, f         ; Add it to the lsb of the product.
  
  movf   Multipland + 1, w      ; Middle byte.
  btfsc  STATUS, C              ; Check carry for overflow.
  incfsz Multipland + 1, w      ; If carry set we add one to the source 
  addwf  Product + 1, f         ; and add it (if not zero, in
                                ; that case mulitpland = 0xff->0x00).
        
  movf   Multipland, w          ; MSB byte.
  btfsc  STATUS, C              ; Check carry.
  incfsz Multipland, w          ;
  addwf  Product, f             ; Handle overflow.

SKIP_LOOP_24x24
; Note carry contains most significant bit of
; addition here.

; Shift in carry and shift out
; next multiplier bit, starting from less
; significant bit.

  rrf    Product, f             ;
  rrf    Product + 1, f         ;
  rrf    Product + 2, f         ;
  rrf    Product + 3, f         ;
  rrf    Product + 4, f         ;
  rrf    Product + 5, f         ;

  decfsz BitCount, f            ;
  goto   ADD_LOOP_24x24         ;



  return                        ;

;**********************************************************************
;*                                                                    *
;*     Div4823                                                        *
;*                                                                    *
;*     Function: divides a 48 bit number by a 23 bit number.          *
;*                                                                    *
;*     return: result in Dividend.                                    *
;*                                                                    *
;**********************************************************************
;

Div4823
DIVIDE_48by23
; Test for zero division.
  movf   Divisor, w             ;
  iorwf  Divisor + 1, w         ;
  iorwf  Divisor + 2, w         ;
  btfsc  STATUS, Z              ;
  retlw  0                      ; Divisor = zero, not possible to calculate.
                                ; Return with zero in w.

; Prepare used variables.
  clrf   Temp                   ;
  clrf   Temp + 1               ;
  clrf   Temp + 2               ;

  movlw  D'48'                  ; Initialize bit counter.
  movwf  BitCount               ;

  setc                          ;

DIVIDE_LOOP_48by23
  rlf    Dividend + 5, f        ;
  rlf    Dividend + 4, f        ;
  rlf    Dividend + 3, f        ;
  rlf    Dividend + 2, f        ;
  rlf    Dividend + 1, f        ;
  rlf    Dividend, f            ;

; Shift in highest bit from dividend through carry in temp.
  rlf    Temp + 2, f            ;
  rlf    Temp + 1, f            ;
  rlf    Temp, f                ;

  movf   Divisor + 2, w         ; Get LSB of divisor.
  btfss  Dividend + 5, 0        ;
  goto   Div48by23_add          ;

; Subtract 23 bit divisor from 24 bit temp.
  subwf  Temp + 2, f            ; Subtract.

  movf   Divisor + 1, w         ; Get middle byte.
  skpc                          ; If overflow (from prev. subtraction)
  incfsz Divisor + 1, w         ; incresase source
  subwf  Temp + 1, f            ; and subtract from dest.

  movf   Divisor, w             ; Get top byte.
  skpc                          ; If overflow (from prev. subtraction)
  incfsz Divisor, w             ; increase source
  subwf  Temp, f                ; and subtract from dest.
  goto   DIVIDE_SKIP_48by23     ; Carry was set, subtraction ok,
                                ; continue with next bit.

Div48by23_add
; Result of subtraction was negative restore temp.
  addwf  Temp + 2, f            ; Add it to the lsb of temp.

  movf   Divisor + 1, w         ; Middle byte.
  btfsc  STATUS, C              ; Check carry for overflow from previous
                                ; addition.
  incfsz Divisor + 1, w         ; If carry set we add 1 to the source
  addwf  Temp + 1, f            ; and add it if not zero in that case
                                ; Product + Multipland = Product.

  movf   Divisor, w             ; MSB byte.
  btfsc  STATUS, C              ; Check carry for overflow from previous
                                ; addition.
  incfsz Divisor, w
  addwf  Temp, f                ; Handle overflow.

DIVIDE_SKIP_48by23
  decfsz BitCount, f            ; Decrement loop counter.
  goto   DIVIDE_LOOP_48by23     ; Another run.

; Finally shift in the last carry.
  rlf    Dividend + 5, f        ;
  rlf    Dividend + 4, f        ;
  rlf    Dividend + 3, f        ;
  rlf    Dividend + 2, f        ;
  rlf    Dividend + 1, f        ;
  rlf    Dividend, f            ;
  retlw  1                      ; Done.


;**********************************************************************
;*                                                                    *
;*     AdjustADresExtra                                               *
;*                                                                    *
;*     Function: adjusts ADRESH:ADRESL by cricking this up or down    *
;*               with a certain percentage and bias.                  *
;*                                                                    *
;*     input: ADRESH:ADRESL                                           *
;*                                                                    *
;*     return: ADRESH:ADRESL                                          *
;*                                                                    *
;**********************************************************************
AdjustADresExtra
  clrf   Multipland             ; Move ADRES register to multipland.
  movf   ADRESH, w              ;
  movwf  Multipland + 1         ;
  bsf    STATUS, RP0            ;
  movf   ADRESL, w              ;
  bcf    STATUS, RP0            ;
  movwf  Multipland + 2         ;

  movlw  D'00'                  ; Perform a reverse bias of 19.
  addwf  Dividend + 5, f        ;
  btfsc  STATUS, C              ;
  incf   Dividend + 4, f        ;

  clrf   Multiplier             ; Perform a multiplication with 1047
  movlw  4                      ; (4.7 percent).
  movwf  Multiplier + 1         ;
  movlw  0x17                   ;
  movwf  Multiplier + 2         ;

  fcall  Mul2424                ;

  movf   Product, w             ; Move result to dividend.
  movwf  Dividend               ;
  movf   Product + 1, w         ;
  movwf  Dividend + 1           ;
  movf   Product + 2, w         ;
  movwf  Dividend + 2           ;
  movf   Product + 3, w         ;
  movwf  Dividend + 3           ;
  movf   Product + 4, w         ;
  movwf  Dividend + 4           ;
  movf   Product + 5, w         ;
  movwf  Dividend + 5           ;

  clrf   Divisor                ; Perform a division with 1000.
  movlw  0x3                    ;
  movwf  Divisor + 1            ;
  movlw  0xe8                   ;
  movwf  Divisor + 2            ;

  fcall  Div4823                ;

  movlw  D'00'                  ; Perform a bias of 19.
  subwf  Multipland + 2, f      ;
  btfss  STATUS, C              ;
  decf   Multipland + 1, f      ;

  movf   Dividend + 4, w        ; Move result back to ADRES register.
  movwf  ADRESH                 ;
  movf   Dividend + 5, w        ;
  bsf    STATUS, RP0            ;
  movwf  ADRESL                 ;
  bcf    STATUS, RP0            ;



  return                        ;










  end


