;******************************************************************************
;	Test Application
;
;	A sample user program for testing with the bootloader.
;	It echoes any characters received on the serial port
;	and it blinks an LED connected to PortC.5.
;
;	Written for a PIC18F14K22 chip
;	Written by VegiPete	picprog.strongedge.net
;
;	Version Control:
;	0.1	The beginning
;
#define	version	"0.1"
;
;	Hardware:
;	Internal RC OSC with 4x PLL enabled
;
;	RB4 - !CTS flow control output - drive low to receive
;	RB5 - RX - Serial in
;	RB7 - TX - Serial out
;
;	RC5 - LED
;	
;******************************************************************************
;Resources Used:
;	Serial Port	
;******************************************************************************

	LIST	P=18F14K22		;directive to define processor
	#include <P18F14K22.INC>	;processor specific variable definitions
	radix	dec
	errorlevel -311                 ;don't warn on HIGH() operator values >16-bits

;******************************************************************************
;
; CONFIGURATION WORD SETUP
;
; The 'CONFIG' directive is used to embed the configuration word within the .asm file.
; The labels following the directive are located in the respective .inc file. 
; See the data sheet for additional information on configuration word settings.
;
; The flash loader won't allow these to be changed, but they're useful for reference.
;
;******************************************************************************

     ;Setup CONFIG11H
     CONFIG FOSC = IRC, PLLEN = ON, PCLKEN = OFF, FCMEN = OFF, IESO = OFF
     ;Setup CONFIG2L
     CONFIG PWRTEN = ON, BOREN = OFF, BORV = 30
     ;Setup CONFIG2H
     CONFIG WDTEN = OFF, WDTPS = 1
     ;Setup CONFIG3H
     CONFIG MCLRE = ON, HFOFST = OFF
     ;Setup CONFIG4L
     CONFIG STVREN = OFF, LVP = OFF, BBSIZ = OFF, XINST = OFF
     ;Setup CONFIG5L
     CONFIG CP0 = OFF, CP1 = OFF
     ;Setup CONFIG5H
     CONFIG CPB = OFF, CPD = OFF
     ;Setup CONFIG6L
     CONFIG WRT0 = OFF, WRT1 = OFF
     ;Setup CONFIG6H
     CONFIG WRTB = OFF, WRTC = OFF, WRTD = OFF
     ;Setup CONFIG7L
     CONFIG EBTR0 = OFF, EBTR1 = OFF
     ;Setup CONFIG7H
     CONFIG EBTRB = OFF

;******************************************************************************
; PIC18F14K22 Characteristics
#define DEVICEID                .633
#define WRITE_FLASH_BLOCKSIZE   .16
#define ERASE_FLASH_BLOCKSIZE   .64
#define END_FLASH               0x4000
#define END_GPR                 0x200
#define	FLASH_LOADER_ADDR	0x3B00
#define	IDADDRESS		0x200000 ;location of ID in memory map
#define	CONFIGADDRESS		0x300000 ;location of CONFIG in memory map
#define	EEADDRESS		0xF00000 ;location of EE in memory map
#define	EE_LENGTH		0x100	 ;number of bytes of EEPROM

;
;serial port flow control - CTS low to permit incoming data
#define	CTS_PIN		4
#define	CTS_STOP	bsf	LATB,4
#define	CTS_GO		bcf	LATB,4
#define	TX_PIN		7

#define	LED_PIN		5

	CBLOCK	0x000
	STATUS_TEMP	:1	;save STATUS register
	WREG_TEMP	:1	;save working register
	BSR_TEMP	:1	;save BSR register
	int_count	:2	;count interrupts
	ENDC

;******************************************************************************
; Note: each line of DE data must have an even number of bytes to avoid 0 padding.
;EEPROM data
	ORG	0xF00000
;	DE	"Data for EEPROM"
;	DE	0,1,2,3,4,5

;******************************************************************************
;Reset vector
; This code will start executing when a reset occurs.
	ORG	0x0000  	; start address 0
	goto	UserProg	; Start of user program - changed by bootloader
	goto	UserProg	; Start of user program

;******************************************************************************
;High priority interrupt vector
; This code will start executing when a high priority interrupt occurs or
; when any interrupt occurs if interrupt priorities are not enabled.

	ORG	0x0008
	bra	HighInt		;go to high priority interrupt routine

;******************************************************************************
;Low priority interrupt vector and routine
; This code will start executing when a low priority interrupt occurs.
; This code can be removed if low priority interrupts are not used.

	ORG	0x0018
	movff	STATUS,STATUS_TEMP	;save STATUS register
	movff	WREG,WREG_TEMP		;save working register
	movff	BSR,BSR_TEMP		;save BSR register

;	*** low priority interrupt code goes here ***
	btfss	PIR1,TMR2IF		;has the Timer2 interrupt fired?
	bra	interupt_error		;nope, unknown interrupt
	bcf	PIR1,TMR2IF		;clear the interrupt
	infsnz	int_count,f		;increment the counter low digit
	incf	int_count+1,f		;increment the counter high digit on overflow
	
	btfss	int_count+1,2		;LED shows
	bcf	LATC,LED_PIN		;  bit 10 
	btfsc	int_count+1,2		;  of the 
	bsf	LATC,LED_PIN		;  16 bit interrupt counter
;	*** end of low priority interrupt code ***

lo_int_done:
	movff	BSR_TEMP,BSR		;restore BSR register
	movff	WREG_TEMP,WREG		;restore working register
	movff	STATUS_TEMP,STATUS	;restore STATUS register
	retfie

;******************************************************************************
;High priority interrupt routine
HighInt:
	movlb	0
;	*** high priority interrupt code goes here ***
	bra	interupt_error
	retfie	FAST

;unknown interupt
interupt_error:
	bra	interupt_error

;******************************************************************************
;User ptogram starts here
UserProg:

;Initialize hardware
FlashLoaderBoot:
	clrf	INTCON		;make sure interupts are off
	clrf	BSR		;make sure bank 0 is selected

	movlw	b'01111100'	;sleep, 16MHz, R, R, Primary Clock
	movwf	OSCCON		;set oscillator

;set up LED on PORTC I/O pin
	bcf	TRISC,LED_PIN	; LED pin as output

;set up Timer 0
	movlw	b'10010110'	; On, 16 bit, Internal, 1:128 prescale
	movwf	T0CON

;set up Timer 2
	movlw	250
	movwf	PR2
	bcf	IPR1,TMR2IP	; Set Timer2 to LOW interrupt priority
	movlw	b'00011101'
		; 0....... Unimplemented: Read as 0
		; .1111... T2OUTPS 0011 = 1:16 Postscale
		; .....0.. TMR2ON  1 = Timer2 is on 
		; ......11 TMR1CS  1x = Prescaler is 16 
	movwf	T2CON
	bcf	PIR1,TMR2IF	; Clear any pending interrupt
	bsf	PIE1,TMR2IE	; Enable Timer2 Interrupt

;set up serial port (USART)
	clrf	LATB
	movlw	b'11101111'	; Set TX pin back to input so UART can control it
	movwf	TRISB		; RB4 as output for !CTS
	movlw	b'00000000'
		; 0.......	 Auto-Baud Acquisition Rollover Status bit
		; .0......	 (read only) 1 = Receive operation is Idle
		; ..0..... RXDTP 0 = Receive data (RX) is not inverted (active-high)
		; ...0.... TXCKP 0 = Idle state for transmit (TX) is a high level
		; ....0... BRG16 1 = 16-bit Baud Rate Generator  SPBRGH and SPBRG
		; .....0..	 Unimplemented: Read as 0
		; ......0.	 Wake-up Enable bit
		; .......0	 0 = Baud rate measurement disabled or completed
	movwf	BAUDCON

	clrf	SPBRGH 		;EUSART Baud Rate Generator Register High Byte
;	movlw	51		;19k2 @ 16MHz (16MHz x4 PLL / 4 clocks/instruction)
	movlw	25		;38k4 @ 16MHz (16MHz x4 PLL / 4 clocks/instruction)
	movwf	SPBRG		;EUSART Baud Rate Generator Register Low Byte

	movlw	b'00100000'
		; 0....... CSRC  don't care
		; .0...... TX9   0 = Selects 8-bit transmission
		; ..1..... TXEN  1 = Transmit enabled
		; ...0.... SYNC  0 = Asynchronous mode
		; ....0... SENDB Send Break Character bit
		; .....0.. BRGH  1 = High speed BRGH: High Baud Rate Select bit 
		; ......0. TRMT  TRMT: Transmit Shift Register Status bit (1=TSR empty)
		; .......0 TX9D  9th Bit of Transmit Data
	movwf	TXSTA

	movlw	b'00010000'
		; 0....... SPEN  0 = Serial port NOT YET enabled 
		; .0...... RX9   0 = Selects 8-bit transmission
		; ..0..... SREN  don't care
		; ...1.... CREN  1 = Enables receiver
		; ....0... ADDEN don't care
		; .....0.. FERR  1 = Framing error
		; ......0. OERR  1 = Overrun error
		; .......0 RX9   read only, don't care
	movwf	RCSTA

;set up interrupts
	bsf	RCON,IPEN	; enable interrupt priorities
	bsf	INTCON,GIEH	; have to enable high priority interrupts to get low ones
	bsf	INTCON,GIEL	; enable low priority interrupts

;get serial port going
	CTS_STOP		; no serial characters thanks
	bcf	ANSELH,ANS11	; make RX pin digital
	bsf	RCSTA,SPEN	; enable serial port
	rcall	pause100ms	; wait for serial port to settle. (Why?)

	rcall	UARTPutString	;
	db	"\r\n\n\aSample User Program v" version	;'\a' is bell character
	db	"\r\nİ2012 by vegipete@strongedge.net\r\n\n",0

InputLoop:
	rcall	UARTReadChar	; Wait for user to type something
	rcall	UARTprint	; echo it
	xorlw	'\r'		; is it a CR?
	bnz	InputLoop	; no, go look for another to echo
	movlw	'\n'		; add a new line character
	rcall	UARTprint
	bra	InputLoop

;******************************************************************************
; Pause for 100 millisecs
; Timer0 will overflow in the desired time, setting the interrupt flag, if
; the correct value is preloaded into the Timer.
; This routine will run a bit long because of call-return and setup overhead.
pause100ms:
	movlw	high (-1600000/128)	;remember, prescaler was set to 1:128
	movwf	TMR0H
	movlw	low (-1600000/128)
	movwf	TMR0L
	bcf	INTCON,TMR0IF
pausewait:
	btfss	INTCON,TMR0IF	;wait for overflow
	bra	pausewait
;	bcf	INTCON,TMR0IF	;not much point in clearing this because it'll
				;get set again soon enough 
	return

;******************************************************************************
;Wait until a character is received, return it in w-reg
UARTReadChar:
	CTS_GO			; request serial characters
	btfss	PIR1,RCIF	; wait for character
	bra	UARTReadChar	; keep looping until character appears
	CTS_STOP		; no more chacters please!

	btfss	RCSTA,OERR	; test for overrun error
	bra	read_framing	; jump if no overrun
	bcf	RCSTA,CREN	; clear continous receive bit
	bsf	RCSTA,CREN	; and set it again
read_framing:
	btfss	RCSTA,FERR	; check from framing errors
	bra	GotChar		; jump if no framing error
	movf	RCREG,w		; read byte and discard
	bra	UARTReadChar	; keep waiting

GotChar:
	movf	RCREG,w		; grab the new character
	return

;******************************************************************************
;wait until the serial transmit reg is empty, then send w-reg
UARTprint:
	btfss	PIR1,TXIF	;test if TXREG is empty
	bra	UARTprint	;loop until serial port ready
	nop
	movwf	TXREG
	return

;******************************************************************************
;  UARTPutString - send an in-line string out the serial port via Stack and TBLPTR
;
;  string must be terminated with a 00 byte and does not need
;  to be word aligned
;
;  Usage:
;	rcall	UARTPutString	;
;	db	str,0		; put string into flash, null terminated
;
UARTPutString:
	movff   TOSH,TBLPTRH	; copy return address to TBLPTR
	movff   TOSL,TBLPTRL	;
	clrf    TBLPTRU		; assume PIC with < 64KB FLASH
UARTPutNext:
	tblrd   *+		; get in-line string character
	movf    TABLAT,W	; last character (00)?
	bz      UARTPutExit	; yes, exit, else
	rcall   UARTprint	; print character
	bra     UARTPutNext	; and do another
UARTPutExit:
	btfsc   TBLPTRL,0	; odd address?
	tblrd   *+		; yes, make it even (fix PC)
	movf    TBLPTRH,W	; setup new return address
	movwf   TOSH		; 
	movf    TBLPTRL,W	;
	movwf   TOSL		;
	return			;

;******************************************************************************
;End of program
	END
