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