v0.2 - Boot Agent for UART flash is back? master
authorWerner Johansson <wj@xnk.nu>
Sat, 16 Oct 2010 07:00:00 +0000 (00:00 -0700)
committerWerner Johansson <wj@xnk.nu>
Sat, 16 Oct 2010 07:00:00 +0000 (00:00 -0700)
Signed-off-by: Werner Johansson <wj@xnk.nu>

wjuni2.asm

index 39e85d6..16f4b38 100644 (file)
@@ -15,6 +15,7 @@
 ;\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
@@ -71,11 +72,11 @@ Icount      equ   2Dh   ; Offset of string to print
 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
@@ -86,7 +87,12 @@ TxTemp2      equ   2Fh   ; Blahblah2
                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
@@ -212,10 +218,349 @@ UpdateLEDS
 \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