; serial.asm
; half-duplex software UART (hopefully)
; Adapted from Microchip App note AN555

; start out making serial output work
; then do serial input
; then do control

; Include the header file for our target chip
	#include "p16f84.inc"

; First line, declare that we want a 16F84 PIC
; and that our numbers will be in Hexadecimal
; unless otherwise noted
	LIST P=16f84, R=HEX

; Tell the programmer what options we want to use
; In this case:
; _WDT_OFF -- Watchdog Timer Off
; _XT_OSC -- Crystal Oscillator
; _CP_OFF -- Code Protect Off

	__CONFIG _WDT_OFF & _XT_OSC & _CP_OFF

; Let PORTA be for serial XMT/RCV
; and also control
; PORTB will be our 8-bit I/O

; Serial port at 4800baud = 208uS period
; Variables

RxBits		equ	10h		; state of output port
TxBits		equ	11h		; state of input port
TxData		equ	12h		; byte to output, destroyed in process
RxData		equ	13h

WRegSave	equ	14h		; place to store W in interrupt
StatusSave	equ	15h		; stores STATUS in interrupt

PortStatus	equ	16h		; serial port control flags

index		equ	0Ch
delay1		equ	0Dh

#define RxInProgress 	PortStatus, 0
#define TxInProgress 	PortStatus, 1
#define TxStopBit    	PortStatus, 2
#define RxError		PortStatus, 3
#define RxDone		PortStatus, 4

#define CycleOffset	h'0e'		; fudge factor in interrupt timing
#define BitPeriod	d'208'		; 208 us = 208 instructions w/4MHz crystal
#define DataBits	d'8'		; 8 bits


#define TX	PORTA, 0
#define RX	PORTA, 4

	org	0

	goto	Main

	org	4

	goto	Interrupt

Interrupt:
	btfss	INTCON, T0IF		; test for other interrupts
	retfie				; and return on them

	movwf	WRegSave		; save accumulator
	swapf	STATUS, w		; operation doesn't affect status bits
	movwf	StatusSave		; save (nibble-flipped) status reg

	btfsc	TxInProgress		; check to see if we're transmitting
	goto	TransmitNextBit		; handle it
	btfsc	RxInProgress		; check to see if we're receiving
	goto	ReceiveNextBit		; handle it
	goto	RxStartBit		; otherwise, it must be a start bit interrupt

; restore status, w and return from interrupt
EndOfInterrupt:
	swapf	StatusSave, w		; unflip and restore status reg
	movwf	STATUS			
	swapf	WRegSave, f		; restore W reg without affecting status reg
	swapf	WRegSave, w
	bcf	INTCON, T0IF		; reset TMR0 interrupt flag
	retfie

TransmitNextBit:
	bcf	STATUS, RP0		; make sure we're in bank 0

	movlw	-BitPeriod + CycleOffset	; load TMR0 to send next bit
	movwf	TMR0
	
	movf	TxBits, f		; check to see if transmission is over
	btfsc	STATUS, Z		
	goto	TransmitFinished	; if it is, send a stop bit

	decf	TxBits, f		
	goto	TransmitDataBit

TransmitFinished:
	btfsc	TxStopBit		; need to make sure we spend enough time sending
	goto	StopBitDone		; a stop bit, so spend another 208us transmitting
	bsf	TX			; stop bit is high
	bsf	TxStopBit
	goto	EndOfInterrupt

StopBitDone:
	bcf	TxStopBit
	bcf	INTCON, T0IE		; disable interrupt
	bcf	TxInProgress		; stop transmission
	goto	EndOfInterrupt

TransmitDataBit:
	rrf	TxData, f
	btfss	STATUS, C
	bcf	TX
 	btfsc	STATUS, C
	bsf	TX
	
	goto	EndOfInterrupt

TransmitStartBit:
	bsf	STATUS, RP0
	bcf	OPTION_REG, T0CS
	bsf	INTCON, T0IE
	bsf	INTCON, GIE
	bcf	STATUS, RP0
	bcf	TX			; start bit = low
	movlw	-BitPeriod
	movwf	TMR0
	return

; ---------------------------------------------
; SendByte
; sends a byte in W out over the serial port
; ---------------------------------------------
SendByte:

	bsf	TxInProgress
	movwf	TxData
	movlw	DataBits
	movwf	TxBits
	call	TransmitStartBit
	retfie

ReceiveNextBit:
;	bsf	STATUS, RP0			; bank 1
;	bsf	OPTION, T0CS
	bcf	STATUS, RP0
	movlw	-BitPeriod + CycleOffset	; load TMR0 to send next bit
	movwf	TMR0
	
	movf	RxBits, f			; check to see if transmission is over
	btfsc	STATUS, Z			
	goto	ReceiveFinished			; if it is, send a stop bit

	decf	RxBits, f
	goto	ReceiveDataBit
	
ReceiveFinished:
	btfss	RX
	bcf	RxError				; stop bit is high
	bcf	RxInProgress
	bsf	RxDone
	bcf	INTCON, T0IE			; disable interrupt
	goto	EndOfInterrupt

ReceiveDataBit:
	bcf	STATUS, C
	btfsc	RX
	bsf	STATUS, C
	rrf	RxData, f
	goto	EndOfInterrupt

RxStartBit:
	bsf	STATUS, RP0
	bcf	OPTION_REG, T0CS
	bsf	INTCON, T0IE
	bcf	STATUS, RP0
	bsf	RxInProgress
	movlw	-BitPeriod
	movwf	TMR0
	goto	EndOfInterrupt

GetByte:
	movlw	DataBits
	movwf	RxBits
	bcf	RxDone
	bsf	STATUS, RP0
	bsf	OPTION_REG, T0CS
	bsf	OPTION_REG, T0SE
	bsf	INTCON, T0IE
	bsf	INTCON, GIE
	bcf	STATUS, RP0
	movlw	h'FF'
	movwf	TMR0

	btfss	RxDone
	goto	$-1

	movf	RxData, w
	return

Main:
	bsf	STATUS, RP0
	movlw	b'00010000'
	movwf	TRISA
	bcf	STATUS, RP0
	clrf	index
	clrf	PortStatus
	bsf	TX
loop:	
	movf	index, w

	call	GetByte
	call	SendByte
;	call	Delay

	btfsc	TxInProgress
	goto	$-1
	incf	index, f
	goto	loop

Delay:
	clrf	delay1
Delay1:
	decfsz	delay1, f
	goto	Delay1
	return

	end