; VERSION 1.3, 2009/04/04

; V.1.1
;   - Fixed an alarm setup problem
; V.1.2
;   - Fixed bug with alarm sometimes resetting or stopping the wrong timer
;   - Adjusting for a 1/4 of a second lost when timer was reset by an alarm
; V.1.3
;   - Some timers were blocking when alarm setting were adjusted

#include <p16F877A.inc>

    __config _HS_OSC & _WDT_OFF & _LVP_OFF & _BODEN_ON & _PWRTE_ON ;& _CP_OFF

#include "LCD.h"
#include "KBD.h"

; Status byte definitions
#define TMR_ON                  7    ; 0 = off, 1 = on
#define TMR_DIR                 6    ; 0 = inc, 1 = dec
#define TMR_ON_ALARM_RESET      4    ; 1 = on alarm, reset timer
#define TMR_ON_ALARM_STOP       3    ; 1 = on alarm, stop timer
                                     ; bits 0-1: alarm sound
; Rate Adjustments
#define TMR_STD_LO              0xC5
#define TMR_STD_HI              0x66

#define TMR_XTR_LO              0x18
#define TMR_XTR_HI              0x80

#define PULSES_PER_TICK         0x20


#define F_TICK                  0    ; tick flag
#define F_BTNDOWN               1    ; 1 if the button has been down from the previous loop, 0 otherwise
#define F_TMR_LABEL_DIRTY       2    ; set if the timer label has to be updated on the display
#define F_VAL_DISPLAY_DIRTY     3    ; set if the value label has to be updated on the display
#define F_CLOCK_STOPPED         4    ; set if the main clock is stopped
#define F_CURSOR_DIRTY          5    ; set if the cursor position needs to be updated or reset
#define F_VALUE_ENTERED         6    ; set if a new value has been just entered

#define MSG_ID_Clear            0xFF

#define PRECISION               7

#define KBD_SCAN_DELAY          3

#define D_SEL_TIMER             0x01    ; Destination - selected timer
#define D_SEL_TIMER_LENGTH      0x02    ; Destination - selected timer's length
#define D_SEL_TIMER_ALARM       0x03    ; Destination - selected timer's alarm time
#define D_SEL_TIMER_COPY        0x04    ; Destination - selected timer; use value as timer ID to lookup timer value

#define B_LED                   0       ; RA0
#define B_SPK                   1       ; RA1

#define SND_SILENT              0x00    ; no sound
#define SND_PIP                 0x01    ; sound: "pip"
#define SND_PEEP                0x02    ; sound: "peep"
#define SND_WHIP                0x03    ; sound: "whip"


; math macros
M_StorStatus macro WHERE
    movf    STATUS,w
    movwf   WHERE
    endm

M_RetrStatus macro WHERE
    movf    WHERE,w
    movwf   STATUS
    endm

; state machine macros
IfJump macro BTN,ADDR
    movlw   BTN
    subwf   BTN_ID,w
    btfsc   STATUS,Z
    goto    ADDR
    ENDM

; state machine macros
AnyBtnJump macro ADDR
    btfss   BTN_ID,7
    goto    ADDR
    ENDM

IfState macro BTN,STATE
    movlw   BTN
    subwf   BTN_ID,w
    btfss   STATUS,Z
    goto    $+5
    movlw   high STATE
    movwf   STATE_HI
    movlw   low STATE
    movwf   STATE_LO
    ENDM    ; goto here

AnyBtnState macro STATE
    btfsc   BTN_ID,7
    goto    $+5
    movlw   high STATE
    movwf   STATE_HI
    movlw   low STATE
    movwf   STATE_LO
    ENDM    ; goto here

GoToState macro STATE
    movlw   high STATE
    movwf   STATE_HI
    movlw   low STATE
    movwf   STATE_LO
    ENDM

SetMsg macro NEW_MSG_ID
    movlw   NEW_MSG_ID
    movwf   MSG_ID
    ENDM


    cblock 0x20
    ; main timers registers
    LOOP_TIMER
    FLAGS
    EE_ADDR
    EE_DATA
    CTR1
    CTR2
    TMP
    TMP1
    TMP2
    TMP3
    REG
    ; selected timer
    SEL_TMR
    SEL_TMR_SETTINGS
    SEL_TMR_LEN:4
    ; alarm variables
    ALARM           ; 0 if alarm is off, alarm ID otherwise
    ALARM_TIMER     ; counts the ticks untill the alarm switches off
    ; button press data
    BTN_ID
    BTN_DOWN
    ; state machine registers
    STATE_LO
    STATE_HI
    ; display registers
    LCD_CURSOR_POSITION ; cursor position (bits 0-6) and status (bit 7: 1=on; 0=off)
    DISP:6
    MSGL
    MSGH
    MSGDAT
    MSG_ID              ; ID of the message that is to be displayed on the second row
    MSG_ID_CURRENT      ; ID of the message currently displayed on the second row
    ; math registers
    REG_X:PRECISION
    REG_Y:PRECISION
    REG_Z:PRECISION
    REG_COUNTER
    REG_STATUS
    REG_T1
    REG_T2
    REG_ROT_COUNTER
    ; value enter registers
    VAL_STAT            ; status - destination of the value that is entered (later may be used to store also format in top nibble)
    VAL_IND             ; index - the index of the digit being currently entered
    VAL_DIGIT           ; place to store a single digit while other operations are performed
    VAL:6               ; the value itself (can be in decimal presentation or directly in hex)
    endc

    ; interrupt registers in shared space
    cblock 0x78
    INT_W
    INT_STATUS
    INT_PCLATH
    INT_FSR
    INT_PULSECTR
    INT_TIMER
    endc

    cblock 0xA0
    TIM:16*4
    endc

    cblock 0x120
    LEN:16*4
    endc

    cblock 0x1A0
    ALM:16*4
    endc

    cblock 0x1E0
    TMR_SETTINGS:16*1
    endc 

    org 0x00
    goto    Start
    org 0x04
    nop        ; interrupt code follows - avoid potential paging issues

Interrupt
    movwf   INT_W
    swapf   STATUS,W
    clrf    STATUS
    movwf   INT_STATUS
    movf    PCLATH,w
    movwf   INT_PCLATH
    clrf    PCLATH
    movf    FSR,w
    movwf   INT_FSR
    ;
    banksel T1CON    ; stop timer
    bcf     T1CON,TMR1ON
    banksel TMR1H    ; set TMR1 counter
    decf    INT_PULSECTR,f
    btfss   STATUS,Z
    goto    Interrupt_std_len
    movlw   TMR_XTR_LO
    movwf   TMR1L
    movlw   TMR_XTR_HI
    movwf   TMR1H
    movlw   PULSES_PER_TICK
    movwf   INT_PULSECTR
    incf    INT_TIMER,f
    goto    Interrupt_continue
Interrupt_std_len
    movlw   TMR_STD_LO
    movwf   TMR1L
    movlw   TMR_STD_HI
    movwf   TMR1H
Interrupt_continue
    banksel PIR1    ; clear TMR1 overflow bit
    bcf     PIR1,TMR1IF
    banksel PIE1    ; enable TMR1 interrupt
    bsf     PIE1,TMR1IE
    banksel T1CON    ; start timer
    bsf     T1CON,TMR1ON
    banksel PORTC
    ;
    movf    INT_FSR,w
    movwf   FSR
    movf    INT_PCLATH,w
    movwf   PCLATH
    swapf   INT_STATUS,w
    movwf   STATUS
    swapf   INT_W,F
    swapf   INT_W,W
    retfie

TMR_Init
    banksel INTCON
    bsf     INTCON,PEIE
    bsf     INTCON,GIE
    banksel T1CON
    movlw   b'00000000'
    banksel PORTC
    movlw   PULSES_PER_TICK
    movwf   INT_PULSECTR
    banksel PIR1    ; clear TMR1 overflow bit
    bcf     PIR1,TMR1IF
    banksel PIE1    ; enable TMR1 interrupt
    bsf     PIE1,TMR1IE
    banksel T1CON    ; start timer
    bsf     T1CON,TMR1ON
    banksel PORTC
    return

SM_Init
    movlw   low S00_TimerSelectLoop
    movwf   STATE_LO
    movlw   high S00_TimerSelectLoop
    movwf   STATE_HI
    return

Start
    clrf    FLAGS
    clrf    INT_TIMER
    clrf    LOOP_TIMER
    clrf    LCD_CURSOR_POSITION
    call    EEPROM_CheckInit
    call    PORTS_Init
    call    LCD_Init
    call    SM_Init
    call    ReadTimerDataFromEEPROM
    clrf    SEL_TMR
    call    LCD_UpdateTimerLabel
    call    TMR_Init
    call    SPK_Whip

MainLoop
    call    IfPlaySound
    btfsc   FLAGS,F_VALUE_ENTERED
    call    ProcessEnteredValue
    btfsc   FLAGS,F_TMR_LABEL_DIRTY
    call    LCD_UpdateTimerLabel
    movf    MSG_ID,w
    subwf   MSG_ID_CURRENT,w
    btfss   STATUS,Z
    call    LCD_UpdateSecondRowMessage
    btfsc   FLAGS,F_VAL_DISPLAY_DIRTY
    call    LCD_UpdateValDisplay
    call    IfTick
    call    KBD_Scan
    movf    STATE_HI,w
    movwf   PCLATH
    movf    STATE_LO,w
    movwf   PCL             ; go to the state machine
SM_Rtn                      ; return point of the state machine
    movlw   high SM_Rtn
    movwf   PCLATH
    movlw   0xFF
    movwf   BTN_ID
    btfsc   FLAGS,F_CURSOR_DIRTY
    call    LCD_UpdateCursor
    btfsc   FLAGS,F_TICK
    goto    MainLoop_Tick
    clrf    TMP
MainLoop_Delay              ; keyboard debounce
    decf    TMP,f
    nop
    nop
    btfss   STATUS,Z
    goto    MainLoop_Delay
    goto    MainLoop
MainLoop_Tick
    movf    SEL_TMR,w
    movwf   CTR1
    call    LoadSelTmrSettingsAndLen
    call    MC_Normalize
    call    MC_NormalizedTo86400
    call    MC_86400toHMS
    call    LCD_UpdateTimer
    movf    MSG_ID,w
    sublw   MSG_ID_Clear
    btfss   STATUS,Z
    goto    MainLoop
    movf    SEL_TMR,w
    movwf   CTR1
    call    LoadSelTmrSettingsAndLen
    call    MC_Normalize
    call    MC_NormalizedToJulianNormalized
    call    MC_NormalizedToDecimal
    call    LCD_UpdateTimerDecimalPt
    bcf     FLAGS,F_TICK
    goto    MainLoop

ProcessEnteredValue
    bcf     FLAGS,F_VALUE_ENTERED
    movf    VAL_STAT,w
    sublw   D_SEL_TIMER_LENGTH 
    btfsc   STATUS,Z
    goto    ProcessEnteredValue_STLEN
    movf    VAL_STAT,w
    sublw   D_SEL_TIMER
    btfsc   STATUS,Z
    goto    ProcessEnteredValue_ST
    movf    VAL_STAT,w
    sublw   D_SEL_TIMER_ALARM
    btfsc   STATUS,Z
    goto    ProcessEnteredValue_STALM
    sublw   D_SEL_TIMER_COPY
    btfsc   STATUS,Z
    goto    ProcessEnteredValue_COPY
    return


ProcessEnteredValue_STALM
    bsf     STATUS,IRP              ; copy VAL to the alarm

ProcessEnteredValue_ST
    movf    REG,w                   ; copy VAL to the timer  (or alarm, if STATUS,IRP is set)
    movwf   FSR
    movf    VAL,w
    movwf   INDF
    incf    FSR,f
    movf    VAL+1,w
    movwf   INDF
    incf    FSR,f
    movf    VAL+2,w
    movwf   INDF
    incf    FSR,f
    movf    VAL+3,w
    movwf   INDF
    bcf     STATUS,IRP
    return

ProcessEnteredValue_COPY
    ; NOT IMPLEMENTED IN THIS VERSION
    ;
    ; - get timer ID from hour data
    ; - load timer value
    ; - normalize value
    ; - de-normalize value selected timer's range
    ; - copy timer value to selected timer
    return


ProcessEnteredValue_STLEN   ; copy VAL to selected timer length
    movf    SEL_TMR,w
    movwf   CTR1
    call    CalculateEEPROMAddressFromCTR1
    incf    EE_ADDR,f
    call    CalculateFileAddressFromCTR1
    clrf    TMP
ProcessEnteredValue_STLEN_1
    movf    TMP,w
    addlw   VAL
    movwf   FSR
    movf    INDF,w
    movwf   VAL_DIGIT
    bsf     STATUS,IRP
    movf    REG,w
    andlw   0x7F
    addwf   TMP,w
    movwf   FSR
    movf    VAL_DIGIT,w
    movwf   INDF
    bcf     STATUS,IRP
    movwf   EE_DATA
    call    EEPROM_Write
    incf    EE_ADDR,f
    incf    TMP,f
    movf    TMP,w
    sublw   0x04
    btfss   STATUS,Z
    goto    ProcessEnteredValue_STLEN_1
    call    LoadSelTmrSettingsAndLen
    return

PORTS_Init
    banksel PORTA
    clrf    PORTA
    clrf    PORTB
    clrf    PORTC
    clrf    PORTD
    clrf    PORTE
    banksel TRISA
    movlw   b'00000110'
    movwf   ADCON1
    movlw   b'11111100'     ; RA0=LED, RA1=SPK
    movwf   TRISA
    movlw   b'11110000'     ; KBD
    movwf   TRISB
    bcf     OPTION_REG,7;   ; enable PORTB weak pull-ups
    clrf    TRISC           ; LCD: DB0-DB7
    movlw   0xFF
    movwf   TRISD           ; not used - all inputs
    movlw   b'0000000'      ; LCD: RE0=RS, RE1=R/W; RE2=E
    movwf   TRISE
    movlw   b'11000000'     ; enable interrupts
    movwf   INTCON
    banksel PORTA
    return

CalculateEEPROMAddressFromCTR1
    ; Calculate timer data address in EEPROM
    movf    CTR1,w
    movwf   EE_ADDR
    bcf     STATUS,C
    rlf     EE_ADDR,f
    rlf     EE_ADDR,f
    rlf     EE_ADDR,f
    addwf   EE_ADDR,f
    return

CalculateFileAddressFromCTR1
    ; Timer offsett address in REG
    movf    CTR1,w
    movwf   REG
    bcf     STATUS,C
    rlf     REG,f
    rlf     REG,f
    movlw   TIM
    addwf   REG,f
    return

ReadTimerDataFromEEPROM
    clrf    CTR1
ReadTimerDataFromEEPROM_loop

    call    CalculateEEPROMAddressFromCTR1
    call    CalculateFileAddressFromCTR1
    
    ; Load timer settings
    movf    CTR1,w
    addlw   low TMR_SETTINGS
    movwf   FSR
    bsf     STATUS,IRP
    call    EEPROM_Read
    movwf   INDF

    ; Load lengths
    incf    EE_ADDR,f
    movf    REG,w
    andlw   0x7F
    movwf   FSR
    call    EEPROM_Read
    movwf   INDF
    incf    EE_ADDR,f
    incf    FSR,f
    call    EEPROM_Read
    movwf   INDF
    incf    EE_ADDR,f
    incf    FSR,f
    call    EEPROM_Read
    movwf   INDF
    incf    EE_ADDR,f
    incf    FSR,f
    call    EEPROM_Read
    movwf   INDF
    bcf     STATUS,IRP
    
    ; Load offsetts (initial values)
    incf    EE_ADDR,f
    movf    REG,w
    movwf   FSR
    call    EEPROM_Read
    movwf   INDF
    incf    EE_ADDR,f
    incf    FSR,f
    call    EEPROM_Read
    movwf   INDF
    incf    EE_ADDR,f
    incf    FSR,f
    call    EEPROM_Read
    movwf   INDF
    incf    EE_ADDR,f
    incf    FSR,f
    call    EEPROM_Read
    movwf   INDF

    incf    CTR1,f
    movf    CTR1,w
    sublw   0x10
    btfss   STATUS,Z
    goto    ReadTimerDataFromEEPROM_loop
    return


EEPROM_CheckInit
    movlw   0xFE
    movwf   EE_ADDR
    call    EEPROM_Read
    movlw   0xA5
    subwf   EE_DATA,w
    btfss   STATUS,Z
    goto    InitEEPROM
    incf    EE_ADDR,f
    call    EEPROM_Read
    movlw   0x5A
    subwf   EE_DATA,w
    btfsc   STATUS,Z
    return
InitEEPROM
    clrf    EE_ADDR
    clrf    CTR1
    clrf    CTR2
InitEEPROM_loop

    movf    CTR2,w
    lcall   LOOKUP_StdTmr
    movwf   EE_DATA
    movlw   high InitEEPROM_loop
    movwf   PCLATH
    call    EEPROM_Write
    incf    EE_ADDR,f
    incf    CTR2,f
    movf    CTR2,w
    sublw   0x09
    btfss   STATUS,Z
    goto    InitEEPROM_loop
    clrf    CTR2
    incf    CTR1,f
    movf    CTR1,w
    sublw   0x10
    btfss   STATUS,Z
    goto    InitEEPROM_loop
    movlw   0xFE
    movwf   EE_ADDR
    movlw   0xA5
    movwf   EE_DATA
    call    EEPROM_Write
    incf    EE_ADDR,f
    movlw   0x5A
    movwf   EE_DATA
    call    EEPROM_Write
    return

EEPROM_Read
    banksel EE_ADDR
    movf    EE_ADDR,w
    banksel EEADR
    movwf   EEADR
    banksel EECON1
    bcf     EECON1, EEPGD
    bsf     EECON1, RD
    banksel EEDATA
    movf    EEDATA,w
    banksel EE_DATA
    movwf   EE_DATA
    return

EEPROM_Write
    banksel EE_ADDR
    movf    EE_ADDR,w
    banksel EEADR
    movwf   EEADR
    banksel EE_DATA
    movf    EE_DATA,w
    banksel EEDATA
    movwf   EEDATA
    banksel EECON1
    bcf     EECON1, EEPGD
    bsf     EECON1, WREN
    bcf     INTCON, GIE
    movlw   0x55
    movwf   EECON2
    movlw   0xAA
    movwf   EECON2
    bsf     EECON1, WR
    bsf     INTCON, GIE
EEPROM_Write_WaitForConfirmation
    btfsc   EECON1, WR
    goto    EEPROM_Write_WaitForConfirmation
    bcf     EECON1, WREN
    banksel EE_ADDR
    return


IfTick
    movf    INT_TIMER,w
    btfss   FLAGS,F_CLOCK_STOPPED
    goto    IfTick_Continue
    movwf   LOOP_TIMER
    return
IfTick_Continue
    subwf   LOOP_TIMER,w
    btfsc   STATUS,Z
    return    
Tick                        ; called 4 times per second
    incf    LOOP_TIMER,f
    movlw   0x01
    xorwf   PORTA,f
    clrf    CTR1
Tick_next_ctr
    call    LoadSelTmrSettingsAndLen
    btfss   SEL_TMR_SETTINGS,TMR_ON
    goto    Tick_continue
    btfss   SEL_TMR_SETTINGS,TMR_DIR
    goto    Tick_increase
; Tick_decrease
    call    DecTimer
    goto    Tick_checkAlarm
Tick_increase
    call    IncTimer
Tick_checkAlarm
    call    CheckTimerAlarm
Tick_continue
    incf    CTR1,f
    movlw   0x10
    subwf   CTR1,w
    btfss   STATUS,Z
    goto    Tick_next_ctr
    bsf     FLAGS,F_TICK
    return

LoadSelTmrSettingsAndLen   ; loads selected timer settings and length to SEL_TMR_SETTINGS and SEL_TMR_LEN
    ; load settings
    movf    CTR1,w
    addlw   low TMR_SETTINGS
    movwf   FSR
    bsf     STATUS,IRP
    movf    INDF,w
    movwf   SEL_TMR_SETTINGS
    ; load length
    call    CalculateFileAddressFromCTR1
    movf    REG,w
    andlw   0x7F
    movwf   FSR
    movf    INDF,w
    movwf   SEL_TMR_LEN
    incf    FSR,f
    movf    INDF,w
    movwf   SEL_TMR_LEN+1
    incf    FSR,f
    movf    INDF,w
    movwf   SEL_TMR_LEN+2
    incf    FSR,f
    movf    INDF,w
    movwf   SEL_TMR_LEN+3
    bcf     STATUS,IRP
    return

IncTimer                        ; increment a timer;  make sure that LoadSelTmrLen is called before
    movf    REG,w
    movwf   FSR
    incf    INDF,f
    btfss   STATUS,Z
    goto    IncTimer_check
    incf    FSR,f
    incf    INDF,f
    btfss   STATUS,Z
    goto    IncTimer_check
    incf    FSR,f
    incf    INDF,f
    btfss   STATUS,Z
    goto    IncTimer_check
    incf    FSR,f
    incf    INDF,f
IncTimer_check
    movf    REG,w
    movwf   FSR
    movf    INDF,w
    subwf   SEL_TMR_LEN,w
    btfss   STATUS,Z
    return
    incf    FSR,f
    movf    INDF,w
    subwf   SEL_TMR_LEN+1,w
    btfss   STATUS,Z
    return
    incf    FSR,f
    movf    INDF,w
    subwf   SEL_TMR_LEN+2,w
    btfss   STATUS,Z
    return
    incf    FSR,f
    movf    INDF,w
    subwf   SEL_TMR_LEN+3,w
    btfss   STATUS,Z
    return
    clrf    INDF
    decf    FSR,f
    clrf    INDF
    decf    FSR,f
    clrf    INDF
    decf    FSR,f
    clrf    INDF
    return
    
DecTimer                        ; decrement a timer;  make sure that LoadSelTmrLen is called before
    movf    REG,w
    movwf   FSR
    movf    INDF,f
    btfss   STATUS,Z
    goto    DecTimer_continue
    incf    FSR,f
    movf    INDF,f
    btfss   STATUS,Z
    goto    DecTimer_continue
    incf    FSR,f
    movf    INDF,f
    btfss   STATUS,Z
    goto    DecTimer_continue
    incf    FSR,f
    movf    INDF,f
    btfss   STATUS,Z
    goto    DecTimer_continue
    movf    SEL_TMR_LEN+3,w
    movwf   INDF
    decf    FSR,f
    movf    SEL_TMR_LEN+2,w
    movwf   INDF
    decf    FSR,f
    movf    SEL_TMR_LEN+1,w
    movwf   INDF
    decf    FSR,f
    movf    SEL_TMR_LEN,w
    movwf   INDF
DecTimer_continue 
    movf    REG,w
    movwf   FSR
    decf    INDF,f
    movlw   0xFF
    subwf   INDF,w
    btfss   STATUS,Z
    return
    incf    FSR,f
    decf    INDF,f
    movlw   0xFF
    subwf   INDF,w
    btfss   STATUS,Z
    return
    incf    FSR,f
    decf    INDF,f
    movlw   0xFF
    subwf   INDF,w
    btfss   STATUS,Z
    return
    incf    FSR,f
    decf    INDF,f
    return

CheckTimerAlarm
    movlw   0x04
    movwf   TMP
    movf    REG,w
    movwf   FSR
CheckTimerAlarm_0
    bsf     STATUS,IRP
    movf    INDF,w
    bcf     STATUS,IRP
    subwf   INDF,w
    btfss   STATUS,Z
    return                      ; no alarm
    incf    FSR,f
    decf    TMP,f
    btfss   STATUS,Z
    goto    CheckTimerAlarm_0
    movf    SEL_TMR_SETTINGS,w
    andlw   0x03
    movwf   ALARM
    clrf    ALARM_TIMER
    btfss   SEL_TMR_SETTINGS,TMR_ON_ALARM_STOP
    goto    CheckTimerAlarm_1
    ; Stop timer on alarm
    ;movlw   0x01<<TMR_ON
    ;lcall   ToggleTimerSettings
    movf    CTR1,w
    addlw   low TMR_SETTINGS
    movwf   FSR
    bsf     STATUS,IRP
    movlw   0x01<<TMR_ON
    xorwf   INDF,f
    bcf     STATUS,IRP
CheckTimerAlarm_1
    btfss   SEL_TMR_SETTINGS,TMR_ON_ALARM_RESET
    return
    ; Reset timer on alarm
    ;movf    SEL_TMR,w
    ;movwf   CTR1
    call    CalculateFileAddressFromCTR1
    movf    REG,w
    movwf   FSR
    movlw   0x01
    movwf   INDF
    incf    FSR,f
    clrf    INDF
    incf    FSR,f
    clrf    INDF
    incf    FSR,f
    clrf    INDF
    return

IfPlaySound
    movlw   SND_PIP
    subwf   ALARM,w
    btfsc   STATUS,Z
    goto    PlayPip
    movlw   SND_PEEP
    subwf   ALARM,w
    btfsc   STATUS,Z
    goto    PlayPeep
    movlw   SND_WHIP
    subwf   ALARM,w
    btfsc   STATUS,Z
    goto    PlayWhip
PlayContinue
    clrf    ALARM
    return

PlayPip
    lcall   SPK_Pip
    goto   PlayContinue
    
PlayPeep
    lcall   SPK_Peep
    goto   PlayContinue
    
PlayWhip
    lcall   SPK_Whip
    goto   PlayContinue
    

ToggleTimerSettings             ; toggle a timer settings byte
    movwf   TMP
    movf    SEL_TMR,w
    addlw   low TMR_SETTINGS
    movwf   FSR
    bsf     STATUS,IRP
    movf    TMP,w
    xorwf   INDF,f
    bcf     STATUS,IRP
    return


CopyDispToVal                   ; copy the currently displayed timer value to VAL
    movf    DISP,w
    movwf   VAL
    movf    DISP+1,w
    movwf   VAL+1
    movf    DISP+2,w
    movwf   VAL+2
    movf    DISP+3,w
    movwf   VAL+3
    movf    DISP+4,w
    movwf   VAL+4
    movf    DISP+5,w
    movwf   VAL+5
    return



M_CLR                           ; clear a register
    movwf   FSR
    movlw   PRECISION
    movwf   REG_COUNTER
M_CLR_loop
    clrf    INDF
    incf    FSR,f
    decf    REG_COUNTER,f
    btfss   STATUS,Z
    goto    M_CLR_loop
    return


M_ROL                           ; rotate a register to the left
    movwf   FSR
    M_StorStatus REG_STATUS
    clrf    REG_COUNTER
M_ROL_loop
    M_RetrStatus REG_STATUS
    rlf     INDF,f
    M_StorStatus REG_STATUS
    incf    FSR,f
    incf    REG_COUNTER,f
    movlw   PRECISION
    subwf   REG_COUNTER,w
    btfss   STATUS,Z
    goto    M_ROL_loop
    return


M_ROR                           ; rotates a register to the right
    movwf   FSR
    movlw   PRECISION-1
    addwf   FSR,f
    M_StorStatus REG_STATUS
    clrf    REG_COUNTER
M_ROR_loop
    M_RetrStatus REG_STATUS
    rrf     INDF,f
    M_StorStatus REG_STATUS
    decf    FSR,f
    incf    REG_COUNTER,f
    movlw   PRECISION
    subwf   REG_COUNTER,w
    btfss   STATUS,Z
    goto    M_ROR_loop
    return


M_CMP                           ; Z <=> X -> STATUS(C,Z)
                                ; STATUS,C set if Z => X;
                                ; STATUS,Z set if Z == X
    clrf    REG_COUNTER
M_CMP_loop
    movf    REG_COUNTER,w
    sublw   REG_Z+PRECISION-1
    movwf   FSR
    movf    INDF,w
    movwf   REG_T1
    movf    REG_COUNTER,w
    sublw   REG_X+PRECISION-1
    movwf   FSR
    movf    INDF,w
    subwf   REG_T1,f
    btfss   STATUS,Z
    return
    incf    REG_COUNTER,f
    movlw   PRECISION
    subwf   REG_COUNTER,w
    btfss   STATUS,Z
    goto    M_CMP_loop
    return


M_INC                           ; increment a register
    movwf   FSR
    movlw   PRECISION
    movwf   REG_COUNTER
M_INC_loop
    incf    INDF,f
    btfss   STATUS,Z
    return
    incf    FSR,f
    decf    REG_COUNTER,f
    btfss   STATUS,Z
    goto    M_INC_loop
    return


M_ADD                           ; Z + X -> Z
    bcf     STATUS,C
    clrf    REG_STATUS
    clrf    REG_COUNTER
M_ADD_loop
    clrf    REG_T1
    btfsc   REG_STATUS,C
    incf    REG_T1,f
    clrf    REG_STATUS
    movlw   REG_X
    addwf   REG_COUNTER,w
    movwf   FSR
    movf    INDF,w
    addwf   REG_T1,f
    btfsc   STATUS,C
    bsf     REG_STATUS,C
    movlw   REG_Z
    addwf   REG_COUNTER,w
    movwf   FSR
    movf    INDF,w
    addwf   REG_T1,f
    btfsc   STATUS,C
    bsf     REG_STATUS,C
    movf    REG_T1,w
    movwf   INDF
    incf    REG_COUNTER,f
    movlw   PRECISION
    subwf   REG_COUNTER,w
    btfss   STATUS,Z
    goto    M_ADD_loop
    return


M_SUB                           ; Z - X -> Z
    clrf    REG_COUNTER
    bsf     REG_STATUS,C
M_SUB_loop
    bsf     REG_T2,C
    movlw   REG_Z
    addwf   REG_COUNTER,w
    movwf   FSR
    movf    INDF,w
    movwf   REG_T1
    movlw   REG_X
    addwf   REG_COUNTER,w
    movwf   FSR
    movf    INDF,w
    subwf   REG_T1,f
    btfss   STATUS,C
    bcf     REG_T2,C
    btfsc   REG_STATUS,C
    goto    M_SUB_no_carry
    movlw   0x01
    subwf   REG_T1,f
    btfss   STATUS,C
    bcf     REG_T2,C
M_SUB_no_carry
    movlw   REG_Z
    addwf   REG_COUNTER,w
    movwf   FSR
    movf    REG_T1,w
    movwf   INDF
    bsf     REG_STATUS,C
    btfss   REG_T2,C
    bcf     REG_STATUS,C
    incf    REG_COUNTER,f
    movlw   PRECISION
    subwf   REG_COUNTER,w
    btfss   STATUS,Z
    goto    M_SUB_loop
    btfss   REG_STATUS,C
    bcf     STATUS,C
    return


M_MUL                           ; X * Y -> Z
    movlw   REG_Z
    call    M_CLR
    movlw   PRECISION*8+1
    movwf   REG_ROT_COUNTER
M_MUL_loop
    decf    REG_ROT_COUNTER,f
    btfsc   STATUS,Z
    return
    btfsc   REG_Y,0
    call    M_ADD
    bcf     STATUS,C
    movlw   REG_Y
    call    M_ROR
    bcf     STATUS,C
    movlw   REG_X
    call    M_ROL
    goto    M_MUL_loop


M_DIV                           ; Z / X -> Y;  remainder -> Z
    movlw   REG_Y
    call    M_CLR
    movlw   PRECISION*8
    movwf   REG_ROT_COUNTER
M_DIV_rot_loop
    btfsc   REG_X+PRECISION-1,7
    goto    M_DIV_loop
    movlw   REG_X
    bcf     STATUS,C
    call    M_ROL
    decf    REG_ROT_COUNTER,f
    btfss   STATUS,Z
    goto    M_DIV_rot_loop
    bsf     STATUS,Z
    return
M_DIV_loop
    call    M_CMP
    M_StorStatus REG_T2
    movlw   REG_Y
    call    M_ROL
    M_RetrStatus REG_T2
    btfsc   STATUS,C
    call    M_SUB
    bcf     STATUS,Z
    bcf     STATUS,C
    movlw   REG_X
    call    M_ROR
    incf    REG_ROT_COUNTER,f
    movlw   PRECISION*8+1
    subwf   REG_ROT_COUNTER,w
    btfss   STATUS,Z
    goto    M_DIV_loop
    return

M_MOV_Y_TO_Z
    movf    REG_Y,w
    movwf   REG_Z
    movf    REG_Y+1,w
    movwf   REG_Z+1
    movf    REG_Y+2,w
    movwf   REG_Z+2
    movf    REG_Y+3,w
    movwf   REG_Z+3
    movf    REG_Y+4,w
    movwf   REG_Z+4
    movf    REG_Y+5,w
    movwf   REG_Z+5
    movf    REG_Y+6,w
    movwf   REG_Z+6
    return

M_MOV_Z_TO_Y
    movf    REG_Z,w
    movwf   REG_Y
    movf    REG_Z+1,w
    movwf   REG_Y+1
    movf    REG_Z+2,w
    movwf   REG_Y+2
    movf    REG_Z+3,w
    movwf   REG_Y+3
    movf    REG_Z+4,w
    movwf   REG_Y+4
    movf    REG_Z+5,w
    movwf   REG_Y+5
    movf    REG_Z+6,w
    movwf   REG_Y+6
    return

M_MOV_Z_TO_VAL
    movf    REG_Z,w
    movwf   VAL
    movf    REG_Z+1,w
    movwf   VAL+1
    movf    REG_Z+2,w
    movwf   VAL+2
    movf    REG_Z+3,w
    movwf   VAL+3
    movf    REG_Z+4,w
    movwf   VAL+4
    movf    REG_Z+5,w
    movwf   VAL+5
    return

M_MOV_VAL_TO_X
    movf    VAL,w
    movwf   REG_X
    movf    VAL+1,w
    movwf   REG_X+1
    movf    VAL+2,w
    movwf   REG_X+2
    movf    VAL+3,w
    movwf   REG_X+3
    movf    VAL+4,w
    movwf   REG_X+4
    movf    VAL+5,w
    movwf   REG_X+5
    clrf    REG_X+6
    return

KBD_Scan
    ; If button is down, returns its ID in the W register
    ; If no button is down, returns 0xFF in the W register and Z=1
    ; If more than one btn is down, returns 0xFE in W and Z=1
    movlw   0x01
    movwf   CTR1    ; row index
KBD_Scan_NextRow
    movlw   0xFF
    xorwf   CTR1,w
    movwf   PORTB

    movlw   KBD_SCAN_DELAY
    movwf   TMP
KBD_Delay_0
    decf    TMP,f
    btfss   STATUS,Z
    goto    KBD_Delay_0

    movlw   0xF0
    andwf   PORTB,w
    xorlw   0xF0
    btfss   STATUS,Z
    goto    KBD_Scan_FoundKeyDown
    bcf     STATUS,C
    rlf     CTR1,f
    movlw   0x0F
    andwf   CTR1,f
    btfss   STATUS,Z
    goto    KBD_Scan_NextRow
    goto    KBD_Scan_ReturnNone
KBD_Scan_FoundKeyDown
    movwf   CTR2    ; col index
    swapf   CTR2,f
    clrf    BTN_DOWN
    movf    CTR1,w
    call    KBD_IndexToID
    btfsc   STATUS,Z
    goto    KBD_Scan_ReturnInvalid
    movwf   BTN_DOWN
    bcf     STATUS,C
    rlf     BTN_DOWN,f
    bcf     STATUS,C
    rlf     BTN_DOWN,f
    movf    CTR2,w
    call    KBD_IndexToID
    btfsc   STATUS,Z
    goto    KBD_Scan_ReturnInvalid
    xorwf   BTN_DOWN,f
    movf    BTN_DOWN,w
    btfss   FLAGS,F_BTNDOWN
    movwf   BTN_ID
    bsf     FLAGS,F_BTNDOWN
    bcf     STATUS,Z
    return
KBD_Scan_ReturnInvalid
    movlw   0xFE
    goto    KBD_Scan_ReturnEnd
KBD_Scan_ReturnNone
    bcf     FLAGS,F_BTNDOWN
    movlw   0xFF
KBD_Scan_ReturnEnd
    movwf   BTN_DOWN
    bsf     STATUS,Z
    return

KBD_IndexToID
    ; Converts the lower 4 bits of the W register to 2-bit ID;
    ; Sets Z flag if invalid
    movwf   TMP
    movlw   0x0F
    andwf   TMP,f
    clrf    TMP1
    movlw   0x01
    movwf   TMP2
KBD_IndexToID_0
    movf    TMP2,w
    subwf   TMP,w
    btfsc   STATUS,Z
    goto    KBD_IndexToID_ReturnID
    incf    TMP1,f
    movlw   0x0F
    andwf   TMP2,w
    btfsc   STATUS,Z
    goto    KBD_IndexToID_ReturnInvalid
    bcf     STATUS,C
    rlf     TMP2,f
    goto    KBD_IndexToID_0
KBD_IndexToID_ReturnInvalid
    movlw   0xFF
    bsf     STATUS,Z
    return
KBD_IndexToID_ReturnID
    movf    TMP1,w
    bcf     STATUS,Z
    return

; Before calling this make sure timer data and length are
; loaded by calling LoadSelTmrSettingsAndLen
MC_Normalize
    ; timer value -> REG_Z
    movlw   REG_Z
    call    M_CLR
    movlw   REG_X
    call    M_CLR
MC_Normalize_NO_CLR
    movf    REG,w
    movwf   FSR
    movf    INDF,w
    movwf   REG_Z+3
    incf    FSR,f
    movf    INDF,w
    movwf   REG_Z+4
    incf    FSR,f
    movf    INDF,w
    movwf   REG_Z+5
    incf    FSR,f
    movf    INDF,w
    movwf   REG_Z+6
    bcf     STATUS,IRP
    ; length -> REG_X
    movf    SEL_TMR_LEN,w
    movwf   REG_X
    movf    SEL_TMR_LEN+1,w
    movwf   REG_X+1
    movf    SEL_TMR_LEN+2,w
    movwf   REG_X+2
    movf    SEL_TMR_LEN+3,w
    movwf   REG_X+3
    ; normalized value [0, 1) -> REG_Y
    goto    M_DIV
    ;return

; Normalized value in REG_Y -> REG_Z
MC_NormalizedToJulianNormalized
    call    M_MOV_Y_TO_Z
    ; 0.5 in REG_X
    movlw   REG_X
    call    M_CLR
    movlw   0x80
    movwf   REG_X+2
    ; REG_X + REG_Z -> REG_Z
    call    M_ADD
    ; REG_Z -> REG_Y
    goto    M_MOV_Z_TO_Y

; Before callind this, make sure you have called MC_Normalize
; to get the [0, 1) value
MC_NormalizedTo86400
    ; 86400 ->: REG_X
    movlw   REG_X
    call    M_CLR
    movlw   0x46
    movwf   REG_X+1
    movlw   0x05
    movwf   REG_X+2
    ; multiply norm. value (stored in REG_Y) * REG_X
    goto    M_MUL
    ;return

; Before calling this, make sure you have called MC_NormalizedTo86400
MC_86400toHMS
    movlw   0x0A
    movwf   DISP
    movwf   DISP+2
    movwf   DISP+4
    movlw   0x06
    movwf   DISP+1
    movwf   DISP+3
    movwf   DISP+5
    movlw   REG_X
    call    M_CLR
    movlw   0x04
    movwf   REG_X+3
    call    M_DIV
    movlw   DISP
    movwf   CTR1
MC_86400toHMS_loop
    call    M_MOV_Y_TO_Z
    movlw   REG_X
    call    M_CLR
    movf    CTR1,w
    movwf   FSR
    movf    INDF,w
    movwf   REG_X
    call    M_DIV
    movf    CTR1,w
    movwf   FSR
    movf    REG_Z,w
    movwf   INDF
    incf    CTR1,f
    movlw   DISP+6
    subwf   CTR1,w
    btfss   STATUS,Z
    goto    MC_86400toHMS_loop
    return

MC_HMSto86400t4     ; VAL(HH:MM:SS) -> REG_Z(86400t4)
    movlw   0x0A
    movwf   DISP
    movwf   DISP+2
    movwf   DISP+4
    movlw   0x06
    movwf   DISP+1
    movwf   DISP+3
    movlw   REG_Y
    call    M_CLR
    movlw   0x04
    movwf   CTR1
    movf    VAL+5,w
    movwf   REG_Y
MC_HMSto86400t4_loop
    movlw   REG_X
    call    M_CLR
    movlw   DISP
    addwf   CTR1,w
    movwf   FSR
    movf    INDF,w
    movwf   REG_X
    call    M_MUL
    movlw   REG_Y
    call    M_CLR
    movlw   VAL
    addwf   CTR1,w
    movwf   FSR
    movf    INDF,w
    movwf   REG_X
    call    M_ADD
    call    M_MOV_Z_TO_Y
    decf    CTR1,f
    movlw   0xFF
    subwf   CTR1,w
    btfss   STATUS,Z
    goto    MC_HMSto86400t4_loop
    movlw   REG_X
    call    M_CLR
    movlw   0x04
    movwf   REG_X
    call    M_MUL
    movlw   REG_Z
    call    M_INC
    movlw   REG_Z
    call    M_INC
    return

MC_86400toNormalized        ; REG_Z(86400t4) -> REG_Y(normalized)
    movf    REG_Z+3,w
    movwf   REG_Z+6
    movf    REG_Z+2,w
    movwf   REG_Z+5
    movf    REG_Z+1,w
    movwf   REG_Z+4
    movf    REG_Z,w
    movwf   REG_Z+3
    clrf    REG_Z+2
    clrf    REG_Z+1
    clrf    REG_Z
    movlw   REG_X
    call    M_CLR
    movlw   0x46
    movwf   REG_X+1
    movlw   0x05
    movwf   REG_X+2
    call    M_DIV
    return

MC_NormalizedToLength       ; REG_Y(normalized) -> REG_Z(sel_timer_length_value, +3 bytes offsett)
    movlw   REG_X
    call    M_CLR
    movf    SEL_TMR_LEN,w
    movwf   REG_X
    movf    SEL_TMR_LEN+1,w
    movwf   REG_X+1
    movf    SEL_TMR_LEN+2,w
    movwf   REG_X+2
    movf    SEL_TMR_LEN+3,w
    movwf   REG_X+3
    call    M_MUL
    return

MC_NormalizedToDecimal
    clrf    REG_Y+3
    clrf    REG_Y+4
    clrf    REG_Y+5
    clrf    REG_Y+6
    movlw   REG_X
    call    M_CLR
    movlw   0x0A
    movwf   REG_X
    call    M_MUL
    movf    REG_Z+3,w
    movwf   DISP+5
    clrf    REG_Z+3
    call    M_MOV_Z_TO_Y
    movlw   0x0A
    movwf   REG_X
    call    M_MUL
    movf    REG_Z+3,w
    movwf   DISP+4
    clrf    REG_Z+3
    call    M_MOV_Z_TO_Y
    movlw   0x0A
    movwf   REG_X
    call    M_MUL
    movf    REG_Z+3,w
    movwf   DISP+3
    clrf    REG_Z+3
    call    M_MOV_Z_TO_Y
    movlw   0x0A
    movwf   REG_X
    call    M_MUL
    movf    REG_Z+3,w
    movwf   DISP+2
    clrf    REG_Z+3
    call    M_MOV_Z_TO_Y
    movlw   0x0A
    movwf   REG_X
    call    M_MUL
    movf    REG_Z+3,w
    movwf   DISP+1
    clrf    REG_Z+3
    call    M_MOV_Z_TO_Y
    movlw   0x0A
    movwf   REG_X
    call    M_MUL
    movf    REG_Z+3,w
    movwf   DISP
    clrf    REG_Z+3
    call    M_MOV_Z_TO_Y
    return

LCD_Init
    call    LCD_Wait
    call    LCD_Wait
    call    LCD_Wait
    call    LCD_Wait
    call    LCD_Wait
    call    LCD_Wait
    movlw   0x00
    movwf   PORTE
    movlw   b'00110000'
    movwf   PORTC
    call    LCD_PulseZ
    call    LCD_Wait
    call    LCD_Wait
    call    LCD_Wait
    call    LCD_Wait
    call    LCD_Wait
    movlw   b'00111000'
    movwf   PORTC
    call    LCD_PulseZ
    call    LCD_Wait
    call    LCD_Wait
    call    LCD_Wait
    call    LCD_Wait
    call    LCD_Wait
    movlw   b'00111000'
    movwf   PORTC
    call    LCD_PulseZ
    call    LCD_Wait
    call    LCD_Wait
    call    LCD_Wait
    call    LCD_Wait
    call    LCD_Wait
    movlw   b'00111000'
    movwf   PORTC
    call    LCD_PulseZ
    movlw   b'00001000'
    movwf   PORTC
    call    LCD_PulseZ
    movlw   b'00000001'
    movwf   PORTC
    call    LCD_PulseZ
    movlw   b'00000110'
    movwf   PORTC
    call    LCD_PulseZ
    movlw   b'00001000'
    movwf   PORTC
    call    LCD_PulseZ
    movlw   b'00000010'
    movwf   PORTC
    call    LCD_PulseZ
    ;movlw   b'10000000'
    ;movwf   PORTC
    ;call    LCD_PulseZ
    ;movlw   b'00010100'
    ;movwf   PORTC
    ;call    LCD_PulseZ
    ;movlw   b'10000000'
    ;movwf   PORTC
    ;call    LCD_PulseZ
    bsf     PORTE,0
    movlw   b'00001100'
    call    LCD_SendCMD
    movlw   MSG_ID_Clear
    movwf   MSG_ID
    movwf   MSG_ID_CURRENT
    return

LCD_SendDAT
    movwf   PORTC
    bsf     PORTE,0
    call    LCD_PulseZ
    return

LCD_SendCMD
    movwf   PORTC
    bcf     PORTE,0
    call    LCD_PulseZ
    bsf     PORTE,0
    return


LCD_PulseZ
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    bsf     PORTE,2
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    bcf     PORTE,2
    call    LCD_Wait
    return

LCD_Wait
    movlw   0x10
    movwf   CTR1
    movlw   0x01
    movwf   TMP
LCD_Wait_0
    decfsz  TMP, f
    goto    $+2
    decfsz  CTR1, f
    goto    LCD_Wait_0
    return


LCD_UpdateSecondRowMessage
    movlw   LCDCMD_SET_DD_RAM | 0x40
    call    LCD_SendCMD
    movlw   0x10
    movwf   CTR2
    movf    MSG_ID,w
    movwf   MSG_ID_CURRENT
    sublw   MSG_ID_Clear
    btfsc   STATUS,Z
    goto    LCD_UpdateSecondRowMessage_ClEOL
    movf    MSG_ID,w
    call    MSG_StartRead
LCD_UpdateSecondRowMessage_Loop
    call    MSG_NextChar
    btfsc   STATUS,Z
    goto    LCD_UpdateSecondRowMessage_ClEOL
    call    LCD_SendDAT
    decf    CTR2,f
    btfss   STATUS,Z
    goto    LCD_UpdateSecondRowMessage_Loop
    return
LCD_UpdateSecondRowMessage_ClEOL
    movlw   LCD_SPACE
    call    LCD_SendDAT
    decf    CTR2,f
    btfss   STATUS,Z
    goto    LCD_UpdateSecondRowMessage_ClEOL
    bsf     FLAGS,F_CURSOR_DIRTY
    return
    
    

LCD_UpdateTimerLabel
    movlw   LCDCMD_HOME
    call    LCD_SendCMD
    movlw   LCD_T
    call    LCD_SendDAT
    movlw   LCD_m
    call    LCD_SendDAT
    movlw   LCD_A
    addwf   SEL_TMR,w
    call    LCD_SendDAT
    bcf     FLAGS,F_TMR_LABEL_DIRTY
    bsf     FLAGS,F_CURSOR_DIRTY
    return


LCD_UpdateCursor
    bcf     FLAGS,F_CURSOR_DIRTY
    btfss   LCD_CURSOR_POSITION,7
    goto    LCD_UpdateCursor_off
    movf    LCD_CURSOR_POSITION,w           ; 1xxxxxxx is actually the set cursor position command, no extra processing needed
    ;movlw   LCDCMD_SET_DD_RAM | 0x40
    call    LCD_SendCMD
    movlw   b'00001110'
    call    LCD_SendCMD
    return
LCD_UpdateCursor_off
    movlw   b'00001100'
    call    LCD_SendCMD
    return


LCD_UpdateTimer
    ;movlw   LCDCMD_HOME
    ;call    LCD_SendCMD
    bsf     FLAGS,F_CURSOR_DIRTY
    bcf     FLAGS,F_TICK
    movlw   LCDCMD_SET_DD_RAM | 0x04
    call    LCD_SendCMD
    goto    LCD_DispTmrHMS

LCD_UpdateTimerDecimalPt
    bsf     FLAGS,F_CURSOR_DIRTY
    movlw   LCDCMD_SET_DD_RAM | 0x44
    call    LCD_SendCMD
    goto    LCD_DispTmrDecimalPt

LCD_DispTmrDecimalPt
    movlw   LCD_0
    call    LCD_SendDAT
    movlw   LCD_PERIOD
    call    LCD_SendDAT
LCD_DispTmrDecimal
    movf    DISP+5,w
    addlw   LCD_0
    call    LCD_SendDAT
    movf    DISP+4,w
    addlw   LCD_0
    call    LCD_SendDAT
    movf    DISP+3,w
    addlw   LCD_0
    call    LCD_SendDAT
    movf    DISP+2,w
    addlw   LCD_0
    call    LCD_SendDAT
    movf    DISP+1,w
    addlw   LCD_0
    call    LCD_SendDAT
    movf    DISP,w
    addlw   LCD_0
    call    LCD_SendDAT
    return

LCD_DispTmrHMS
    movf    DISP+5,w
    addlw   LCD_0
    call    LCD_SendDAT
    movf    DISP+4,w
    addlw   LCD_0
    call    LCD_SendDAT
    movlw   LCD_COLON
    call    LCD_SendDAT
    movf    DISP+3,w
    addlw   LCD_0
    call    LCD_SendDAT
    movf    DISP+2,w
    addlw   LCD_0
    call    LCD_SendDAT
    movlw   LCD_COLON
    call    LCD_SendDAT
    movf    DISP+1,w
    addlw   LCD_0
    call    LCD_SendDAT
    movf    DISP,w
    addlw   LCD_0
    call    LCD_SendDAT
    return


LCD_UpdateValDisplay
    bcf     FLAGS,F_VAL_DISPLAY_DIRTY
    movlw   LCDCMD_SET_DD_RAM | 0x44
    call    LCD_SendCMD
    goto    LCD_DispValHMS

LCD_DispValDecimalPt
    movlw   LCD_0
    call    LCD_SendDAT
    movlw   LCD_PERIOD
    call    LCD_SendDAT
LCD_DispValDecimal
    movf    VAL+5,w
    addlw   LCD_0
    call    LCD_SendDAT
    movf    VAL+4,w
    addlw   LCD_0
    call    LCD_SendDAT
    movf    VAL+3,w
    addlw   LCD_0
    call    LCD_SendDAT
    movf    VAL+2,w
    addlw   LCD_0
    call    LCD_SendDAT
    movf    VAL+1,w
    addlw   LCD_0
    call    LCD_SendDAT
    movf    VAL,w
    addlw   LCD_0
    call    LCD_SendDAT
    return

LCD_DispValHMS
    movf    VAL+5,w
    addlw   LCD_0
    call    LCD_SendDAT
    movf    VAL+4,w
    addlw   LCD_0
    call    LCD_SendDAT
    movlw   LCD_COLON
    call    LCD_SendDAT
    movf    VAL+3,w
    addlw   LCD_0
    call    LCD_SendDAT
    movf    VAL+2,w
    addlw   LCD_0
    call    LCD_SendDAT
    movlw   LCD_COLON
    call    LCD_SendDAT
    movf    VAL+1,w
    addlw   LCD_0
    call    LCD_SendDAT
    movf    VAL,w
    addlw   LCD_0
    call    LCD_SendDAT
    return

MSG_StartRead            ; msg ID in "w" -> MSGH,MSGL
    banksel EEADR
    movwf   EEADR
    movlw   0x18
    movwf   EEADRH
    banksel EECON1    
    bsf     EECON1,EEPGD ; Point to PROGRAM memory
    bsf     EECON1,RD ; EE Read
    nop
    nop
    banksel EEDATA
    movf    EEDATA,w
    banksel MSGL
    movwf   MSGL
    banksel EEDATH
    movf    EEDATH,w
    banksel MSGL
    movwf   MSGH
    return

MSG_NextChar
    movf    MSGL,w
    banksel EEADR
    movwf   EEADR
    banksel MSGH
    movf    MSGH,w
    banksel EEADRH
    movwf   EEADRH
    banksel EECON1    
    bsf     EECON1,EEPGD ; Point to PROGRAM memory
    bsf     EECON1,RD ; EE Read
    nop
    nop
    banksel MSGH
    btfsc   MSGH,7
    goto    MSG_NextChar_second
    banksel EEDATH
    movf    EEDATH,w
    banksel MSGDAT
    movwf   MSGDAT
    banksel EEDATA
    rlf     EEDATA,w
    banksel MSGDAT
    rlf     MSGDAT,f
    bsf     MSGH,7
    movf    MSGDAT,w
    return
MSG_NextChar_second
    banksel EEDATA
    movf    EEDATA,w
    banksel MSGDAT
    movwf   MSGDAT
    bcf     MSGDAT,7
    incf    MSGL,f
    btfsc   STATUS,Z
    incf    MSGH,f
    bcf     MSGH,7
    movf    MSGDAT,w
    return


SPK_Delay
    movlw   0x0F
    movwf   TMP
    decf    TMP,f
    btfss   STATUS,Z
    goto    $-2
    return

SPK_Pip
    movlw   0xFF
    movwf   TMP2
SPK_Pip_0
    movlw   0x20
    movwf   TMP1
SPK_Pip_1
    lcall   SPK_Delay
    decf    TMP1,f
    btfss   STATUS,Z
    goto    SPK_Pip_1
    movlw   0x01<<B_SPK
    xorwf   PORTA,f
    decf    TMP2,f
    btfss   STATUS,Z
    goto    SPK_Pip_0
    bcf     PORTA,B_SPK
    return


SPK_Peep
    movlw   0x10
    movwf   TMP3
SPK_Peep_0
    call    SPK_Pip
    decf    TMP3,f
    btfss   STATUS,Z
    goto    SPK_Peep_0
    return


SPK_Whip
    clrf    TMP2
    movlw   0xC0
SPK_Whip_0
    movf    TMP2,w
    addlw   0x38
    movwf   TMP1
SPK_Whip_1
    lcall   SPK_Delay
    decf    TMP1,f
    btfss   STATUS,Z
    goto    SPK_Whip_1
    movlw   0x01<<B_SPK
    xorwf   PORTA,f
    decf    TMP2,f
    btfss   STATUS,Z
    goto    SPK_Whip_0
    return
    

    org 0x1000
S00_TimerSelectLoop
    clrf    LCD_CURSOR_POSITION
    SetMsg  MSG_ID_Clear
    IfJump  VK_MENU_NEXT,S00_NextTmr
    IfJump  VK_MENU_PREV,S00_PrevTmr
    IfState VK_MENU_ENTER,S10_AdjustTimer
    lgoto   SM_Rtn
S00_NextTmr
    incf    SEL_TMR,f
    movlw   0x0F
    andwf   SEL_TMR,f
    bsf     FLAGS,F_TMR_LABEL_DIRTY
    bsf     FLAGS,F_TICK
    lgoto   SM_Rtn
S00_PrevTmr
    decf    SEL_TMR,f
    movlw   0x0F
    andwf   SEL_TMR,f
    bsf     FLAGS,F_TMR_LABEL_DIRTY
    bsf     FLAGS,F_TICK
    lgoto   SM_Rtn

S10_AdjustTimer
    clrf    LCD_CURSOR_POSITION
    SetMsg  0x00
    IfState VK_MENU_EXIT,S00_TimerSelectLoop
    IfState VK_MENU_NEXT,S20_AdjustClock
    IfState VK_MENU_PREV,S20_AdjustClock
    ;IfState VK_MENU_PREV,S30_ThermometerSetup
    IfState VK_MENU_ENTER,S11_SetTimer
    IfJump  VK_START_STOP,S10_StartStopTimer
    IfJump  VK_DIRECTION,S10_ToggleTimerDirection
    lgoto   SM_Rtn
S10_StartStopTimer
    movlw   0x01<<TMR_ON
    lcall   ToggleTimerSettings
    lgoto   SM_Rtn
S10_ToggleTimerDirection
    movlw   0x01<<TMR_DIR
    lcall   ToggleTimerSettings
    lgoto   SM_Rtn

S11_SetTimer
    SetMsg  0x01    ; MSG_SetTimer
    IfState VK_MENU_EXIT,S10_AdjustTimer
    IfState VK_MENU_NEXT,S12_SetAlarm
    IfState VK_MENU_PREV,S13_SetLength
    IfState VK_MENU_ENTER,S15_SetTimerValue
    lgoto   SM_Rtn

S12_SetAlarm
    SetMsg  0x07
    IfState VK_MENU_EXIT,S10_AdjustTimer
    IfState VK_MENU_NEXT,S13_SetLength
    IfState VK_MENU_PREV,S11_SetTimer
    IfState VK_MENU_ENTER,S21_SetAlarmTime
    lgoto   SM_Rtn

S13_SetLength
    SetMsg  0x04    ; MSG_SetLength
    IfState VK_MENU_EXIT,S10_AdjustTimer
    IfState VK_MENU_NEXT,S11_SetTimer
    IfState VK_MENU_PREV,S12_SetAlarm
    IfState VK_MENU_ENTER,S16_SetLengthValue
    lgoto   SM_Rtn

S15_SetTimerValue
    SetMsg  0x06    ; MSG_Set
    movlw   D_SEL_TIMER
    movwf   VAL_STAT
    GoToState S_EnterValueHMS
    lgoto   SM_Rtn

S16_SetLengthValue
    SetMsg  0x06    ; MSG_Set
    movlw   D_SEL_TIMER_LENGTH 
    movwf   VAL_STAT
    GoToState S_EnterLongValue
    lgoto   SM_Rtn

S20_AdjustClock
    SetMsg  0x02
    IfState VK_MENU_EXIT,S00_TimerSelectLoop
    IfState VK_MENU_NEXT,S10_AdjustTimer
    ;IfState VK_MENU_NEXT,S30_ThermometerSetup
    IfState VK_MENU_PREV,S10_AdjustTimer
    IfJump  VK_START_STOP,S20_StartStopClock
    IfJump  VK_SKIP_FORWARD,S20_StartStopClock
    lgoto   SM_Rtn
S20_StartStopClock
    movlw   0x01<<F_CLOCK_STOPPED
    xorwf   FLAGS,f
    lgoto   SM_Rtn
S20_SkipForward
    movlw   (0x01<<F_CLOCK_STOPPED) % 0xFF
    andwf   FLAGS,f
    incf    INT_PULSECTR,f
    incf    INT_PULSECTR,f
    incf    INT_PULSECTR,f
    incf    INT_PULSECTR,f
    lgoto   SM_Rtn

;S30_ThermometerSetup
;    SetMsg  0x03
;    IfState VK_MENU_EXIT,S00_TimerSelectLoop
;    IfState VK_MENU_NEXT,S10_AdjustTimer
;    IfState VK_MENU_PREV,S20_AdjustClock
;    lgoto   SM_Rtn

S21_SetAlarmTime
    SetMsg  0x08
    IfState VK_MENU_EXIT,S12_SetAlarm
    IfState VK_MENU_NEXT,S22_SetSound
    IfState VK_MENU_PREV,S23_AlarmAction
    IfState VK_MENU_ENTER,S21_EnterAlarmValue
    lgoto   SM_Rtn

S21_EnterAlarmValue
    SetMsg  0x06    ; MSG_Set
    movlw   D_SEL_TIMER_ALARM
    movwf   VAL_STAT
    movf    SEL_TMR,w
    movwf   CTR1
    lcall   LoadSelTmrSettingsAndLen
    movlw   REG_Z
    lcall    M_CLR
    movlw   REG_X
    lcall    M_CLR
    bsf     STATUS,IRP  ; TUK E PROBLEMAT!!!
    lcall   MC_Normalize_NO_CLR
    bcf     STATUS,IRP
    lcall   MC_NormalizedTo86400
    lcall   MC_86400toHMS
    GoToState S_EnterValueHMS
    lgoto   SM_Rtn

S22_SetSound
    SetMsg  0x09
    IfState VK_MENU_EXIT,S12_SetAlarm
    IfState VK_MENU_NEXT,S23_AlarmAction
    IfState VK_MENU_PREV,S21_SetAlarmTime
    IfState VK_MENU_ENTER,S40_SoundSelect
    lgoto   SM_Rtn

S23_AlarmAction
    SetMsg  0x0A
    IfState VK_MENU_EXIT,S12_SetAlarm
    IfState VK_MENU_NEXT,S21_SetAlarmTime
    IfState VK_MENU_PREV,S22_SetSound
    IfState VK_MENU_ENTER,S51_SelectAction
    lgoto   SM_Rtn

S40_SoundSelect
    movlw   0x03
    andwf   SEL_TMR_SETTINGS,w
    movwf   TMP
    btfss   STATUS,Z
    goto    S40_SoundSelect_0
; sound is Silent
    GoToState S44_SoundSilent
    lgoto   SM_Rtn
S40_SoundSelect_0
    movlw   SND_PIP
    subwf   TMP,w
    btfss   STATUS,Z
    goto    S40_SoundSelect_1
; sound is Pip
    GoToState S41_SoundPip
    lgoto   SM_Rtn
S40_SoundSelect_1
    movlw   SND_PEEP
    subwf   TMP,w
    btfss   STATUS,Z
    goto    S40_SoundSelect_2
; sound is Peep
    GoToState S42_SoundPeep
    lgoto   SM_Rtn
S40_SoundSelect_2
; sound is Whip
    GoToState S43_SoundWhip
    lgoto   SM_Rtn
    lgoto   SM_Rtn

S41_SoundPip
    SetMsg  0x0B
    movlw   SND_PIP
    call    SetSound
    IfState VK_MENU_ENTER,S22_SetSound
    IfState VK_MENU_EXIT,S22_SetSound
    IfState VK_MENU_NEXT,S42_SoundPeep
    IfState VK_MENU_PREV,S44_SoundSilent
    lgoto   SM_Rtn

S42_SoundPeep
    SetMsg  0x0C
    movlw   SND_PEEP
    call    SetSound
    IfState VK_MENU_ENTER,S22_SetSound
    IfState VK_MENU_EXIT,S22_SetSound
    IfState VK_MENU_NEXT,S43_SoundWhip
    IfState VK_MENU_PREV,S41_SoundPip
    lgoto   SM_Rtn

S43_SoundWhip
    SetMsg  0x0D
    movlw   SND_WHIP
    call    SetSound
    IfState VK_MENU_ENTER,S22_SetSound
    IfState VK_MENU_EXIT,S22_SetSound
    IfState VK_MENU_NEXT,S44_SoundSilent
    IfState VK_MENU_PREV,S42_SoundPeep
    lgoto   SM_Rtn

S44_SoundSilent
    SetMsg  0x0E
    movlw   SND_SILENT
    call    SetSound
    IfState VK_MENU_ENTER,S22_SetSound
    IfState VK_MENU_EXIT,S22_SetSound
    IfState VK_MENU_NEXT,S41_SoundPip
    IfState VK_MENU_PREV,S43_SoundWhip
    lgoto   SM_Rtn

Action_SetAddress   ; WARNING: This sets STATUS,IRP!  Clear it after done with INDF
    movf    SEL_TMR,w
    addlw   low TMR_SETTINGS
    movwf   FSR
    bsf     STATUS,IRP
    bcf     INDF,TMR_ON_ALARM_STOP
    bcf     INDF,TMR_ON_ALARM_RESET
    return

S51_SelectAction
    btfss   SEL_TMR_SETTINGS,TMR_ON_ALARM_RESET
    goto    S51_SelectAction_ResetOff
S51_SelectAction_ResetOn
    btfss   SEL_TMR_SETTINGS,TMR_ON_ALARM_STOP
    goto    S51_SelectAction_GoToReset
S51_SelectAction_GoToBoth
    GoToState S54_ActionBoth
    lgoto   SM_Rtn
S51_SelectAction_GoToReset
    GoToState S52_ActionReset
    lgoto   SM_Rtn
S51_SelectAction_ResetOff
    btfss   SEL_TMR_SETTINGS,TMR_ON_ALARM_STOP
    goto    S51_SelectAction_GoToNone
S51_SelectAction_GoToStop
    GoToState S53_ActionStop
    lgoto   SM_Rtn
S51_SelectAction_GoToNone
    GoToState S51_ActionNone
    lgoto   SM_Rtn

S51_ActionNone
    SetMsg  0x0F
    movf    SEL_TMR,w
    call    Action_SetAddress
    movf    INDF,w
    bcf     STATUS,IRP
    movwf   SEL_TMR_SETTINGS
    IfState VK_MENU_ENTER,S23_AlarmAction
    IfState VK_MENU_EXIT,S23_AlarmAction
    IfState VK_MENU_NEXT,S52_ActionReset
    IfState VK_MENU_PREV,S54_ActionBoth
    lgoto   SM_Rtn

S52_ActionReset
    SetMsg  0x10
    call    Action_SetAddress
    bsf     INDF,TMR_ON_ALARM_RESET
    movf    INDF,w
    bcf     STATUS,IRP
    movwf   SEL_TMR_SETTINGS
    IfState VK_MENU_ENTER,S23_AlarmAction
    IfState VK_MENU_EXIT,S23_AlarmAction
    IfState VK_MENU_NEXT,S53_ActionStop
    IfState VK_MENU_PREV,S51_ActionNone
    lgoto   SM_Rtn

S53_ActionStop
    SetMsg  0x11
    call    Action_SetAddress
    bsf     INDF,TMR_ON_ALARM_STOP
    movf    INDF,w
    bcf     STATUS,IRP
    movwf   SEL_TMR_SETTINGS
    IfState VK_MENU_ENTER,S23_AlarmAction
    IfState VK_MENU_EXIT,S23_AlarmAction
    IfState VK_MENU_NEXT,S54_ActionBoth
    IfState VK_MENU_PREV,S52_ActionReset
    lgoto   SM_Rtn

S54_ActionBoth
    SetMsg  0x12
    call    Action_SetAddress
    bsf     INDF,TMR_ON_ALARM_STOP
    bsf     INDF,TMR_ON_ALARM_RESET
    movf    INDF,w
    bcf     STATUS,IRP
    movwf   SEL_TMR_SETTINGS
    IfState VK_MENU_ENTER,S23_AlarmAction
    IfState VK_MENU_EXIT,S23_AlarmAction
    IfState VK_MENU_NEXT,S51_ActionNone
    IfState VK_MENU_PREV,S53_ActionStop
    lgoto   SM_Rtn

SetSound
    movwf   TMP
    movlw   low TMR_SETTINGS
    movwf   FSR
    movf    SEL_TMR,w
    addwf   FSR,f
    bsf     STATUS,IRP
    movlw   0xFC
    andwf   INDF,f
    movf    TMP,w
    iorwf   INDF,f
    bcf     STATUS,IRP
    return

S_EnterValueHMS
    clrf    VAL_IND
    lcall   CopyDispToVal
    bsf     FLAGS,F_VAL_DISPLAY_DIRTY
    movlw   0x80 | 0x44
    movwf   LCD_CURSOR_POSITION
    bsf     FLAGS,F_CURSOR_DIRTY
    GoToState S_EnterValueHMS_Digit
    lgoto   SM_Rtn

S_EnterValueHMS_Digit
    pagesel $
    IfState VK_MENU_EXIT,S_EnterValue_End
    IfState VK_MENU_ENTER,S_EnterValueHMS_Accept
    ;movf    VAL_STAT,w
    ;sublw   D_SEL_TIMER
    ;btfsc   STATUS,Z
    ;goto    S_EnterValueHMS_Digit_0
    ;IfState VK_COPY,S_CopyFromTimer
    ;lgoto   SM_Rtn
;S_EnterValueHMS_Digit_0
    btfsc   BTN_ID,7
    goto    S_EnterValueHMS_DigitRTN
    movf    BTN_ID,w
    andlw   0x0F
    lcall   LOOKUP_DigitFromKeyID
    movwf   TMP
    pagesel $
    btfss   TMP,7
    goto    S_EnterValueHSM_Digit_1
    lgoto   SM_Rtn
S_EnterValueHSM_Digit_1
    movf    VAL_IND,w
    sublw   VAL+5
    movwf   FSR
    movf    TMP,w
    andlw   0x0F
    movwf   INDF
    bsf     FLAGS,F_VAL_DISPLAY_DIRTY
    incf    VAL_IND,f
    movf    VAL_IND,w
    lcall   LOOKUP_CursorPosHMS
    movwf   LCD_CURSOR_POSITION
    pagesel $
    bsf     FLAGS,F_CURSOR_DIRTY
    movf    VAL_IND,w
    sublw   0x06
    btfsc   STATUS,Z
    goto    S_EnterValueHMS_Accept
S_EnterValueHMS_DigitRTN
    lgoto   SM_Rtn

    
S_EnterValueHMS_Accept
    pagesel $
    ; should accept value here and do whatever is needed, then should continue toward S_EnterValue_End
    movf    SEL_TMR,w
    movwf   CTR1
    lcall   LoadSelTmrSettingsAndLen
    lcall   MC_HMSto86400t4             ; new timer value in REG_Z
    lcall   MC_86400toNormalized        ; REG_Z(86400t4) -> REG_Y(normalized)
    lcall   MC_NormalizedToLength       ; REG_Y(normalized) -> REG_Z(sel_timer_length_value, +3 bytes offsett)
    movf    REG_Z+3,w
    movwf   VAL
    movf    REG_Z+4,w
    movwf   VAL+1
    movf    REG_Z+5,w
    movwf   VAL+2
    movf    REG_Z+6,w
    movwf   VAL+3
    bsf     FLAGS,F_VALUE_ENTERED

S_EnterValue_End
    pagesel $
    clrf    LCD_CURSOR_POSITION
    bsf     FLAGS,F_CURSOR_DIRTY
    movf    VAL_STAT,w
    sublw   D_SEL_TIMER_ALARM
    btfsc   STATUS,Z
    goto    S_EnterValue_End_STA
    GoToState S11_SetTimer
    lgoto   SM_Rtn

S_EnterValue_End_STA
    GoToState S21_SetAlarmTime
    lgoto   SM_Rtn

S_CopyFromTimer
    movlw   D_SEL_TIMER_COPY
    movwf   VAL_STAT
    GoToState S_EnterLongValue
    lgoto   SM_Rtn

S_EnterLongValue    ; enter a long value
    movlw   REG_Z
    lcall   M_CLR
    lcall   M_MOV_Z_TO_VAL
    clrf    VAL_IND
    addlw   0x80 | 0x44
    movwf   LCD_CURSOR_POSITION
    bsf     FLAGS,F_CURSOR_DIRTY
    bcf     FLAGS,F_VAL_DISPLAY_DIRTY   ; DO NOT update the VAL display - it's not used here
    GoToState S_EnterLongValue_Digit
    lgoto   SM_Rtn

S_EnterLongValue_Digit
    pagesel $
    IfState VK_MENU_EXIT,S_EnterLongValue_End
    IfState VK_MENU_ENTER,S_EnterLongValue_Accept
    pagesel $
    btfsc   BTN_ID,7
    goto    S_EnterLongValue_DigitRTN
    movf    BTN_ID,w
    andlw   0x0F
    lcall   LOOKUP_DigitFromKeyID
    movwf   VAL_DIGIT
    pagesel $
    btfss   VAL_DIGIT,7
    goto    S_EnterLongValue_Digit_1
    lgoto   SM_Rtn
S_EnterLongValue_Digit_1
    movlw   REG_Y
    lcall   M_CLR
    movlw   .10
    movwf   REG_Y
    lcall   M_MOV_VAL_TO_X
    lcall   M_MUL
    movlw   REG_X
    lcall   M_CLR
    movf    VAL_DIGIT,w
    movwf   REG_X
    lcall   M_ADD
    lcall   M_MOV_Z_TO_VAL
    movf    VAL_IND,w
    addlw   LCDCMD_SET_DD_RAM | 0x44
    lcall   LCD_SendCMD
    movf    VAL_DIGIT,w
    addlw   LCD_0
    lcall   LCD_SendDAT
    incf    VAL_IND,f
    movf    VAL_IND,w
    addlw   0x80 | 0x44
    movwf   LCD_CURSOR_POSITION
    bsf     FLAGS,F_CURSOR_DIRTY
    pagesel $
    movf    VAL_IND,w
    sublw   .10
    btfsc   STATUS,Z
    decf    VAL_IND,f
S_EnterLongValue_DigitRTN
    lgoto   SM_Rtn
    
S_EnterLongValue_Accept
    bsf     FLAGS,F_VALUE_ENTERED
S_EnterLongValue_End
    clrf    LCD_CURSOR_POSITION
    bsf     FLAGS,F_CURSOR_DIRTY
    GoToState S13_SetLength
    lgoto   SM_Rtn

    org 0x1800
    da      MSG_AdjustTimer         ; 0x00
    da      MSG_SetTimer            ; 0x01
    da      MSG_AdjustClock         ; 0x02
    da      MSG_ThermometerSetup    ; 0x03 - not used in this version
    da      MSG_SetLength           ; 0x04
    da      MSG_SetFormat           ; 0x05
    da      MSG_Set                 ; 0x06
    da      MSG_SetAlarm            ; 0x07
    da      MSG_SetAlarmTime        ; 0x08
    da      MSG_SetSound            ; 0x09
    da      MSG_AlarmAction         ; 0x0A
    da      MSG_SoundPip            ; 0x0B
    da      MSG_SoundPeep           ; 0x0C
    da      MSG_SoundWhip           ; 0x0D
    da      MSG_SoundSilent         ; 0x0E
    da      MSG_ActionNone          ; 0x0F
    da      MSG_ActionReset         ; 0x10
    da      MSG_ActionStop          ; 0x11
    da      MSG_ActionBoth          ; 0x12
MSG_AdjustTimer
    da      "Adjust Timer",0
MSG_SetTimer
    da      "Set Timer",0
MSG_AdjustClock
    da      "Adjust Clock",0
MSG_ThermometerSetup
    da      "Therm. Setup",0        ; not used in this version
MSG_SetLength
    da      "Set Cycle Length",0
MSG_SetFormat
    da      "Set Format",0
MSG_Set
    da      "Set",0
MSG_SetAlarm
    da      "Set Alarm",0
MSG_SetAlarmTime
    da      "Alarm Time",0
MSG_SetSound
    da      "Alarm Sound",0
MSG_AlarmAction
    da      "Alarm Action",0
MSG_SoundPip
    da      "Set to: Pip",0
MSG_SoundPeep
    da      "Set to: Peep",0
MSG_SoundWhip
    da      "Set to: Whip",0
MSG_SoundSilent
    da      "Set to: Silent",0
MSG_ActionNone
    da      "Action: None",0
MSG_ActionReset
    da      "Action: Reset",0
MSG_ActionStop
    da      "Action: Stop",0
MSG_ActionBoth
    da      "Action: Stop+Rst",0

    org 0x1F80
LOOKUP_StdTmr
    movwf   TMP
    movlw   high $
    movwf   PCLATH
    movf    TMP,w
    addwf   PCL,f
; settings
    retlw   b'10000000'
; length
    retlw   0x00
    retlw   0x46
    retlw   0x05
    retlw   0x00
; start offsett
    retlw   0x00
    retlw   0x00
    retlw   0x00
    retlw   0x00

LOOKUP_DigitFromKeyID
    movwf   TMP
    movlw   high $
    movwf   PCLATH
    movf    TMP,w
    addwf   PCL,f
    retlw   0x00
    retlw   0x01
    retlw   0x02
    retlw   0x03
    retlw   0x04
    retlw   0x05
    retlw   0x06
    retlw   0x07
    retlw   0x08
    retlw   0x09
    retlw   0xFF
    retlw   0xFF
    retlw   0xFF
    retlw   0xFF
    retlw   0xFF
    retlw   0xFF

LOOKUP_CursorPosHMS
    movwf   TMP
    movlw   high $
    movwf   PCLATH
    movf    TMP,w
    addwf   PCL,f
    retlw   0xC4
    retlw   0xC5
    retlw   0xC7
    retlw   0xC8
    retlw   0xCA
    retlw   0xCB
    retlw   0xCC

    END
