;
; led-ctrl.asm
;
; Accept serial data at 9600 8N1 and interpret it
; If a '1' is received, turn on an LED connected to RB4
; If a '0' is received, turn off the LED connected to RB4
;
; Serial TX line is connected via a 10K resistor to RA0
;
; PORT MAP
;
; PORTA
;    0    Input   Serial
;
; PORTB
;    4    Output  LED
;
; Based on code by Tom Coonan
;
; Mark Crosbie  9/29/98
;

	include p16c84.inc
	radix dec
	processor 16c84

SERIAL_BIT	equ	0	; Bit 0 on PORTA
LED_BIT		equ	4	; Bit 4 on PORTB

;**** Application's Memory Variables
INCHAR    equ   0xc   ; Input character
COUNTHI   equ   0x10  ; Counter for Delay routine (MSB)
COUNTLO   equ   0x11  ; Counter for Delay routine (LSB)
DELAYHI   equ   0x12  ; Argument for DELAY subroutine (MSB)
DELAYLO   equ   0x13    ; Argument for DELAY subroutine (LSB)
MASK      equ   0x14    ; Mask register for each serial bit input
TEMP      equ   0x15    ;

; After the baud rates are established through the jumpers, the right
; delay constants are written into here.  These are the constants
; used in all the run-time delays for serial.
;
DELAY_HALF_SERIAL_HI  equ   20   ;
DELAY_HALF_SERIAL_LO  equ   21   ;
DELAY_FULL_SERIAL_HI  equ   22   ;
DELAY_FULL_SERIAL_LO  equ   23   ;

;*** End MEMORY Registers **********************************

; DELAY Constants.
;
; Delays were set emperically - using a Logic Analyzer and
;    looking at peek signals for bit timing.  ASSUMES the
;    current design's 4MHz crystal, of course.

DELAY_FULL_9600_HI   equ    1
DELAY_FULL_9600_LO   equ   25

DELAY_HALF_9600_HI   equ    1
DELAY_HALF_9600_LO   equ   12

DELAY_50MS_HI        equ 0x34
DELAY_50MS_LO        equ 0x95


	org     0       ; Reset Vector
	goto    Start       ; Main entry point of program

;****************************************
;                                       *
;  Start Up Initializtion Stuff         *
;                                       *
;****************************************

; Remember, INCHAR started out as all zeros..
;    Therefore, we only need to SET bits (when PORTA:0 = 0V)
; Also, remember, serial is inverted:
;    0V on PORTA:0 indicates a logic '1'
;    5V on PORTA:0 indicates a logic '0'
;
; The MASK variable is maintained by caller.
;
;
GetSerialBit
	nop
	nop

	btfsc   PORTA, SERIAL_BIT  
		; Get PORTA with a 'POSITIVE' serial bit on it
	retlw   0           ; Do nothing if we got 5V, or logic '0'

GetSerialBitIsClr
	movf    MASK, W
	iorwf   INCHAR, f      ; SET the bit
	retlw   0

;***********************************************************
;                                                          *
; Specific Delay Functions.  These are specific delays     *
; which load specific delay counts.  These routines then   *
; call the generic DELAY subroutine.                       *
;                                                          *
;***********************************************************

DelayHalfSerial
	movf    DELAY_HALF_SERIAL_HI, W
	movwf   DELAYHI
	movf    DELAY_HALF_SERIAL_LO, W
	movwf   DELAYLO
	retlw   0

DelayFullSerial
	; Baud rate is 9600, so set up accorsingly
	movf    DELAY_FULL_SERIAL_HI, W
	movwf   DELAYHI
	movf    DELAY_FULL_SERIAL_LO, W
	movwf   DELAYLO
	retlw   0

Delay50MS
	movlw   DELAY_50MS_HI
	movwf   DELAYHI
	movlw   DELAY_50MS_LO
	movwf   DELAYLO
	retlw   0

InitDelay200MS
	call    Delay50MS
	call    Delay
	call    Delay50MS
	call    Delay
	call    Delay50MS
	call    Delay
	call    Delay50MS
	call    Delay
	retlw   0

; General DELAY routine
; Inputs:
;    DELAYHI   -   Initial Countdown value MSB
;    DELAYLO   -   Initial Countdown value LSB
; Uses:
;    COUNTHI   -   Counter value MSB
;    COUNTLO   -   Counter value LSB

Delay    movf    DELAYHI, W
	movwf   COUNTHI
	movf    DELAYLO, W
	movwf   COUNTLO
DelayLoop
	decfsz  COUNTLO
	goto    DelayLoop
	decfsz  COUNTHI
	goto    DelaySetup2
	goto    DelayDone
DelaySetup2
	movf    DELAYLO, W
	movwf   COUNTLO
	goto    DelayLoop
DelayDone
	retlw   0

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ***** Main Program Entry !!!
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Start
	clrf    PORTA
	clrf    PORTB

	; Set up input and output bits
	bsf	STATUS, RP0
	bsf	TRISA, SERIAL_BIT	; PORTA.0 is input
	bcf	TRISB, LED_BIT		; PORTB.4 is output
	bcf	STATUS, RP0

	call    Delay50MS    ; Wait for tristate to settle...?...
	call    Delay

	;** Set up Baud Constants, based on BAUD jumper.
	movlw   DELAY_FULL_9600_HI
	movwf   DELAY_FULL_SERIAL_HI
	movlw   DELAY_FULL_9600_LO
	movwf   DELAY_FULL_SERIAL_LO
	movlw   DELAY_HALF_9600_HI
	movwf   DELAY_HALF_SERIAL_HI
	movlw   DELAY_HALF_9600_LO
	movwf   DELAY_HALF_SERIAL_LO

	call    InitDelay200MS

;******* START Main Loop Here **************
; Loop and read characters

WaitChar                     ; Wait for start bit

	clrf    INCHAR      ; clear the input character
	movlw   B'00000001' ; start out at bit 0
	movwf   MASK

	btfss   PORTA, SERIAL_BIT    ; If we get a '1' or a START bit, skip!
	goto    WaitChar    ; Nope.. no start bit. Keep looking

	; Got an edge!
	nop
	nop

	call    DelayHalfSerial   ; Delay half a bit
	call    Delay
	btfss   PORTA, SERIAL_BIT     ; Serial bit *should* be zero.
	goto    WaitChar    ; False alarm..

BitLoop
	; Delay one bit time
	call    DelayFullSerial
	call    Delay
	call    GetSerialBit  ; Get Bit into INCHAR

	bcf	STATUS, C	; Clear the carry bit
	rlf	MASK, F		; shift mask left one bit, thru carry
	btfss	STATUS, C	; is the carry set?
	goto BitLoop

	; Delay one bit time for the STOP bit
	call    DelayFullSerial
	call    Delay

	; See if the character is 0, if so, turn off the LED
	movf	INCHAR, w
	sublw	'0'
	btfsc	STATUS, Z
	bcf	PORTB, LED_BIT	; turn off the LED
	btfss	STATUS, Z
	bsf	PORTB, LED_BIT	; turn on the LED

	; A small pause between each sample
	call Delay50MS
	call Delay

	goto WaitChar

	end
