; EMERGENCY EXCUSE GENERATOR Version 1.00 2010-01-01
; http://avtanski.net/eeg

#include <p16F690.inc>
     __config (_INTRC_OSC_NOCLKOUT & _WDT_OFF & _PWRTE_OFF & _MCLRE_OFF & _CP_OFF & _BOR_OFF & _IESO_OFF & _FCMEN_OFF)

#include excuse.inc

; Uncomment this if the button is Normally Closed
;#define BTN_NC

; Define the dimensions of your LCD display here
#define LCD_COLUMNS     .20
#define LCD_ROWS        .4

#define ADDR_ROW0       0x80
#define ADDR_ROW1       0xC0
#define ADDR_ROW2       0x94
#define ADDR_ROW3       0xD4

#if LCD_ROWS==4
#define ADDR_MSG1       ADDR_ROW1+(LCD_COLUMNS-0x10)/2
#define ADDR_MSG2       ADDR_ROW2+(LCD_COLUMNS-0x10)/2
#else
    ; assuming two rows
#define ADDR_MSG1       ADDR_ROW0+(LCD_COLUMNS-0x10)/2
#define ADDR_MSG2       ADDR_ROW1+(LCD_COLUMNS-0x10)/2
#endif

#define F_CAPITALIZE    0

    cblock  0x20
    RND
    RND1
    TMP0
    TMP1
    TMP2
    FLAGS
    COUNTER
    ROW
    COLUMN
    LOOKUP_TEMP
    IXH
    IXL
    DTH
    DTL
    CHAR
    CHARH
    STACK_PTR
    STACK:40
    endc

    org 0x00
    goto    Start
    org 0x04
    goto    Start

Start
    call    Init
#ifdef BTN_NC
    btfsc   PORTA,2
#else
    btfss   PORTA,2
#endif
    goto    RND_Test
    bcf     FLAGS,F_CAPITALIZE
    movlw   TAB_1H
    movwf   DTH
    movlw   TAB_1L
    movwf   DTL
    movlw   ADDR_MSG1
    call    GenerateAtLocation
    movlw   TAB_2H
    movwf   DTH
    movlw   TAB_2L
    movwf   DTL
    movlw   ADDR_MSG2
    call    GenerateAtLocation

MainLoop
#ifdef BTN_NC
    btfsc   PORTA,2
#else
    btfss   PORTA,2
#endif
    goto    $-1
    movlw   0x30
    incf    RND,f
    movwf   TMP2
#ifdef BTN_NC
    btfss   PORTA,2
#else
    btfsc   PORTA,2
#endif
    goto    $-4
    call    LCD_Wait
    decfsz  TMP2,f
    goto    $-4
    bsf     PORTB,4
    call    RunGenerator
    bcf     PORTB,4
    goto    MainLoop

Init
    banksel PORTC   ; Init ports
    clrf    PORTC
    clrf    PORTB
    banksel ANSEL
    movlw   b'00000000'
    movwf   ANSEL
    movwf   ANSELH
    banksel TRISC
    movlw   b'00000000'
    movwf   TRISC
    movlw   b'00101111'
    movwf   TRISB
    bcf     OPTION_REG, 7
    bsf     WPUA,2
    banksel WPUB
    clrf    WPUB
    banksel PORTC
    call    LCD_Wait ; Init LCD
    movlw   0x00
    movwf   PORTB
    clrf    TMP2
Init_LCD_loop
    movf    TMP2,w
    call    Init_LCD_Table
    movwf   PORTC
    call    LCD_PulseZ
    incf    TMP2,f
    movlw   0x0A
    subwf   TMP2,w
    btfss   STATUS,Z
    goto    Init_LCD_loop
    bsf     PORTB,7
    return

Init_LCD_Table
    movwf   LOOKUP_TEMP
    clrf    PCLATH
    movf    LOOKUP_TEMP,w
    addwf   PCL,f
    retlw   b'00110000'
    retlw   b'00111000'
    retlw   b'00111000'
    retlw   b'00111000'
    retlw   b'00001000'
    retlw   b'00000001'
    retlw   b'00000110'
    retlw   b'00001000'
    retlw   b'00000010'
    retlw   b'00001100'

LCD_Row_Address_Table
    movwf   LOOKUP_TEMP
    clrf    PCLATH
    movf    LOOKUP_TEMP,w
    addwf   PCL,f
    retlw   ADDR_ROW0
    retlw   ADDR_ROW1
    retlw   ADDR_ROW2
    retlw   ADDR_ROW3

LCD_PulseZ
    nop
    nop
    nop
    bsf     PORTB,6
    nop
    nop
    nop
    bcf     PORTB,6
    call    LCD_Wait
    return

LCD_Wait
    movlw   0x01
    movwf   TMP0
    movlw   0x10
    movwf   TMP1
LCD_Wait_0
    decfsz  TMP0, f
    goto    $+2
    decfsz  TMP1, f
    goto    LCD_Wait_0
    return

Random
    movlw   0x04
    movwf   TMP2
Random_loop
    bcf     PORTB,5 ; Prepare capacitor
    banksel TRISB
    bcf     TRISB,5
    banksel PORTB
    call    LCD_Wait
    banksel TRISB
    bsf     TRISB,5 ; Count discharge time
    banksel PORTB
    incf    RND,f
    btfss   PORTB,5
    goto    $-2
    decfsz  TMP2,f
    goto    Random_loop
    movf    RND,w
    movwf   RND1
    return

GenerateAtLocation
    movwf   PORTC
    bcf     PORTB,7
    call    LCD_PulseZ
    bsf     PORTB,7
    clrf    STACK_PTR
    goto    Generate

RunGenerator
    bsf     FLAGS,F_CAPITALIZE
    call    LCD_ClearScreen
    clrf    STACK_PTR
    movlw   TAB_0H
    movwf   DTH
    movlw   TAB_0L
    movwf   DTL
#ifdef BTN_NC
    btfsc   PORTA,2
#else
    btfss   PORTA,2
#endif
    goto    $-1

Generate
    call    Random
    call    GetCharFromTable
    clrf    TMP0
Generate_randomSelect
    incf    TMP0,f
    movf    CHAR,w
    xorwf   TMP0,w
    btfsc   STATUS,Z
    clrf    TMP0
    decfsz  RND1,f
    goto    Generate_randomSelect
GenerateChar_loop
    movf    TMP0,f
    btfsc   STATUS,Z
    goto    GenerateChar_readAddress
    call    GetCharFromTable
    call    GetCharFromTable
    decf    TMP0,f
    goto    GenerateChar_loop
GenerateChar_readAddress
    call    GetCharFromTable
    movwf   IXH
    call    GetCharFromTable
    movwf   DTL
    movf    IXH,w
    movwf   DTH
    
GenerateChar
    call    GetCharFromTable
    movf    CHAR,w
    btfss   STATUS,Z
    goto    GenerateChar_0
    ; CHAR==0x00
    movf    STACK_PTR,f
    btfsc   STATUS,Z
    return
    ; PopStack->(DTH,DTL)
    decf    STACK_PTR,f
    decf    STACK_PTR,f
    movlw   low STACK
    addwf   STACK_PTR,w
    movwf   FSR
    movf    INDF,w
    movwf   DTH
    incf    FSR,f
    movf    INDF,w
    movwf   DTL
    goto    GenerateChar
GenerateChar_0
    movf    CHAR,w
    sublw   0x01    ; ADDR
    btfss   STATUS,Z
    goto    GenerateChar_1
    ; CHAR==0x01
    call    GetCharFromTable
    movwf   IXH
    call    GetCharFromTable
    movwf   IXL
    ; PushStack(DTH,DTL)
    movlw   low STACK
    addwf   STACK_PTR,w
    movwf   FSR
    movf    DTH,w
    movwf   INDF
    incf    FSR,f
    movf    DTL,w
    movwf   INDF
    incf    STACK_PTR,f
    incf    STACK_PTR,f
    movf    IXH,w
    movwf   DTH
    movf    IXL,w
    movwf   DTL
    goto    Generate
GenerateChar_1
    ; Prepare the char
    movf    COLUMN,w
    sublw   LCD_COLUMNS
    btfss   STATUS,Z
    goto    GenerateChar_outputChar
    ; Reached row end, see if there is another row below
    clrf    COLUMN
    incf    ROW,f
    movf    ROW,w
    sublw   LCD_ROWS
    btfsc   STATUS,Z
    goto    GenerateChar_pauseAndClear
    movf    ROW,w
    call    LCD_Row_Address_Table
    movwf   PORTC
    bcf     PORTB,7
    call    LCD_PulseZ
    bsf     PORTB,7
GenerateChar_outputChar
    ; Do the actual output
    movf    COLUMN,f
    btfss   STATUS,Z
    goto    GenerateChar_doOutput
    movlw   0x20
    subwf   CHAR,w
    btfsc   STATUS,Z
    goto    GenerateChar
GenerateChar_doOutput
    incf    COLUMN,f
    btfss   FLAGS,F_CAPITALIZE
    goto    GenerateChar_doOutput_cnt
    bcf     STATUS,C
    movlw   0x61
    subwf   CHAR,w
    btfss   STATUS,C
    goto    GenerateChar_doOutput_cnt
    movlw   0x7B
    subwf   CHAR,w
    btfsc   STATUS,C
    goto    GenerateChar_doOutput_cnt
    movlw   0x20
    subwf   CHAR,f
GenerateChar_doOutput_cnt
    bcf     FLAGS,F_CAPITALIZE
    movf    CHAR,w
    movwf   PORTC
    call    LCD_PulseZ
    goto    GenerateChar

GenerateChar_pauseAndClear
#ifdef BTN_NC
    btfsc   PORTA,2
    goto    $-1
    btfss   PORTA,2
#else
    btfss   PORTA,2
    goto    $-1
    btfsc   PORTA,2
#endif
    goto    $-1
    call    LCD_ClearScreen
    goto    GenerateChar_outputChar

LCD_ClearScreen
    clrf    ROW
    clrf    COLUMN
    movlw   b'00000001'
    movwf   PORTC
    bcf     PORTB,7
    call    LCD_PulseZ
    bsf     PORTB,7
    call    LCD_Wait
    return

GetCharFromTable
    rrf     DTH,w
    andlw   0x1F
    bcf     STATUS,RP0
    bsf     STATUS,RP1
    movwf   EEADRH
    bcf     STATUS,RP1
    rlf     DTL,w
    movwf   TMP2
    rrf     DTH,w
    rrf     TMP2,w
    bsf     STATUS,RP1
    movwf   EEADR
    bsf     STATUS,RP0
    bsf     EECON1,EEPGD
    bsf     EECON1,RD
    nop
    nop
    bcf     STATUS,RP0
    movf    EEDAT,w
    bcf     STATUS,RP1
    movwf   CHAR
    bsf     STATUS,RP1
    movf    EEDATH,w
    bcf     STATUS,RP1
    movwf   CHARH
    btfsc   DTH,6
    goto    GetCharFromTable_inc
    bsf     DTH,6
    movlw   0x7F
    andwf   CHAR,f
    movf    CHAR,w
    return
GetCharFromTable_inc
    rlf     CHAR,w
    rlf     CHARH,w
    movwf   CHAR
    incf    DTL,f
    bcf     DTH,6
    btfsc   DTL,7
    incf    DTH,f
    bcf     DTL,7
    return

RND_Test
    clrf    RND
    call    Random
    andlw   0x0F
    addlw   0x41
    movwf   PORTC
    call    LCD_PulseZ
    goto    RND_Test

    END
