;
; _A.Fenyo 2003 - 2004
;

include p16f876.inc

		list	P=PIC16F876

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; local variables used by functions

		cblock	H'20'
		VARPAUSEBCL0, VARPAUSEBCL1, VARPAUSEBCL2
		VAR_READ_SERIAL, VAR_WRITE_INTRO
		VAR_TURN_LED_W, VAR_TURN_LED_VAL
		VAR_CMD
		endc

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; module address

		org 0

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; avoid assembler warning message "Register in operand not in bank 0"

		errorlevel -302

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; define constants

		constant led_bit=4
		constant cts_bit=5
		constant L293D_control_A=7
		constant L293D_control_B=6
		constant L293D_enable=5

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; initial values for variables used by functions

		banksel	VAR_TURN_LED_VAL
		CLRF	VAR_TURN_LED_VAL

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; initialize PCLATH for GOTO and CALL instructions

		errorlevel -306
INITPCLATH	pagesel	FIRSTLIB
		CALL	FIRSTLIB
		pagesel	INITPCLATH
		errorlevel +306

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; start of program

; C0-2 as output
		banksel TRISB
	        BCF	TRISB, L293D_control_A
	        BCF	TRISB, L293D_control_B
	        BCF	TRISB, L293D_enable

; clear C0-2
		banksel PORTB
	        BCF	PORTB, L293D_control_A
	        BCF	PORTB, L293D_control_B
	        BCF	PORTB, L293D_enable	; disable stepper
myCmdLOOP

; read serial to W
		errorlevel -306
		pagesel READ_SERIAL
		CALL    READ_SERIAL
		CALL	WRITE_SERIAL
                CALL    CHANGE_LED
		pagesel INITPCLATH
		errorlevel +306

		banksel	VAR_CMD
		MOVWF	VAR_CMD		; save the character read

		SUBLW	H'41'		; compare to ascii 'A'
		BTFSC	STATUS, Z
		CALL	CMD_A		; call "A" command procedure

		banksel	VAR_CMD
		MOVF	VAR_CMD, W
		SUBLW	H'42'		; compare to ascii 'B'
		BTFSC	STATUS, Z
		CALL	CMD_B		; call "B" command procedure

		banksel	VAR_CMD
		MOVF	VAR_CMD, W
		SUBLW	H'43'		; compare to ascii 'C'
		BTFSC	STATUS, Z
		CALL	CMD_C		; call "C" command procedure

		banksel	VAR_CMD
		MOVF	VAR_CMD, W
		SUBLW	H'44'		; compare to ascii 'D'
		BTFSC	STATUS, Z
		CALL	CMD_D		; call "D" command procedure

		banksel	VAR_CMD
		MOVF	VAR_CMD, W
		SUBLW	H'45'		; compare to ascii 'E'
		BTFSC	STATUS, Z
		CALL	CMD_E		; call "E" command procedure

		GOTO	myCmdLOOP

CMD_A		banksel PORTB
	        BCF	PORTB, L293D_control_A
	        BCF	PORTB, L293D_control_B
	        BSF	PORTB, L293D_enable	; enable stepper
		RETURN

CMD_B		banksel PORTB
	        BCF	PORTB, L293D_control_A
	        BSF	PORTB, L293D_control_B
	        BSF	PORTB, L293D_enable	; enable stepper
		RETURN

CMD_C		banksel PORTB
	        BSF	PORTB, L293D_control_A
	        BCF	PORTB, L293D_control_B
	        BSF	PORTB, L293D_enable	; enable stepper
		RETURN

CMD_D		banksel PORTB
	        BSF	PORTB, L293D_control_A
	        BSF	PORTB, L293D_control_B
	        BSF	PORTB, L293D_enable	; enable stepper
		RETURN

CMD_E		banksel PORTB
	        BSF	PORTB, L293D_control_A
	        BSF	PORTB, L293D_control_B
	        BCF	PORTB, L293D_enable	; disable stepper
		RETURN

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; end of program

THEEND		GOTO	0	; reset

; Starting from here: Alexandre Fenyo's BIOS for Microchip PICs
: This BIOS is able to drive the RS-232-C interface in order to erase and flash
; the EEPROM memory. No need to use an EEPROM programer to debug your code !


;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; start of 1st LIBRARY in program page 3

FIRSTLIB	org	H'1800'
;FIRSTLIB	org	H'1000'

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; initialize I/O ports

		banksel TRISC		; bank 1
	        BCF	TRISC, led_bit	; C4 (red led) operates as output

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; initialize UART

		MOVLW	H'02'		; wait .4 sec
		CALL	PAUSE

		BSF	TRISC, 6	; C6-7 must be set to use UART
		BSF	TRISC, 7

	        BCF	TRISC, cts_bit	; C5 (CTS) signal operates as output

; high speed UART
; set communications speed (with 3.6864 MHz ext. oscillator)
		BCF	TXSTA, BRGH

;		MOVLW	H'BF'		; 300 b/s
		MOVLW	H'00'		; 57600 b/s

		MOVWF	SPBRG

		banksel RCSTA		; bank 0
		BSF	RCSTA, SPEN	; use UART

		banksel	TXSTA		; bank 1
		BCF	TXSTA, SYNC	; async UART
		BSF	TXSTA, TXEN	; enable transmit

		banksel	RCSTA		; bank 0
		BSF	RCSTA, CREN	; enable receive
		BCF	PORTC, cts_bit	; clear C5 (set CTS)

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; initial sequence (test peripherals)

; ToDo: call change_led instead of this
INIT	        BSF	PORTC, led_bit	; red led on

		MOVLW	H'02'		; wait .4 sec
		CALL	PAUSE

; ToDo: call change_led instead of this
	        BCF	PORTC, led_bit	; red led off

		MOVLW	H'02'		; wait .4 sec
		CALL	PAUSE

	        BSF	PORTC, led_bit	; red led on

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; write introduction text to rs232c

		CALL	WRITE_INTRO

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; "read" command

CMD_READ	MOVLW	H'3C'		; write ascii '<' to ask for address
		CALL	WRITE_SERIAL

		CALL	READ_SERIAL	; read memory address low byte
		banksel	EEADR
		MOVWF	EEADR		; save byte

		CALL	READ_SERIAL	; read memory address high byte
		banksel	EEADRH
		MOVWF	EEADRH		; save byte

		banksel EECON1
		BSF	EECON1, EEPGD	; want to read flash

		BSF	EECON1, RD	; read flash
		NOP			; requires two cycles wait
		NOP

		banksel	EEDATA
		MOVF	EEDATA, W	; read char from flash
		CALL	WRITE_SERIAL	; write 8 lowest bits of flash to serial

		banksel	EEDATH
		MOVF	EEDATH, W	; read char from flash
		CALL	WRITE_SERIAL	; write 6 highest bits of flash to serial
		RETURN

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; "write" command

CMD_WRITE	MOVLW	H'3E'		; write ascii '>' to ask for address
		CALL	WRITE_SERIAL

; this bit clear seems to stop the pic
;		banksel	EECON1		; bank 3
;		BCF	EECON1, WRERR	; clear write error bit

		CALL	READ_SERIAL	; read memory address low byte
		banksel	EEADR
		MOVWF	EEADR		; save byte

		CALL	READ_SERIAL	; read memory address high byte
		banksel	EEADRH
		MOVWF	EEADRH		; save byte

		MOVLW	H'26'		; write ascii '&' to ask for data
		CALL	WRITE_SERIAL

		CALL	READ_SERIAL	; read low byte of data
		banksel	EEDATA
		MOVWF	EEDATA		; save byte

		CALL	READ_SERIAL	; read high byte of data
		banksel	EEDATH
		MOVWF	EEDATH		; save byte

		banksel	EECON1		; bank 3
		BSF	EECON1, EEPGD	; want to write flash
		BSF	EECON1, WREN	; enable write on flash memory

		MOVLW	0x55		; magic op code
		MOVWF	EECON2		; magic op code
		MOVLW	0xAA		; magic op code
		MOVWF	EECON2		; magic op code

		BSF	EECON1, WR	; start write operation
		NOP			; requires two cycles wait
		NOP

CMD_WRITE_WAIT	BTFSC	EECON1, WR	; wait until write cycle completion
		GOTO	CMD_WRITE_WAIT

		BCF	EECON1, WREN	; disable write on flash memory

		MOVLW	H'21'		; write ascii '!' to inform about end of flash operation
		CALL	WRITE_SERIAL
		RETURN

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; turn led on/off

CHANGE_LED	banksel	RCSTA		; default bank 0
		MOVWF	VAR_TURN_LED_W	; save W
		MOVLW	H'01'		; invert bit 0 in VAR_TURN_LED_VAL
		XORWF	VAR_TURN_LED_VAL, F
	        BCF	PORTC, led_bit	; red led off
		BTFSS	STATUS, Z	; check parity of VAR_TURN_LED_VAL
	        BSF	PORTC, led_bit	; red led on
		MOVF	VAR_TURN_LED_W, W	; restore W
		RETURN

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; write a char to rs232c
; input : W
; output : W not altered

WRITE_SERIAL	CALL	CHANGE_LED
		banksel	RCSTA		; default bank 0
		BTFSS	PIR1, TXIF
		GOTO	WRITE_SERIAL
		MOVWF	TXREG
		RETURN

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; read a char from rs232c
; output : W

READ_SERIAL	banksel	RCSTA		; default bank 0
READ_SERIAL_2	BTFSS	PIR1, RCIF	; test buffer
		GOTO	READ_SERIAL_2	; nothing to read

; a byte can be read
		MOVF	RCSTA, W	; check error bits
		MOVWF	VAR_READ_SERIAL
		BTFSC	VAR_READ_SERIAL, OERR	; test overrun
		GOTO	RS_RESET	; reset USART receiver
		BTFSS	VAR_READ_SERIAL, FERR	; test framing error
		GOTO	RS_END		; go to read byte

RS_RESET	MOVF	RCREG, W	; must read register
		BCF	RCSTA, CREN	; reset USART receiver
		BSF	RCSTA, CREN
		GOTO 	READ_SERIAL

RS_END		MOVF	RCREG, W	; read byte
		RETURN		

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; short PAUSE function
; number of cycles :
; 3 + ((((255 * 3 + 2) + 3) * 255 + (255 * 3 + 2) + 2) + 3) * W
;   +  (((255 * 3 + 2) + 3) * 255 + (255 * 3 + 2) + 2) + 2 + 2
; = 197122 * W + 197123
; nearly equal to 197123 * W = 0.214 * W sec (with 3.6864MHz ext. oscillator)

PAUSE		banksel	VARPAUSEBCL0
		MOVWF	VARPAUSEBCL0
		CLRF	VARPAUSEBCL1
		CLRF	VARPAUSEBCL2
PAUSE_1		DECFSZ	VARPAUSEBCL2, F
		GOTO	PAUSE_1
		DECFSZ	VARPAUSEBCL1, F
		GOTO	PAUSE_1
		DECFSZ	VARPAUSEBCL0, F
		GOTO	PAUSE_1
		RETURN

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; write introduction text to rs232c
; since index counter is a 8 bit register, string length must be 256 characters max

WRITE_INTRO	banksel	VAR_WRITE_INTRO
		CLRF	VAR_WRITE_INTRO		; clear index counter
		CLRW
WRITE_INTR_LOOP	CALL	GET_INTRO		; get one char
		BTFSC	STATUS, Z		; last char is null ?
		RETURN				; return

		CALL	WRITE_SERIAL		; write char

		INCF	VAR_WRITE_INTRO, F	; increment index counter
		MOVF	VAR_WRITE_INTRO, W
		GOTO	WRITE_INTR_LOOP		; loop : get next char

GET_INTRO	banksel	EEADR
		ADDLW	LOW(STR_INTRO)		; compute address of text string
		MOVWF	EEADR			; select address

		MOVLW	HIGH(STR_INTRO)
		BTFSC	STATUS, C
		ADDLW	1
		MOVWF	EEADRH

		banksel EECON1
		BSF	EECON1, EEPGD		; want to read flash

		BSF	EECON1, RD		; read flash
		NOP				; requires two cycles wait
		NOP

		banksel	EEDATA
		MOVF	EEDATA, W		; read char

		banksel	RCSTA			; default bank 0
		RETURN				; return char

STR_INTRO
		DA h'000D'
		DA h'000A'
		DA " M i c r o c h i p   P I C   1 6 F 8 7 x   B I O S"
		DA h'000D'
		DA h'000A'
		DA " c o n t a c t   :   a l e x @ f e n y o . n e t"
		DA h'000D'
		DA h'000A'
		DA " 3 . 6 8 6 4   M H z   e x t e r n a l   o s c i l l a t o r"
		DA h'000D'
		DA h'000A'
		DA " 1   a s y n c   s e r i a l   p o r t"
		DA h'000D'
		DA h'000A'
		DA " 1   h i g h   p o w e r   l a t c h i n g   r e l a y   [ 6 0 A   2 5 0 V A C ]"
		DA h'000D'
		DA h'000A'
		DA h'0000'

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		org	H'2007'
		DATA	_HS_OSC & _WDT_OFF & _PWRTE_ON & _BODEN_OFF & _LVP_OFF & _CPD_OFF & _WRT_ENABLE_ON & _DEBUG_OFF & _CP_OFF

		END