; NAME: flickering-led.asm
; AUTHOR: Jacques Deschênes
; DATE: 2013-12-18
; Description: a flickering LED that hide an easter egg
;              when the LED is shorted for a short while it start blinking
;              HTTP://HACKADY.COM in morse code after which it resume normal
;              operation.
; REF: http://en.wikipedia.org/wiki/Morse_code
; REF: http://cpldcpu.wordpress.com/2013/12/08/hacking-a-candleflicker-led/
; REF: http://en.wikipedia.org/wiki/LFSR


    include p10f200.inc
    __config _WDTE_OFF

    radix dec ; use decimal base for literals

;constants
TMR0_CNT EQU 221  ; timer0 value for PWM period delay
LED EQU GP2       ; LED output on GP2
LFSR_MASK EQU 0x7FFFF159  ; pseudo random number generator polynomial
MSG_LEN EQU 19 ; easter egg message length



; macros

led_on macro
    bsf GPIO, LED
    endm

led_off macro
    bcf GPIO, LED
    endm

load_r16 macro  n, r16  ; load 16 bits register 'r16' with value 'n'
    if n&255==0
        clrf r16
    else
        movlw n&255
        movwf r16
    endif
    if (n>>8)&255 ==0
        clrf r16+1
    else
        movlw (n>>8)&255
        movwf r16+1
    endif
    endm


; variables
    udata 16
pwm_cntr res 1   ; PWM cycle counter
frame_cntr res 1 ; frame counter
pwm_val res 1   ; PWM level value
next_brigth res 1 ; next brigthness
rand  res 4     ; LFSR  32 bits register (pseudo random number generator)
rand_val res 1    ; last rand() value lowest byte.
char_cntr res 1; number of character in morse message
char res 1 ; character to blink in morse
symb_cntr res 1 ; symbol counter for char
ms_cntr res 2 ; millisecond delay counter (used by delay_ms)


    org 0
reset
    movlw OSCCAL ; set oscillator calibration value
    goto init


;    LFSR 32 bits
;    This LFSR should not be initialised with 0xFFFFFFFF
;    output: lowest 5 bits in rand_val
lfsr_rand
    rrf rand+3,W   ; rotate  LFSR 1 bit right
    rrf rand, F
    rrf rand+1,F
    rrf rand+2,F
    rrf rand+3,F
    btfsc rand+3,7
    goto lfsr_rand_exit
    movlw LFSR_MASK & 0xFF
    xorwf rand
    movlw (LFSR_MASK>>8) & 0xFF
    xorwf rand+1
    movlw (LFSR_MASK>>16) & 0xFF
    xorwf rand+2
    movlw (LFSR_MASK>>24) & 0xFF
    xorwf rand+3
lfsr_rand_exit
    movlw 0x1F
    andwf rand,W  ; keep only 5 bits
    movwf rand_val
    retlw 0

pwm_control  ; 4 bits PWM, control led brightness
    incf pwm_cntr
    movlw 15
    andwf pwm_cntr,F  ; count modulo 16
    movfw pwm_cntr
    subwf pwm_val,W   ; check for LED turn-off level
    skpnc
    goto pwm_hi
pwm_lo
    led_off
    retlw 0
pwm_hi
    led_on
    retlw 0

delay_ms ; variable ms_cntr initialized before call
    movlw 5
    movwf TMR0
    movfw TMR0
    skpz
    goto $-2
    incfsz ms_cntr,F
    goto delay_ms
    incfsz ms_cntr+1,F
    goto delay_ms
    retlw 0



morse_msg ; blink morse message
    clrf char_cntr
char_loop
    movfw char_cntr
    call easter_egg
    movwf char
    rlf char, F
    rlf symb_cntr, F
    rlf char, F
    rlf symb_cntr, F
    movlw 3
    subwf symb_cntr, W
    clrc
    skpz
    rlf char, F
    rlf symb_cntr, F
symbol_loop
    led_on
    rlf char, F
    skpnc
    goto dah
dit ; short symbol
    load_r16 ~100, ms_cntr  ; dit delay
    call delay_ms
    goto symb_end
dah ; long symbol
    load_r16 ~300, ms_cntr  ; dah delay
    call delay_ms
symb_end
    led_off
    load_r16 ~100, ms_cntr ; 1 dit delay between symbol
    call delay_ms
    decfsz symb_cntr,F
    goto symbol_loop
    load_r16 ~200, ms_cntr ; 3 dit delay between character
    call delay_ms
    incf char_cntr,F
    movlw MSG_LEN
    subwf char_cntr,W
    skpz
    goto char_loop
    load_r16 ~500, ms_cntr  ; 7 dit delay between word
    call delay_ms
    retlw 0



init
    movlw 0x81
    option
    movlw B'1011' ; GP2 as output
    tris GPIO
    bsf GPIO,LED  ; led ON
    ; init RAM to 0
zero_ram
    movlw 16
    movwf FSR
    clrf INDF
    incf FSR
    movlw 31
    subwf FSR,W
    bnc zero_ram
    ; timer0 as PWM period counter
    movlw TMR0_CNT
    movwf TMR0

main
    movf TMR0, W  ; 142µsec delay, PWM perdio
    skpz
    goto main
    movlw TMR0_CNT ; reinitialize period delay
    addwf TMR0,F
    call pwm_control ; LED intensity control
    movf pwm_cntr,F
    skpz
    goto main
; frame
    incf frame_cntr  ; increament frame counter
    movlw 0x1F
    andwf frame_cntr,F ; modulo 32
    movlw 7
    andwf frame_cntr,W
    skpz
    goto new_frame
new_rand_val ; request a new random value for next brightness
    call lfsr_rand
    movlw 15
    clrc
    btfsc rand_val, 4
    rrf rand_val, W
    movwf next_brigth
new_frame  ; change brightness every 32 PWM cycle.
    ; check for eatern egg trigger
    movlw b'1111'
    tris GPIO
    btfss GPIO,LED
    goto easter_trigger
new_frame_02
    movlw b'1011'
    tris GPIO
    movf frame_cntr,F
    skpz
    goto main
    movfw next_brigth
    movwf pwm_val
    goto main
easter_trigger ; LED shorted
    load_r16 ~20, ms_cntr
    call delay_ms
    btfsc GPIO,LED
    goto new_frame_02
    movlw b'1011'
    tris GPIO
    call morse_msg
    goto main

easter_egg ; blink "hackaday.com" message as morse code.
    addwf PCL
; morse code for  HTTP://HACKADAY.COM
; if bits 6-7 == b'11' 6 symbols code in bits 0-5 first symbol in bit 5
; else bits 5-7 == symbols count, bits 0-5 are symbols first in bit 4
    dt b'10000000' ; H  dit-dit-dit-dit
    dt b'00110000' ; T dah
    dt b'00110000' ; T dah
    dt b'10001100' ; P dit-dah-dah-dit
    dt b'11111000' ; : dah-dah-dah-dit-dit-dit
    dt b'10110010' ; / dah-dit-dit-dah-dit
    dt b'10110010' ; / dah-dit-dit-dah-dit
    dt b'10000000' ; H  dit-dit-dit-dit
    dt b'01010000' ; A  dit-dah
    dt b'10010100' ; C dah-dit-dah-dit
    dt b'01110100' ; K dah-dit-dah
    dt b'01010000' ; A  dit-dah
    dt b'01110000' ; D dah-dit-dit
    dt b'01010000' ; A  dit-dah
    dt b'10010110' ; Y dah-dit-dah-dah
    dt b'11010101' ; . dit-dah-dit-dah-dit-dah
    dt b'10010100' ; C dah-dit-dah-dit
    dt b'01111100' ; O dah-dah-dah
    dt b'01011000' ; M  dah-dah

     end




