From 1da1eb0ce7960799c3907f13e56b26f6426a9fcc Mon Sep 17 00:00:00 2001
From: Werner Johansson <wj@xnk.nu>
Date: Sat, 16 Oct 2010 00:00:00 -0700
Subject: [PATCH] v0.2 - Boot Agent for UART flash is back?

Signed-off-by: Werner Johansson <wj@xnk.nu>
---
 wjuni2.asm |  359 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 352 insertions(+), 7 deletions(-)

diff --git a/wjuni2.asm b/wjuni2.asm
index 39e85d6..16f4b38 100644
--- a/wjuni2.asm
+++ b/wjuni2.asm
@@ -15,6 +15,7 @@
 ;
 ;  0.0  Very first "Fucking No Work!" version
 ;  0.1  Some receiving works with the new MCU using HW SPI, yay!
+;  0.2  Boot Agent for UART flash is back?
 ;
 ;----------------------------------------------------------------
 
@@ -71,11 +72,11 @@ Icount	equ   2Dh   ; Offset of string to print
 TxTemp	equ   2Eh   ; blahblah
 TxTemp2	equ   2Fh   ; Blahblah2
 
+; Start of code - four instructions fit before the IRQ handler
 		org	0
-		nop						; Leave the first location, just in case
-;		call	Bootstrap		; Call Flash Load routine
-;		call	LCDInit			; Initialize LCD I/F
-;		call	IRQInit			; Set up and start the IRQ handler
+		nop						; Leave the first location as NOP, just in case (required for ICD use)
+		call	Bootstrap		; Call Flash Load routine at top of flash
+		call	IRQInit			; Set up and start the IRQ handler
 		goto	Main			; Run the main program loop (skip the IRQ handler)
 
 		subtitl	"IRQ Handler"
@@ -86,7 +87,12 @@ TxTemp2	equ   2Fh   ; Blahblah2
 		org	4					; Must be on Address 4!
 		retfie			; Interrupt return
 
+		subtitl	"IRQ Initialization code"
+;----------------------------------------------------------------
+; Initializes the interrupt handler and registers.
 
+IRQInit
+		return
 
 		subtitl	"Main loop"
 		page
@@ -212,10 +218,349 @@ UpdateLEDS
 
 		return
 
+	subtitl	"Bootstrap/Bootloader code"
+	page
+
 ;----------------------------------------------------------------------
-; EE Data (64 bytes), located at 2100h
+; Bootstrap code - Allows PIC to flash itself with data from the async port.
+; Accepts a standard INHX8 encoded file as input, the only caveat is that the code is slow when writing to memory
+; (we have to wait for the flash to complete), and therefore care has to be taken not to overflow the RS232 receiver
+; (one good way of solving that is to wait for the echo from the PIC before sending anything else)
+; Both program memory and Data EEPROM memory can be programmed, but due to hardware contraints the configuration
+; register can't be programmed. That means that any references to the config register in the hex file will be ignored.
+;
+; Startup @9600bps
+
+; RAM usage for the bootstrap code
+
+BootBits	equ	7eh		; bit0 1=write 0=read, bit1 1=PGM 0=EE, bit2 0=normal 1=no-op when prog
+BootAddrL	equ	7dh
+BootAddrH	equ	7ch
+BootDataL	equ	7bh
+BootDataH	equ	7ah
+BootTimerL	equ	79h
+BootTimerM	equ	78h
+BootTimerH	equ	77h
+BootNumBytes	equ	76h
+BootDataVL	equ	75h
+BootDataVH	equ	74h
+BootHEXTemp	equ	73h
+
+	org	72eh			; Place the boot code at the top of memory page 0
+
+Bootstrap
+	bsf	STATUS,RP0		; Access bank 1
+	bsf	TXSTA,TXEN		; Enable UART TX
+	movlw	31			; Divisor for 9k6 @ 20MHz Fosc
+	movwf	SPBRG			; Store
+	bcf	STATUS,RP0		; Back to bank 0
+
+	bsf	RCSTA,SPEN		; Enable serial port
+	bsf	RCSTA,CREN		; Enable UART RX
+
+	movlw	low BootStartText	; Send boot banner to the serial port
+	call	BootTXStr
+
+	movlw	0e8h			; Initialize timeout timer (e8 is about 3 secs)
+;	movlw	0fdh			; Initialize timeout timer (fd is short enough to get the headunit to recognize us)
+	movwf	BootTimerL
+	movwf	BootTimerM
+	movwf	BootTimerH
+
+BootTimeout
+	incf	BootTimerL,f		; A 24-bit counter
+	skpnz
+	incf	BootTimerM,f
+	skpnz
+	incf	BootTimerH,f
+	skpnz				; When overflowing here..
+	goto	BootReturn		; ..Exit boot loader, no keypress within timeout period, resume program
+	btfss	PIR1,RCIF		; Wait for RX to complete
+	goto	BootTimeout
+	call	BootRXB
+	xorlw	27			; ESC
+	skpz
+	goto	BootTimeout		; If it wasn't ESC, wait for another key
+
+BootFlash
+	movlw	low BootFlashText	; OK, flashing it is, send "start" text to serial port
+	call	BootTXStr
+
+	bsf	BootBits,1
+	clrf	BootAddrL
+	clrf	BootAddrH
+
+BootLoop
+	call	BootRXB			; First find the ':'
+	xorlw	':'
+	skpz
+	goto	BootLoop		; Loop until we find it!
+
+	call	BootRXHEX		; Get one ASCII encoded byte (two chars)
+	movwf	BootNumBytes		; This is the number of bytes to be programmed on the line
+; Maybe clear cary here?
+	rrf	BootNumBytes,f		; Right shift because we're double addressing this 8-bit format
+
+; Note carry should be clear here as there cannot be odd number of bytes in this format
+
+	call	BootRXHEX		; Receive AddrH
+	movwf	BootAddrH
+	call	BootRXHEX		; Receive AddrL
+	movwf	BootAddrL
+	rrf	BootAddrH,f		; Fix the addressing again
+	rrf	BootAddrL,f
+
+	bcf	BootBits,2		; Assume we should program
+	bsf	BootBits,1		; And assume we should program flash not ee
+
+	movf	BootAddrH,w
+	xorlw	020h			; Check if it's configuration, which we can't program
+	skpnz				; Skip the bit set if it was false alarm
+	bsf	BootBits,2		; No programming for this line
+
+	xorlw	001h			; Also check if it's EEPROM memory (first xor 20h then 1 =21h)
+	skpnz				; Skip the bit set instr if not EE data address
+	bcf	BootBits,1		; We should program EE, will ignore the AddrH
+
+	call	BootRXHEX		; Receive Record Type (must be 0 for real records)
+	skpz				; Check if zero
+	goto	BootFlashComplete
+
+BootLineLoop
+	call	BootRXHEX		; Receive low-byte of data word
+	movwf	BootDataVL
+	call	BootRXHEX		; Receive high-byte of data word
+	movwf	BootDataVH
+	
+	btfsc	BootBits,2		; Check whether this line should be programmed at all
+	goto	BootWriteSkip
+
+	bcf	BootBits,0		; Read mode first, verify if we actually have to write
+	call	BootEE
+	movf	BootDataVL,w
+	xorwf	BootDataL,f		; Compare and destroy DataL
+	movwf	BootDataL		; Write new data to DataL
+	skpz				; Skip if no difference, have to check high byte as well
+	goto	BootWrite		; Jump directly to write
+
+	movf	BootDataVH,w
+	xorwf	BootDataH,f		; Compare
+	skpnz				; Skip if no difference, no programming necessary
+	goto	BootWriteSkip
+
+BootWrite
+	movf	BootDataVH,w
+	movwf	BootDataH		; Have to put the new H byte data in as well
+
+	bsf	BootBits,0
+	call	BootEE			; Write directly into program mem
+
+; Here a verify can take place, the read-back results are now in DataL/H
+
+BootWriteSkip
+
+	incf	BootAddrL,f		; Advance counter to next addr
+	skpnz
+	incf	BootAddrH,f		; And add to high byte if needed
+
+	decfsz	BootNumBytes,f
+	goto	BootLineLoop
+
+	goto	BootLoop
+
+BootFlashComplete
+	
+BootReturn
+	movlw	low BootRunText
+	call	BootTXStr
+
+	bsf	STATUS,RP0		; Reg bank 1
+BootReturnWait
+	btfss	TXSTA,TRMT		; Wait for last things to flush
+	goto	BootReturnWait
+	bcf	TXSTA,TXEN		; Disable UART TX
+	bcf	STATUS,RP0		; Back to bank 0
+
+	bcf	RCSTA,SPEN		; Disable serial port
+	bcf	RCSTA,CREN		; Disable UART RX
+
+	return				; Return to code	
+
+;----------------------------------------------------------------------
+; BootTXB - Sends one byte to the UART, waits for transmitter to become
+;  free before sending
+
+BootTXB
+BootTXW1
+	btfss	PIR1,TXIF		; Wait for TX to empty
+	goto	BootTXW1
+	movwf	TXREG			; Send the byte
+	return
 
-        org 2100h
-;        data 0ffh, 0ffh, 0ffh, 0ffh, 001h, 023h, 045h, 067h
+;----------------------------------------------------------------------
+; BootTXStr - Sends ASCII string pointed to by W, zero terminated
+
+BootTXStr
+	movwf	BootAddrL		; Store LSB of text pointer
+	movlw	07h			; MSB of pointer to the text (0700h in this boot loader)
+	movwf	BootAddrH
+	movlw	02h			; Select "Read Program Memory" operation
+	movwf	BootBits	
+BootTXStrLoop
+	call	BootEE			; Lookup char (actually two packed into one word)
+	rlf	BootDataL,w		; Shift the MSB out into carry (that's the 2nd char LSB)
+	rlf	BootDataH,w		; Shift it into 2nd char
+	call	BootTXB			; Send the high byte first
+	movf	BootDataL,w		; Get the low byte
+	andlw	07fh			; Mask of the highest bit
+	skpnz				; Stop if zero
+	return
+	call	BootTXB			; Send char
+	incf	BootAddrL,f		; Increment pointer
+	goto	BootTXStrLoop
+
+;----------------------------------------------------------------------
+; BootRXB - Receives one byte from the UART, waits if nothing available
+
+BootRXB
+BootRXW1
+	btfss	PIR1,RCIF		; Wait for RX to complete
+	goto	BootRXW1
+	movf	RCREG,w			; Get the recvd byte
+	call	BootTXB			; Echo to terminal
+	return
+
+;----------------------------------------------------------------------
+; BootRXHEXNibble - Receives one byte and converts it from ASCII HEX to binary
+
+BootRXHEXNibble
+	call	BootRXB			; Receive nibble
+
+; This code is from piclist.com, really neat!
+
+	addlw   -'A'			; Convert from BCD to binary nibble
+	skpc				; Test if if was 0-9 or A-F, skip if A-F
+	addlw  'A' - 10 - '0'		; It was numeric '0'
+	addlw   10			; Add 10 (A get to be 0ah etc.)
+
+	return
+
+;----------------------------------------------------------------------
+; BootRXHEX - Receives two bytes from the UART, waits if nothing available
+;  Decodes the bytes as ASCII hex and returns a single byte in W
+
+BootRXHEX
+	call	BootRXHEXNibble
+	movwf	BootHEXTemp
+	swapf	BootHEXTemp,f		; Swap it up to the high nibble
+
+	call	BootRXHEXNibble
+	addwf	BootHEXTemp,w		; And add the two nibbles together
+	return
+
+;----------------------------------------------------------------------
+; BootEE - Reads or writes EE or Flash memory, BootBits specify the
+;  exact action to take. BootAddrL and BootAddrH has to be initialized
+;  to the address of choice (0000-00ffh for EE and 0000h-1fffh for flash.
+;  The data to be written has to be put in BootDataL and BootDataH, and
+;  data will be written to the same place when read back
+
+BootEE
+	bsf	STATUS,RP1		; Select bank 2 (RP0 must be 0)
+
+	movf	BootAddrH,w		; Load desired address
+	movwf	EEADRH
+	movf	BootAddrL,w
+	movwf	EEADR
+	movf	BootDataH,w		; And load the data (only used when writing)
+	movwf	EEDATH
+	movf	BootDataL,w
+	movwf	EEDATA
+
+	bsf	STATUS,RP0		; Go to bank 3
+
+	bsf	EECON1,EEPGD		; Point to Program Flash mem
+	btfss	BootBits,1		; Test if that was correct or if we have to clear again
+	bcf	EECON1,EEPGD		; Point to EE DATA mem
+
+	btfss	BootBits,0		; Check from read or write
+	goto	BootEERD		; Skip the WR if we were going for a read
+
+	bsf	EECON1,WREN		; Enable writes
+	movlw	55h
+	movwf	EECON2
+	movlw	0AAh
+	movwf	EECON2			; Unlock write operation
+	bsf	EECON1,WR		; And start a write cycle
+BootWRLoop
+	btfsc	EECON1,WR		; This executes for EE only not flash, waits for WR to finish
+	goto	BootWRLoop		; These two instructions gets NOPed when flashing program memory
+
+	bcf	EECON1,WREN		; Finally disable writes again
+					; Here we read the data back again, can be used as verify
+BootEERD
+	bsf	EECON1,RD		; Start a read cycle
+	nop				; Only necessary for flash read, same thing as when writing above
+	nop				; Except I could use the two words for something useful there.. :)
+
+BootEEX
+	bcf	STATUS,RP0		; Back to bank 2
+	movf	EEDATA,w		; Store our EE-data
+	movwf	BootDataL
+	movf	EEDATH,w
+	movwf	BootDataH
+	bcf	STATUS,RP1		; And finally back to bank 0
+
+	return
+
+; To produce compact code the end zero byte has to be in the LSB (that means an even number of chars in every string)
+BootStartText
+	DA	"WJ PIC16F876A Boot Loader - press ESC to flash...\x00"
+
+BootFlashText
+	DA	"\r\nSend INHX8 file now...\r\x00"
+
+BootRunText
+	DA	"\r\nExiting loader\r\x00"
+
+;----------------------------------------------------------------------
+; EE Data (256 bytes), located at 2100h
+
+	org 2100h
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
+;	de	0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh, 0ffh
 
 	END
-- 
1.7.3