;**********************************************************************
;    Filename:        sigmod.asm                                      *
;    Date:            2004-01-06                                      *
;    File Version:    1.0                                             *
;                                                                     *
;    Author:          Per Zander                                      *
;**********************************************************************
;    Files required:  p16c505.inc                                     *
;********************************************************************** 
;
;    Description:                                                     
; 
;    General:
;
;    This is a program for model railroad signal control via a 9600 baud
;    serial link. The program supports soft light change via a table at 
;    the end of the program.
;
;    Serial protocol:
;
;    9600 baud, 8 bits, no parity, no handshaking. Data is inverted, so that
;    an rs232 signal can be used directly (after appropriate clamping).
;
;    Signal control packet:
;
;    A signal control packet consists of two or more bytes.
;    First byte:
;
;      7   6   5   4   3   2   1   0
;     _______________________________
;    | 0 |    Device address         |
;    |___|___________________________|
;
;    Second and further byte:
;
;      7   6   5   4   3   2   1   0
;     _______________________________
;    | 1 | 0 | c | 0 | h |Port number|
;    |___|___|___|___|___|___________|
;
;    A correct packet addressed to the device, and with the c bit set, 
;    activates the selected port/ports. If the c bit is cleared, the 
;    selected port is deactivated. 
;
;    The h field is used to select the upper or lower half of a signal
;    module with 16 ports.
;**********************************************************************

;*---------- Device address definition. ----------*

my_addr EQU     0x00              ; Change for each unit. MSB should be 0.
hi_addr EQU     0x80              ; Should be 1000h000 (bin). 

;*---------- Device select. ----------*

        list      p=16c505        ; List directive to define processor
        #include <p16c505.inc>    ; Processor specific variable definitions

;*---------- Configuration specification. ----------*

        __CONFIG   _CP_OFF & _WDT_OFF & _MCLRE_OFF & _XT_OSC

;*---------- Port definitions. ----------*

din     EQU     0x05              ; Data in        on RC5     
led0    EQU     0x00              ; LED 0 on RB0                 
led1    EQU     0x01              ; LED 1 on RB1
led2    EQU     0x02              ; LED 2 on RB2
led3    EQU     0x00              ; LED 3 on RC0 
led4    EQU     0x01              ; LED 4 on RC1
led5    EQU     0x02              ; LED 5 on RC2
led6    EQU     0x04              ; LED 6 on RC4
led7    EQU     0x03              ; LED 7 on RC3

;*---------- Variable definitions. ----------*

; For receiver:

ser_st  EQU     0x08              ; Serial state pointer.
rec_d   EQU     0x09              ; Receive data shift register.
new_d   EQU     0x0A              ; Received_data.

; For command handler:

cmd_st  EQU     0x0B

; For signal output:
 
sig_msk EQU     0x0C              ; Mask for outputs.
wrap_c  EQU     0x0D              ; Counter.
led_st  EQU     0x0E   
led0_c  EQU     0x10              ; Counter values
led1_c  EQU     0x11
led2_c  EQU     0x12
led3_c  EQU     0x13
led4_c  EQU     0x14
led5_c  EQU     0x15
led6_c  EQU     0x16
led7_c  EQU     0x17
led0_t  EQU     0x18              ; Timer values. Use bank 3.
led1_t  EQU     0x19
led2_t  EQU     0x1A
led3_t  EQU     0x1B
led4_t  EQU     0x1C
led5_t  EQU     0x1D
led6_t  EQU     0x1E
led7_t  EQU     0x1F

; General:

cmd_fl  EQU     0x0F

;*---------- Program flag definitions. ----------*

dav     EQU     0x00              ; Data available flag from receiver.
dend    EQU     0x01              ; Serial receiver end of byte flag. 
bs      EQU     0x02              ; Select bank for counter. 

;*---------- Constant definitions. ----------*

led_srl EQU     0x09              ; Length of LED subroutines.

;**********************************************************************
;* The program starts here after reset.
;**********************************************************************

        ORG     0x3FF             ; Processor reset vector.

        movlw   0x38              ; Osc. cal. value.

        ORG     0x000             ; Coding begins here.

startofprog

        movwf   OSCCAL            ; Update register with factory cal value. 
        clrf    FSR               ; Ensure FSR register points to Bank0. 
        goto    init_code         ; Jump over a few words to enable
                                  ; reprogramming.
        ORG     0x006                                 

;*****************************************************
;*---------- Serial receive state machine. ----------*
;*****************************************************

;*---------- Jump to current state. ----------*

serial_proc

        movf    ser_st, W         ; t2 
        movwf   PCL               ; t3, t4

;*---------- Idle state. Must see stop bit to leave it. ----------*

ser_idle

        btfss   PORTC, din        ; t5
        movlw   wait_start       
        movwf   ser_st
        nop
        retlw   led_srl           ; t9, t10

;*---------- Wait for start bit. ----------*

wait_start

        btfsc   PORTC, din
        movlw   sample_data
        movwf   ser_st
        bcf     cmd_fl, dend      ; Clear data end flag.
        retlw   led_srl

;*---------- Sample data in. ----------*

sample_data

        btfsc   PORTC, din
        bcf     rec_d, 7          ; Clear data bit if input is logic 0.
        movlw   get_end_bit
        movwf   ser_st
        retlw   led_srl

;*---------- Wait for next bit. ----------*

get_end_bit

        btfss   rec_d, 0
        bsf     cmd_fl, dend    
        movlw   shift_data
        movwf   ser_st
        retlw   led_srl

;*---------- Shift data in. ----------*

shift_data

        rrf     rec_d, F          ; Shift received data. 
        bsf     rec_d, 7          ; Preset current bit to 7.
        movlw   test_bitnr
        movwf   ser_st
        retlw   led_srl

;*---------- Check if it is the last bit. ----------*

test_bitnr

        movlw   sample_data
        btfsc   cmd_fl, dend
        movlw   sample_last       ; Last bit if start bit is in rec_bit.
        movwf   ser_st
        retlw   led_srl

;*---------- Sample last bit. ----------*     

sample_last
  
        btfsc   PORTC, din
        bcf     rec_d, 7          ; Clear data bit if input is 0.
        movlw   wait_end

ser_return

        movwf   ser_st
        retlw   led_srl

;*---------- Wait before sampling stop bit. ----------*

wait_end

        movlw   set_new_data
        goto    ser_return

;*---------- Move new data. ----------*

set_new_data

        movf    rec_d, W
        movwf   new_d
        movlw   preset_rec
        movwf   ser_st
        retlw   led_srl

;*---------- Set receive data to all 1's. ----------*

preset_rec

        movlw   0xFF
        movwf   rec_d
        movlw   sample_stop
        movwf   ser_st
        retlw   led_srl
 
;*---------- Sample stop bit. ----------*

sample_stop

        btfss   PORTC, din
        bsf     cmd_fl, dav       ; Set dav if correct stop bit.
        movlw   ser_idle
        movwf   ser_st
        retlw   led_srl

;****************************************************
;*---------- Timer handler state machine. ----------*
;****************************************************

led0_st

        movf    led0_t, W         ; t14
        bsf     STATUS, PA0       ; t15  
        call    led_table         ; t16, t17
        bcf     STATUS, PA0       ; t22     
        btfss   cmd_fl, bs        ; t23
        bcf     FSR, 6            ; t24
        movwf   led0_c            ; t25
        call    serial_proc       ; Ser call 3 
        goto    continue_loop     ; t11, t12       

led0_cst
         
        btfss   cmd_fl, bs        ; t14
        bcf     FSR, 6 
        btfsc   led0_t, 6         
        incf    led0_t, F
        btfss   led0_t, 6
        decf    led0_t, F         
        btfsc   led0_t, 7 
        incf    led0_t, F
        goto    continue_short    ; t22, t23  


led1_st

        movf    led1_t, W         ; t14
        bsf     STATUS, PA0       ; t15  
        call    led_table         ; t16, t17

        bcf     STATUS, PA0       ; t22     
        btfss   cmd_fl, bs        ; t23
        bcf     FSR, 6            ; t24
        movwf   led1_c            ; t25
        call    serial_proc       ; Ser call 3 
        goto    continue_loop     ; t17, t18       

led1_cst

        btfss   cmd_fl, bs        ; t14
        bcf     FSR, 6 
        btfsc   led1_t, 6
        incf    led1_t, F
        btfss   led1_t, 6
        decf    led1_t, F         
        btfsc   led1_t, 7 
        incf    led1_t, F
        goto    continue_short    ; t22, t23  


led2_st

        movf    led2_t, W         ; t14
        bsf     STATUS, PA0       ; t15  
        call    led_table         ; t16, t17
        bcf     STATUS, PA0       ; t22     
        btfss   cmd_fl, bs        ; t23
        bcf     FSR, 6            ; t24
        movwf   led2_c            ; t25
        call    serial_proc       ; Ser call 3 
        goto    continue_loop     ; t17, t18       

led2_cst

        btfss   cmd_fl, bs        ; t14
        bcf     FSR, 6 
        btfsc   led2_t, 6
        incf    led2_t, F
        btfss   led2_t, 6
        decf    led2_t, F         
        btfsc   led2_t, 7 
        incf    led2_t, F
        goto    continue_short    ; t22, t23  


led3_st

        movf    led3_t, W         ; t14
        bsf     STATUS, PA0       ; t15  
        call    led_table         ; t16, t17
        bcf     STATUS, PA0       ; t22     
        btfss   cmd_fl, bs        ; t23
        bcf     FSR, 6            ; t24
        movwf   led3_c            ; t25
        call    serial_proc       ; Ser call 3 
        goto    continue_loop     ; t17, t18       

led3_cst

        btfss   cmd_fl, bs        ; t14
        bcf     FSR, 6 
        btfsc   led3_t, 6
        incf    led3_t, F
        btfss   led3_t, 6
        decf    led3_t, F         
        btfsc   led3_t, 7 
        incf    led3_t, F
        goto    continue_short    ; t22, t23  


led4_st

        movf    led4_t, W         ; t14
        bsf     STATUS, PA0       ; t15  
        call    led_table         ; t16, t17
        bcf     STATUS, PA0       ; t22     
        btfss   cmd_fl, bs        ; t23
        bcf     FSR, 6            ; t24
        movwf   led4_c            ; t25
        call    serial_proc       ; Ser call 3 
        goto    continue_loop     ; t17, t18       

led4_cst

        btfss   cmd_fl, bs        ; t14
        bcf     FSR, 6 
        btfsc   led4_t, 6
        incf    led4_t, F
        btfss   led4_t, 6
        decf    led4_t, F         
        btfsc   led4_t, 7 
        incf    led4_t, F
        goto    continue_short    ; t22, t23  

led5_st

        movf    led5_t, W         ; t14
        bsf     STATUS, PA0       ; t15  
        call    led_table         ; t16, t17
        bcf     STATUS, PA0       ; t22     
        btfss   cmd_fl, bs        ; t23
        bcf     FSR, 6            ; t24
        movwf   led5_c            ; t25
        call    serial_proc       ; Ser call 3 
        goto    continue_loop     ; t17, t18       

led5_cst

        btfss   cmd_fl, bs        ; t14
        bcf     FSR, 6 
        btfsc   led5_t, 6
        incf    led5_t, F
        btfss   led5_t, 6
        decf    led5_t, F         
        btfsc   led5_t, 7 
        incf    led5_t, F
        goto    continue_short    ; t22, t23  


led6_st

        movf    led6_t, W         ; t14
        bsf     STATUS, PA0       ; t15  
        call    led_table         ; t16, t17
        bcf     STATUS, PA0       ; t22     
        btfss   cmd_fl, bs        ; t23
        bcf     FSR, 6            ; t24
        movwf   led6_c            ; t25
        call    serial_proc       ; Ser call 3 
        goto    continue_loop     ; t17, t18       

led6_cst

        btfss   cmd_fl, bs        ; t14
        bcf     FSR, 6 
        btfsc   led6_t, 6
        incf    led6_t, F
        btfss   led6_t, 6
        decf    led6_t, F         
        btfsc   led6_t, 7 
        incf    led6_t, F
        goto    continue_short    ; t22, t23  

led7_st

        movf    led7_t, W         ; t14
        bsf     STATUS, PA0       ; t15  
        call    led_table         ; t16, t17
        bcf     STATUS, PA0       ; t22     
        btfss   cmd_fl, bs        ; t23
        bcf     FSR, 6            ; t24
        movwf   led7_c            ; t25
        call    serial_proc       ; Ser call 3 
        goto    continue_loop     ; t11, t12      

led7_cst

        btfss   cmd_fl, bs        ; t14
        bcf     FSR, 6 
        btfsc   led7_t, 6
        incf    led7_t, F
        btfss   led7_t, 6
        decf    led7_t, F         
        btfsc   led7_t, 7 
        incf    led7_t, F
        goto    continue_short    ; t22, t23  

;---------- LED counter idle state. ----------*

led_idle_st

        movlw   0x04              ; t14
        decfsz  wrap_c, F         ; 
        goto    led_no_wrap       ; t16, t17
        xorwf   cmd_fl, F         ; Toggle bs flag.    
        movlw   0x10              ; t18
        movwf   wrap_c            ; Re-initialize counter. 
        movlw   led0_st - led_srl 

led_set_state 

        movwf   led_st            ; Next state - 9.
        goto    continue_short    ; t22, t23     

led_no_wrap               
        
        movlw   led7_cst          ; led_idle_st - 9.
        goto    led_set_state     ; t19, t20

;***********************************************
;*---------- Short delay subroutine. ----------*
;***********************************************

short_delay

        retlw   1        

;******************************************************
;*---------- Command handler state machine. ----------*
;******************************************************

idle_w_dav 

        goto    idle_w_dav_c

wait_dav 

        goto    wait_dav_c

check_hi_addr

        goto    check_hi_addr_c

perform_cmd

;*---------- Perform the command. ----------*

        movlw   wait_dav          ; Return to wait_dav enables several LEDs
                                  ; to be controlled without giving new addr.
        movwf   cmd_st
        movf    new_d, W
        iorlw   0x78              ; Up to bank 3 ledX_t variables. 
        movwf   FSR
        btfsc   new_d, 3
        goto    perform_cmd_c        
        bsf     INDF, 6
        btfss   new_d, 5
        bcf     INDF, 6  

perform_cmd_e

        goto    main_loop

perform_cmd_c

        goto    perform_cmd_e
 
;*---------- Idle state, wait for dav. ----------*

        ORG     0x100

idle_w_dav_c

        movlw   my_addr           ; t16
        xorwf   new_d, W          ; Check for my address.
        movlw   wait_dav          ; Default next state
        btfsc   cmd_fl, dav
        btfss   STATUS, Z
        movlw   idle_w_dav        ; Stay if no dav or address mismatch.  
        movwf   cmd_st
        bcf     cmd_fl, dav
        goto    main_loop         ; t24, t25

;*---------- Address match found, wait for new dav. ----------*

wait_dav_c

        movlw   my_addr
        xorwf   new_d, W
        movlw   check_hi_addr
        btfsc   cmd_fl, dav
        btfsc   STATUS, Z
        movlw   wait_dav          ; Stay here if no dav or address match.
        movwf   cmd_st
        bcf     cmd_fl, dav
        goto    main_loop

;*---------- Check high address bits. ----------*

check_hi_addr_c

        movlw   hi_addr
        xorwf   new_d, F
        movlw   0xd0
        andwf   new_d, W          ; Mask out relevant bits, and set Z flag.
        movlw   perform_cmd
        btfss   STATUS, Z
        movlw   idle_w_dav        ; Go to idle if there is hi addr mismatch.
        movwf   cmd_st 
        goto    main_loop

;***********************************************
;*---------- End of command handler. ----------*
;***********************************************
  
init_code

        movlw   0xc7              ; Internal timer clock, max prescaler.
        option 
        clrf    PORTB             ; Clear output ports.             
        clrf    PORTC 
        movlw   0x38              ; Set output ports to out. 
        tris    PORTB             
        movlw   0x20
        tris    PORTC  
        movlw   ser_idle
        movwf   ser_st            ; Initial state of serial receiver.
        movlw   idle_w_dav
        movwf   cmd_st            ; Initial state of command handler.  
        movlw   led7_st
        movwf   led_st            ; Initialize LED state.
        movlw   0x04
        movwf   wrap_c

init_clr_loop
         
        clrf    led0_t
        clrf    led1_t
        clrf    led2_t
        clrf    led3_t
        clrf    led4_t
        clrf    led5_t
        clrf    led6_t
        clrf    led7_t
        movlw   0x80
        movwf   led0_c            ; Clear all pulse counters.  
        movwf   led1_c
        movwf   led2_c
        movwf   led3_c
        movwf   led4_c
        movwf   led5_c
        movwf   led6_c
        movwf   led7_c
        movlw   0x20
        addwf   FSR, F
        decfsz  wrap_c, F
        goto    init_clr_loop     ; Loop over all four banks.  

        clrf    cmd_fl            ; Clear bs, dav and dend flags
        clrf    sig_msk           ; Clear the mask for output values. 
        movlw   0xff
        movwf   rec_d             ; Set receiver shift register to all 1's.  
        movlw   0x1
        movwf   wrap_c            ; Initialize intensity counter to 1.

;******************************************
;*---------- Main program loop. ----------*
;******************************************

main_loop
        
        call    serial_proc       ; Ser call 0, returns subroutine length. 
        addwf   led_st, F         ; Set next state for LED control.
        btfsc   cmd_fl, bs
        bcf     FSR, 6            ; Select bank 1 or 3.               
        decf    led6_c, F       
        rlf     led6_c, W
        rlf     sig_msk, F
        decf    led7_c, F
        rlf     led7_c, W          
        rlf     sig_msk, F
        decf    led5_c, F
        rlf     led5_c, W         
        rlf     sig_msk, F
        decf    led4_c, F
        rlf     led4_c, W           
        rlf     sig_msk, F
        call    serial_proc       ; Ser call 1.
        decf    led3_c, F
        decf    led2_c, F  
        rlf     led3_c, W          
        rlf     sig_msk, W 
        movwf   PORTC             ; t15
        rlf     led2_c, W         
        rlf     sig_msk, F
        decf    led1_c, F
        rlf     led1_c, W          
        rlf     sig_msk, F      
        decf    led0_c, F
        rlf     led0_c, W
        rlf     sig_msk, W
        bsf     FSR, 6
        movwf   PORTB             ; t25 
        call    serial_proc       ; Ser call 2.

;*---------- Jump to current LED intensity control state. ----------*   

        movf    led_st, W
        movwf   PCL               ; t12, t13, jump to LED state.    

;*---------- Continue the main loop after executing LED state. ----------*

continue_short

        btfss   cmd_fl, bs      
        bcf     FSR, 6 
        call    serial_proc       ; Ser call 3.
        goto    continue_loop

continue_loop

        movlw   0x40
        xorwf   FSR, F            ; Select bank 1 or 3.               
        decf    led6_c, F        
        rlf     led6_c, W         
        rlf     sig_msk, F
        decf    led7_c, F
        rlf     led7_c, W           
        rlf     sig_msk, F
        decf    led5_c, F
        rlf     led5_c, W  
        rlf     sig_msk, F
        decf    led4_c, F 
        decf    led3_c, F 
        call    serial_proc       ; Ser call 4, returns 1.
        rlf     led4_c, W          
        rlf     sig_msk, F
        rlf     led3_c, W           
        rlf     sig_msk, W
        movwf   PORTC             ; t15
        decf    led2_c, F
        rlf     led2_c, W          
        rlf     sig_msk, F
        decf    led1_c, F
        rlf     led1_c, W            
        rlf     sig_msk, F 
        decf    led0_c, F
        rlf     led0_c, W
        rlf     sig_msk, W
        movwf   PORTB             ; t25
        call    serial_proc       ; Ser call 5.

;*---------- Jump to current command handler state. ----------*

command_proc

        movf    cmd_st, W         ; t11
        movwf   PCL               ; t12, t13

;*----- The command procedure ends with a jump to main_loop. -----* 

;******************************************
;*---------- LED intensity table. --------*
;******************************************
    
        ORG     0x200             ; Upper program bank

led_table

        addwf   PCL, F            ; t18, t19

        ;*----- Decreasing intensity table. ----------*

        retlw   0x80              ; t20, t21
        retlw   0x81              ; All values are intensity + 0x80.
        retlw   0x80
        retlw   0x80
        retlw   0x81
        retlw   0x80
        retlw   0x80
        retlw   0x81

        retlw   0x80
        retlw   0x80
        retlw   0x81
        retlw   0x80
        retlw   0x80
        retlw   0x81
        retlw   0x80
        retlw   0x81

        retlw   0x80
        retlw   0x81
        retlw   0x80
        retlw   0x81
        retlw   0x80
        retlw   0x81
        retlw   0x80
        retlw   0x81

        retlw   0x80
        retlw   0x81
        retlw   0x80
        retlw   0x81
        retlw   0x80
        retlw   0x81
        retlw   0x80
        retlw   0x81

        retlw   0x81
        retlw   0x81
        retlw   0x81
        retlw   0x81
        retlw   0x81
        retlw   0x82
        retlw   0x82
        retlw   0x82

        retlw   0x83
        retlw   0x83
        retlw   0x83
        retlw   0x84
        retlw   0x84
        retlw   0x85
        retlw   0x85
        retlw   0x86

        retlw   0x87
        retlw   0x88
        retlw   0x8a
        retlw   0x8b
        retlw   0x8d
        retlw   0x8f
        retlw   0x91
        retlw   0x93

        retlw   0x96
        retlw   0x9a
        retlw   0x9e
        retlw   0xa1
        retlw   0xa6
        retlw   0xab
        retlw   0xb0
        retlw   0xc0

        ;*----- Increasing intensity table. -----*

        retlw   0x80
        retlw   0x81
        retlw   0x81
        retlw   0x81
        retlw   0x81
        retlw   0x81
        retlw   0x82
        retlw   0x82


        retlw   0x82
        retlw   0x82
        retlw   0x83
        retlw   0x83
        retlw   0x83
        retlw   0x84
        retlw   0x84
        retlw   0x84

        retlw   0x85
        retlw   0x85
        retlw   0x85
        retlw   0x86
        retlw   0x86
        retlw   0x87
        retlw   0x87
        retlw   1x87

        retlw   0x88
        retlw   0x89
        retlw   0x89
        retlw   0x8a
        retlw   0x8b
        retlw   0x8c
        retlw   0x8c
        retlw   0x8d

        retlw   0x8e
        retlw   0x8f
        retlw   0x91
        retlw   0x92
        retlw   0x94
        retlw   0x96
        retlw   0x97
        retlw   0x99

        retlw   0x9b
        retlw   0x9e
        retlw   0xa0
        retlw   0xa3
        retlw   0xa6
        retlw   0xa9
        retlw   0xac
        retlw   0xae

        retlw   0xb0
        retlw   0xb2
        retlw   0xb4
        retlw   0xb6
        retlw   0xb7
        retlw   0xb9
        retlw   0xba
        retlw   0xbb

        retlw   0xbc
        retlw   0xbd
        retlw   0xbd
        retlw   0xbe
        retlw   0xbf
        retlw   0xbf
        retlw   0xbf
        retlw   0xc0

;*----- The End. -----* 

endofprog

        END                       ; directive 'end of program'

