TITLE "PIC 16F87x Bootloader Rev 1.1" LIST P=16F877 INCLUDE"P16F877.INC" ; Clock 20.00MHz ; Author Rick Farmer ; Copyright Rick Farmer 1999 ;************************************************************************* ; ; This program is a resident serial bootloader for the Microchip PIC16F87x ; series of microcontrollers. It resides in the last 2K of code space and ; hooks the reset vector. The user code reset vector moves to 0003h. The ; program does not use any interrupts, minimizing the impact on user code. ; ;************************************************************************* ; Compiler flags for program options ON EQU 1 ;TRUE OFF EQU 0 ;FALSE FULLMEM SET ON ;full, four pages of memory, 8K parts ;FULLMEM SET OFF ;half, two pages of memory, 4K parts ;PASSPROT SET ON ;password protection is on PASSPROT SET OFF ;password protection is off ;HDUPLEX SET ON ;force half duplex comm HDUPLEX SET OFF ;allow full duplex comm ;MDROP SET ON ;enable line driver control for multidrop MDROP SET OFF ;disable line driver control for single drop ;************************************************************************* ; CPU Configuration Fuses __CONFIG _BODEN_ON&_CP_OFF&_WRT_ENABLE_ON&_PWRTE_ON&_WDT_OFF&_HS_OSC&_DEBUG_OFF&_CPD_OFF&_LVP_OFF ;************************************************************************* ; Variable Declarations FLAGS equ 20h ;various binary status flags reg DR equ 00h ;1 = data from UART was returned in W VALID equ 01h ;1 = there is valid user code present LOAD equ 02h ;1 = user wants to upload code NOK equ 03h ;1 = upload not succesful FL equ 04h ;1 = flash write error DIG equ 05h ;1 = number has a leading digit SECUR equ 06h ;1 = valid password entered STAR equ 07h ;1 = echo * in GETLINE FLAG2 equ 21h ;second binary status flag reg ECHO equ 00h ;1 = no echo in GETLINE RSTHOK equ 01h ;1 = reset vector got hooked, 0 = called by user code HOSTOFF equ 02h ;1 = an XOFF has been sent NOWT equ 03h ;1 = do not sit forever for a byte PRGFL equ 04h ;1 = programming an image BAIL equ 05h ;1 = exit program INDEX equ 22h ;counter for indexing into strings FDATAH equ 23h ;flash data high byte FDATAL equ 24h ;flash data low byte FADDRH equ 25h ;flash address high byte FADDRL equ 26h ;flash address low byte EDATA equ 27h ;EE data byte EADDR equ 28h ;EE addess byte LASTTX equ 29h ;last byte sent out UART RXLOOP equ 2Ah ;RX recieve loop timer SECOND equ 2Bh ;seconds counter CMD equ 2Ch ;user command byte DTEMPH equ 2Dh ;temp var for high data byte DTEMPL equ 2Eh ;temp var for low data byte TEMP equ 2Fh ;temp var JUNK equ 30h ;2nd temp var BYTECTR equ 31h ;line data counter CHKSUM equ 32h ;hex file line check sum LINCTRO equ 33h ;hex file line counter 1's LINCTRT equ 34h ;hex file line counter 10's LINCTRH equ 35h ;hex file line counter 100's EECTR equ 36h ;count of EE bytes sent LINTYPE equ 37h ;hex file line type WRTCTR equ 38h ;2nd copy of byte counter HOLD equ 39h ;temp var SAVAH equ 3Ah ;address save location SAVAL equ 3Bh ;address save location COUNT equ 3Ch ;counts TMR0 roll overs (13.5/sec) TX/RX SECONDS equ 3Dh ;seconds to wait for time out BTCNT equ 3Eh ;count TMR0 roll overs in BOOTIMR EEPTR equ 3Fh ;pointer to EE address in hex file TBLPTR equ 40h ;bitwise table selector FSRSTOR equ 41h ;place to save FSR DATAFSR equ 42h ;start of data on a hex file line QTEMP equ 43h ;temp var for Queing routines UNITID equ 44h ;out unit address in multidrop mode RSTTMRL equ 45h ;bootloader timer RSTTMRH equ 46h ;bootloader timer UDATAH equ 47h ;user reset opcode flash data high byte UDATAL equ 48h ;user reset opcode flash data low byte LBUFRH equ 50h ;line buffer head LBUFRT equ 7Fh ;line buffer tail (48 byte line buffer) ;************************************************************************* ; Pin Declarations ;PORTB RTS equ 01h ;RTS output, 0 = host can send CTS equ 02h ;CTS input, 0 = we can send ;PORTC DRV equ 05h ;DRV pin TX equ 06h ;TX pin RX equ 07h ;RX pin ;************************************************************************* ; Constant Declarations NULL equ 00h ;ASCII null terminator char BSPACE equ 08h ;ASCII control H CRETURN equ 0Dh ;ASCII carriage return LFEED equ 0Ah ;ASCII line feed XON equ 11h ;ASCII flow control on XOFF equ 13h ;ASCII flow control off ESCAPE equ 1Bh ;ASCII ascape character LDELIM equ '*' ;ASCII line delimiter in multidrop mode BOTWAIT equ D'15' ;time to wait for a char on start up TICKS equ D'26' ;ticks per second ;************************************************************************* ; EEPROM Declarations ; ; This is only used in a multidrop application. All lines must ; start with "*AA" (where AA is the ASCII address of the unit) in ; multidrop mode. EADDRES equ 00h ;unit address (user code may change this) ;************************************************************************* ; Reset Vector org 0000h ;hardware reset vector if FULLMEM == ON ;set jump target to last page of memory movlw 1Fh ;bank 3 for jumps, calls, and goto's (8K) movwf PCLATH ;setup for jump goto 1800h ;jump to bootloader else movlw 0Fh ;bank 1 for jumps, calls, and goto's (4K) movwf PCLATH ;setup for jump goto 0800h ;jump to bootloader endif ;************************************************************************* ; USER code reset vector. ; ; The bootloader reserves all code space from x800h to xFFFh (last page). ; It will not allow itself to be overwitten. Attempting to do so will ; result in an error message. The instruction at this address must be ; "goto XX" for the bootloader to be recognized as the start of a valid ; user image. org 0003h ;user code reset vector ;************************************************************************* ;BOOTLDR This is the Bootloader INIT vector. ; ; This code sets up the serial port pins and the UART to 9600 ; baud assuming a 4.00MHz clock, or whatever baud rate is preset ; if the alternate entry point is used. The serial port is left ; active with RTS high (host send inhibit) when control is given ; to the user code. Set the terminal to ANSI mode 9600-8-N-1, ; hardware flow control on, local echo off. if FULLMEM == ON ;set origin to last page of memory org 1800h ;bootloader code origin else org 0800h ;bootloader code origin endif BOOTLDR bcf STATUS,RP1 ;page 0 or 1 (org x800h) bsf STATUS,RP0 ;switch to memory page 1 ; movlw D'185' ;baud rate divisor for 300@3.58MHz, BRGH=0 ; movlw D'185' ;baud rate divisor for 1200@3.58MHz, BRGH=1 ; movlw D'92' ;baud rate divisor for 2400@3.58MHz ; movlw D'46' ;baud rate divisor for 4800@3.58MHz ; movlw D'22' ;baud rate divisor for 9600@3.58MHz ; movlw D'11' ;baud rate divisor for 19200@3.58MHz ; movlw D'207' ;baud rate divisor for 300@4MHz, BRGH=0 ; movlw D'207' ;baud rate divisor for 1200@4MHz, BRGH=1 ; movlw D'103' ;baud rate divisor for 2400@4MHz ; movlw D'51' ;baud rate divisor for 4800@4MHz ; movlw D'25' ;baud rate divisor for 9600@4MHz ; movlw D'12' ;baud rate divisor for 19200@4MHz movlw D'31' ;baud rate divisor for 9600@5MHz (38,400@20MHz) movwf SPBRG ;load baud rate reg goto RSTCALL ;goto reset hook init ; USER code can "GOTO USRLDR" to use a preset baud rate USRLDR bcf STATUS,RP1 ;page 0 or 1 (org x805h) bcf STATUS,RP0 ;switch to memory page 0 clrf FLAG2 ;init the second flag reg (and RSTHOK bit) bsf STATUS,RP0 ;switch to memory page 1 bcf INTCON,GIE ;turn off INTs btfsc INTCON,GIE ;are INTs off? goto USRLDR ;no goto USRCALL ;yes RSTCALL bcf STATUS,RP0 ;switch to memory page 0 clrf FLAG2 ;init 2nd flag reg bsf FLAG2,RSTHOK ;set the reset vector got hooked flag bsf STATUS,RP0 ;switch to memory page 1 ; bcf TXSTA,BRGH ;clear high baud rate bit (for 300 baud) bsf TXSTA,BRGH ;set high baud rate bit USRCALL movf TXSTA,W ;get current TXSTA reg andlw B'00000100' ;strip everything but BRGH bit iorlw B'00100010' ;add: 8bit, TX on, asynch, BRGH as is movwf TXSTA ;load TX control reg if FULLMEM == ON ;set target page for calculated PCL jumps movlw 1Fh ;bank 3 for jumps, calls, and goto's movwf PCLATH ;setup for jump else movlw 0Fh ;bank 1 for jumps, calls, and goto's movwf PCLATH ;setup for jump endif bsf PORTC,TX ;(TRISC,TX) TX can now be used for UART bsf PORTC,RX ;(TRISC,RX) RX can now be used for UART if MDROP == ON ;load unit ID for multidrop & DIR control pin bcf PORTC,DRV ;(TRISC,DRV) DRV can be used for bus control endif movlw B'11010111' ;TMR0 prescaler 256, internal clock movwf OPTION_REG ;load option reg, 13.65 ticks/second bcf STATUS,RP0 ;switch back to page 0 movlw B'10010000' ;enable receiver in 8 bit asynch mode movwf RCSTA ;load RX control reg clrf RSTTMRH ;zero activity reset timer clrf RSTTMRL ;zero activity reset timer clrf FLAGS ;init the flags reg, drop into main line code if MDROP == ON bcf PORTC,DRV ;DRV low to disable line driver movlw EADDRES ;load EEPROM address movwf EADDR ;into page 1 index reg call EREAD ;read data from EEPROM movf EDATA,W ;load return value movwf UNITID ;save our bus address endif BLSTART clrf TBLPTR ;clear all bits of the table pointer bsf TBLPTR,0 ;set index to copyright string call COPYRT ;yes, send the startup message call USRSCAN ;scan for bootable user image call CODESTR ;send the current code revision STRTLOP btfss FLAGS,VALID ;is there a bootable user image? goto PAS_LOP ;no, skip start timer btfss FLAG2,RSTHOK ;yes, did user code init bootloader? goto PAS_LOP ;yes, skip start timer call PROMPT ;no, send a command prompt call BOOTIMR ;do countdown to user code boot btfss FLAGS,LOAD ;do we load new code? goto BLDONE ;no, boot user image PAS_LOP bsf FLAGS,STAR ;yes, set the echo a star flag call PASSWD ;check password before we allow access bcf FLAGS,STAR ;clear the echo a star flag btfsc FLAGS,SECUR ;was password correct? goto BLPAS ;yes btfsc FLAGS,VALID ;no, is there a valid user image? goto BLDONE ;yes, boot user image goto PAS_LOP ;no, loop until a valid password is entered BLPAS call HELPIST ;send the help prompt message BLMAIN call PROMPT ;send a command prompt to the screen call GETLINE ;wait for a line of data movf INDF,W ;get the first char of line call UPCASE ;force the command to upper case movwf CMD ;save the first keystroke as a command btfsc STATUS,Z ;was null string returned? goto BLMAIN ;yes, eat it NOT_JNK movlw 'W' ;no, load write EE command subwf CMD,W ;compare btfss STATUS,Z ;did user press "W/w"? goto NOT_WR ;no call EEWRITE ;yes, write EE byte goto BLMAIN ;get next command NOT_WR movlw 'R' ;load read EE command subwf CMD,W ;compare btfss STATUS,Z ;did user press "R/r"? goto NOT_EE ;no call EEREAD ;yes, read EE byte goto BLMAIN ;get next command NOT_EE incf FSR,F ;inc pointer to null char that should end line. movf INDF,W ;test for zero btfss STATUS,Z ;null? goto NOT_V ;no, eat line because it had garbage on it movlw '?' ;yes, load help request command subwf CMD,W ;compare btfss STATUS,Z ;did user press "?"? goto NOT_HLP ;no call CMDLIST ;yes, display the list of valid commands goto BLMAIN ;get next command NOT_HLP movlw 'U' ;load upload command subwf CMD,W ;compare btfss STATUS,Z ;did user press "U/u"? goto NOT_COL ;no call UPLOAD ;yes, begin hex file upload btfss FLAGS,LOAD ;did the user REALLY want to upload code? goto BLMAIN ;no, get next command btfsc FLAGS,NOK ;yes, was upload successful? goto BLBAD ;no BLGOOD call EXITMSG ;yes, display upload succesful message call GET_REV ;get user code rev string call CODESTR ;echo new code revision string goto BLMAIN ;get next command BLBAD call LOADBUF ;eat the rest of the hex file call CRLF ;send a CR/LF call LINNUM ;send offending line number to screen goto BLMAIN ;get next command NOT_COL movlw 'D' ;load download command subwf CMD,W ;compare btfss STATUS,Z ;did user press "D/d"? goto NOT_DL ;no call DNLOAD ;yes, dump current user code image goto BLMAIN ;get next command NOT_DL movlw 'Q' ;load quit command subwf CMD,W ;compare btfss STATUS,Z ;did user press "Q/q"? goto NOT_Q ;no btfsc FLAGS,VALID ;yes, is there an image to jump to? goto BLDONE ;yes, we're done call CODESTR ;no, send the current code revision (blank) goto BLMAIN ;ignore command & get another one NOT_Q movlw 'V' ;load display code rev command subwf CMD,W ;compare btfss STATUS,Z ;did user press "R/r"? goto NOT_V ;no call CODESTR ;yes, send the current code revision goto BLMAIN ;get next command NOT_V call CMDERR ;send invalid command error goto BLMAIN ;end of command list BLDONE btfss FLAGS,VALID ;is there an image to jump to? goto BLMAIN ;no, get next command call TMRMSG ;yes, display jumping to user code message BLEXIT clrf PCLATH ;reset PCL to page 0 (reset value) goto 0003h ;give control to user program ;************************************************************************* ;PASSWD This routine password protects user code from overwrite. PASSWD bcf FLAGS,SECUR ;clear the password valid bit if PASSPROT == OFF ;fake return of a valid password goto GOOD_PS ;password checking is off endif movlw 'P' ;"Passsword>" call TXBYTE ;send character movlw 'a' ;load ASCII char call TXBYTE ;send character movlw 's' ;load ASCII char call TXBYTE ;send character call TXBYTE ;send character (double) movlw 'w' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw 'd' ;load ASCII char call TXBYTE ;send character movlw '>' ;load ASCII char call TXBYTE ;send character call GETLINE ;get a line from the user PSWDLOP movf INDEX,W ;get index into password table call PASTBL ;get password char subwf INDF,W ;compare to char that was entered btfss STATUS,Z ;same? goto BAD_PS ;no movf INDF,W ;yes, reload entered data btfsc STATUS,Z ;was the char the null string terminator? goto GOOD_PS ;yes, password is valid btfsc INDEX,3 ;no, is index = 8 (max password length)? goto BAD_PS ;yes, password is invalid incf INDEX,F ;no, inc index into table incf FSR,F ;inc index into entered data goto PSWDLOP ;go check the next char GOOD_PS bsf FLAGS,SECUR ;set the password valid bit BAD_PS return ;done ;************************************************************************* ;EEREAD This routine reads an EE data byte. EEREAD incf FSR,F ;inc pointer to space char movlw ' ' ;load a space subwf INDF,W ;compare btfss STATUS,Z ;same? goto EER_ER ;no, bail incf FSR,F ;yes, inc pointer to first hex char bcf FLAGS,NOK ;clear the error flag call HEXQ ;call routine to hexify 2 chars from Q movwf EADDR ;load address to read btfsc FLAGS,NOK ;was there an error? goto EER_ER ;yes, bail movf INDF,W ;no, test for zero btfss STATUS,Z ;null? goto EER_ER ;no, bail if FULLMEM == OFF btfss EADDR,7 ;is EADDR > 80h? goto EERNG2 ;no clrf FADDRH ;yes, clear high byte address movf EADDR,W ;get EE address movwf FADDRL ;load for error message call ADRERR ;send error message return ;done endif EERNG2 call EREAD ;read the byte call EEDISP ;display EE data location return ;done EER_ER call CMDERR ;send invalid command error return ;done ;************************************************************************* ;EEDISP This routine displays the content of EADDR. EEDISP movf EADDR,W ;get EE address call DISPHEX ;display it as a hex char movlw ' ' ;load ASCII char call TXBYTE ;send character movlw '=' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movf EDATA,W ;get EE address call DISPHEX ;display it as a hex char call CRLF ;send a CR/LF return ;done ;************************************************************************* ;DISPHEX This routine displays a byte from W in ASCII. DISPHEX movwf HOLD ;save W to holding reg andlw 0F0h ;strip low nibble movwf JUNK ;move it into temp swapf JUNK,W ;swap nibbles call ASCII ;asciify digit call TXBYTE ;send character movf HOLD,W ;get LSN of data byte andlw 0Fh ;strip high nibble call ASCII ;asciify digit call TXBYTE ;send character return ;done ;************************************************************************* ;EEWRITE This routine writes an EE data byte. EEWRITE incf FSR,F ;inc pointer to space char movlw ' ' ;load a space subwf INDF,W ;compare btfss STATUS,Z ;same? goto EEW_ER ;no, bail incf FSR,F ;yes, inc pointer to first hex char bcf FLAGS,NOK ;clear the error flag call HEXQ ;call routine to hexify 2 chars from Q btfsc FLAGS,NOK ;was there an error? goto EEW_ER ;yes, bail movwf EDATA ;no, load data to write movlw ' ' ;load a space subwf INDF,W ;compare btfss STATUS,Z ;same? goto EEW_ER ;no, bail incf FSR,F ;yes, inc pointer to "@" char movlw '@' ;load a "@" subwf INDF,W ;compare btfss STATUS,Z ;same? goto EEW_ER ;no, bail incf FSR,F ;yes, inc pointer to space char movlw ' ' ;load a space subwf INDF,W ;compare btfss STATUS,Z ;same? goto EEW_ER ;no, bail incf FSR,F ;yes, inc pointer to second hex char call HEXQ ;call routine to hexify 2 chars from Q movwf EADDR ;load address to write data if FULLMEM == OFF btfss EADDR,7 ;is EADDR > 80h? goto EERNGOK ;no clrf FADDRH ;yes, clear high byte address movf EADDR,W ;get EE address movwf FADDRL ;load for error message call ADRERR ;send error message return ;done endif EERNGOK btfsc FLAGS,NOK ;was there an error? goto EEW_ER ;yes, bail movf INDF,W ;no, test for zero btfss STATUS,Z ;null? goto EEW_ER ;no, bail call EWRITE ;yes, write the data call EREAD ;read the byte we just wrote call EEDISP ;display EE data location return ;done EEW_ER call CMDERR ;send invalid command error return ;done ;************************************************************************* ;GET_REV This routine gets and stores the user code rev string. if FULLMEM == ON ;set target page to store and read PSWD GET_REV movlw 1Fh ;load high byte of string start address else GET_REV movlw 0Fh ;load high byte of string start address endif movwf FADDRH ;store it movlw 0C5h ;load low byte of string start address movwf FADDRL ;store it movlw 34h ;load "retlw" instruction code movwf FDATAH ;store it call REV_MSG ;prompt for rev string call GETLINE ;get a line of data incf INDEX,F ;pre-inc the counter REV_LOP movf INDF,W ;get Qued data movwf FDATAL ;load data reg call FWRITE ;program it movf INDF,W ;get Qued data btfsc STATUS,Z ;was that the null terminator? return ;yes, done incf FADDRL,F ;no, inc the programming address incf FSR,F ;inc the read pointer incf INDEX,F ;inc the counter btfsc INDEX,5 ;has index hit 32? clrf INDF ;yes, force last to null termiantor goto REV_LOP ;do it again ;************************************************************************* ;RXBYTE This routine gets a byte from the UART. ; ; If data is present it is returned in "W" and the DR flag is set. RXBYTE clrwdt ;kick the dog if MDROP == ON ;enable the host bcf PORTC,DRV ;DRV low to disable line driver endif if HDUPLEX == ON ;enable the host bsf RCSTA,CREN ;restart the RX part of the UART endif bcf FLAGS,DR ;clear the data ready flag btfsc RCSTA,FERR ;framing error? goto NOISE ;yes, eat the byte and return btfss RCSTA,OERR ;no, has buffer overrun? goto NO_OVRR ;no bcf RCSTA,CREN ;yes, reset the UART bsf RCSTA,CREN ;restart the UART NOISE movf RCREG,W ;read garbage/corrupted byte return ;done NO_OVRR btfss PIR1,RCIF ;is there data present? return ;no, done bsf FLAGS,DR ;yes, set data ready flag movf RCREG,W ;deque byte movwf QTEMP ;save it sublw LFEED ;check for line feed char btfsc STATUS,Z ;was LF received? bcf FLAGS,DR ;yes, clear byte ready flag movf QTEMP,W ;get RX'ed byte into W return ;done ;************************************************************************* ;GETBYTE This routine will wait about 10 minutes for a byte to Que. GETBYTE clrf TMR0 ;clear timer 0 bcf INTCON,T0IF ;clear the roll over flag RX_LOP call RXBYTE ;look for a byte btfss FLAGS,DR ;was a char ready? goto NO_RX ;no return ;yes, done NO_RX btfss INTCON,T0IF ;has timer 0 rolled? goto RX_LOP ;no btfsc FLAG2,NOWT ;should we wait a long time? return ;no, done incf RSTTMRL,F ;yes, inc reset timer btfsc STATUS,Z ;carry out? incf RSTTMRH,F ;yes, inc reset timer btfss RSTTMRH,5 ;has about 10 minutes gone by idle? return ;no, done btfsc FLAGS,VALID ;yes, is there an image to jump to? goto BLDONE ;yes, exit bootloader return ;no, stay in bootloader ;************************************************************************* ;LOADBUF This routine Que's all incoming data until the UART is quiet. LOADBUF bsf FLAG2,NOWT ;set the no long wait flag call GETBYTE ;Que any data that's still dribbling in btfsc FLAGS,DR ;was UART idle for more than 1/13 sec? goto LOADBUF ;no, get next byte call GETBYTE ;yes, Que any data that's still dribbling in btfsc FLAGS,DR ;was UART idle for more than 1/13 sec? goto LOADBUF ;no, get next byte call GETBYTE ;yes, Que any data that's still dribbling in btfsc FLAGS,DR ;was UART idle for more than 1/13 sec? goto LOADBUF ;no, get next byte bcf FLAG2,NOWT ;yes, clear the no long wait flag return ;done ;************************************************************************* ;BOOTIMR This routine waits 15 seconds for a byte. BOOTIMR bcf FLAGS,LOAD ;clear the load code flag bsf FLAG2,NOWT ;set the no long wait flag movlw TICKS ;load ticks/second movwf BTCNT ;load counter movlw BOTWAIT ;load number of seconds to wait movwf SECOND ;load counter SPCLOOP movlw ' ' ;load ASCII char call TXBYTE ;send character decfsz SECOND,F ;spaces over? goto SPCLOOP ;no movlw '<' ;load ASCII char call TXBYTE ;send character movlw BOTWAIT+1 ;load number of seconds to wait movwf SECOND ;load counter BSPCLOP movlw BSPACE ;load ASCII char call TXBYTE ;send character decfsz SECOND,F ;backspaces over? goto BSPCLOP ;no movlw BOTWAIT ;yes, load number of seconds to wait movwf SECOND ;load counter BOOTLOP call GETBYTE ;fish for a bite btfsc FLAGS,DR ;has the user hit a key? goto BTMR_DN ;yes decfsz BTCNT,F ;no, has TICKS hit 0? goto BOOTLOP ;no movlw TICKS ;yes, reload seconds to wait movwf BTCNT ;load counter movlw '#' ;load ASCII char to tick off the seconds call TXBYTE ;send character decfsz SECOND,F ;boot wait over? goto BOOTLOP ;no, keep waiting goto BOOTOVR ;yes, done BTMR_DN bsf FLAGS,LOAD ;set flag for forground to load code BOOTOVR bcf FLAG2,NOWT ;clear the no long wait flag call CRLF ;send a CR/LF return ;done ;************************************************************************* ;GETLINE This routine reads in a line from the terminal. ; The line is stored in the Q. Lenght = INDEX, null terimated. GETLINE movlw LBUFRH ;load start of line buffer movwf FSR ;into line pointer if MDROP == ON ;enable the host bsf FLAG2,ECHO ;prevent local echo of host data endif LIN_IN clrf RSTTMRH ;zero activity reset timer clrf RSTTMRL ;zero activity reset timer LINELOP call GETBYTE ;scan for a character btfss FLAGS,DR ;was a char ready? goto LINELOP ;no movlw BSPACE ;yes, load DEL subwf QTEMP,W ;compare btfss STATUS,Z ;was that a DEL? goto LIN_NBS ;no movlw LBUFRH ;yes, load start of line buffer subwf FSR,W ;compare to line pointer btfsc STATUS,Z ;same (at begin of line)? goto LIN_IN ;yes, just eat the char decf FSR,F ;no, dec pointer movlw BSPACE ;backspace over char btfss FLAG2,ECHO ;should we echo at all? call TXBYTE ;yes, send character movlw ' ' ;load over write char btfss FLAG2,ECHO ;should we echo at all? call TXBYTE ;yes, send character movlw BSPACE ;backspace agian btfss FLAG2,ECHO ;should we echo at all? call TXBYTE ;yes, send character goto LIN_IN ;get next char LIN_NBS movlw CRETURN ;load CR subwf QTEMP,W ;compare btfsc STATUS,Z ;was that a CR? goto LIN_CHK ;yes, go check what we've read in movf QTEMP,W ;no, echo character to screen movwf INDF ;store it first btfsc FLAGS,STAR ;should we echo a start (*) instead? movlw '*' ;yes btfss FLAG2,ECHO ;should we echo at all? call TXBYTE ;yes, send character movlw LBUFRT ;load end of line buffer subwf FSR,W ;compare to line pointer btfsc STATUS,Z ;same (at end of line)? goto LIN_CHK ;yes, bail incf FSR,F ;no, inc Q pointer goto LIN_IN ;get another char LIN_CHK btfss FLAG2,ECHO ;should we echo at all? call CRLF ;yes, send a CR/LF clrf INDF ;null terminal the input string clrf INDEX ;clear the index pointer movlw LBUFRH ;load start of line buffer movwf FSR ;into line pointer if MDROP == ON ;check to see if line is addressed to us movf INDF,W ;get first char on the line sublw LDELIM ;compare to line delimiter btfss STATUS,Z ;valid multidrop line? goto GETLINE ;no incf FSR,F ;yes, inc pointer to address call HEXQ ;ASCIIify address subwf UNITID,W ;compare btfss STATUS,Z ;line address to us? goto GETLINE ;no endif return ;done ;************************************************************************* ;TXBYTE This routine sends "W" out the UART. TXBYTE clrf TMR0 ;clear timer 0 bcf INTCON,T0IF ;clear the roll over flag if MDROP == ON ;set bus to output mode bsf PORTC,DRV ;DRV high to enable line driver endif if HDUPLEX == ON ;disable reception of our own echo bcf RCSTA,CREN ;reset the RX part of the UART endif TXLOOP clrwdt ;kick the dog btfss PIR1,TXIF ;xmit buffer empty? goto TXLOOP ;no, wait movwf TXREG ;yes, send the byte return ;done ;************************************************************************* ;HEXQ This routine gets 2 ASCII chars and converts them to bin. ; ; This routine gets it's input from the Q. FSR must point to data. HEXQ movf INDF,W ;get first char from the Q call UPCASE ;force to upper case call HEXIFY ;convert the character to binary btfsc FLAGS,NOK ;was there an error? return ;yes, quit movwf JUNK ;no, store result in temp var swapf JUNK,F ;swap data to high nibble incf FSR,F ;inc Q pointer movf INDF,W ;get second char from the Q call UPCASE ;force to upper case call HEXIFY ;convert the character to binary iorwf JUNK,F ;store result in low nibble movf JUNK,W ;move result to W incf FSR,F ;post inc Q pointer return ;done ;************************************************************************* ;UPCASE This routine will convert all chars to upper case. ; ; The raw data is expected in W, result returned in W. UPCASE movwf QTEMP ;store char before modification movlw 'z'+1 ;load highest lowercase letter +1 subwf QTEMP,W ;compare btfsc STATUS,C ;is data less than or equal to "z" goto NOT_LCS ;no movlw 'a' ;yes, load lowest lowercase letter subwf QTEMP,W ;compare btfss STATUS,C ;is data greater than or equal to "a"? goto NOT_LCS ;no movlw 'a'-'A' ;yes, load difference between cases subwf QTEMP,W ;convert to upper case return ;done NOT_LCS movf QTEMP,W ;reload W return ;done ;************************************************************************* ;BLNKCHK This routine checks for a blank program memory location. BLNKCHK call FREAD ;get current memory contents movlw 3Fh ;high byte blank value subwf FDATAH,W ;compare btfss STATUS,Z ;same? return ;no, return Z bit clear = opcode present movlw 0FFh ;yes, low byte blank value subwf FDATAL,W ;compare return ;return Z bit set if blank, clear if opcode ;************************************************************************* ;DNLOAD This routine downloads a hex file from program memory. DNLOAD bcf FLAGS,NOK ;clear download error flag. call CODESTR ;send rev string or part is blank message btfss FLAGS,VALID ;is there a valid image in memory? return ;no, done DN_OK clrf FADDRH ;yes, store high byte of address to read movlw 03h ;load low byte of target movwf FADDRL ;store low byte of address to read DN_LOP clrf BYTECTR ;clear the byte counter call BLNKCHK ;check for a blank memory location btfss STATUS,Z ;is location blank? goto NOTBLNK ;no, go calculate how many bytes to send incf FADDRL,F ;yes, inc address low byte btfsc STATUS,Z ;did low byte carry out? incf FADDRH,F ;yes, inc address high byte call RANGE ;no, range check btfss FLAGS,NOK ;hit the end of memory? goto DN_LOP ;no goto EORLIN ;yes, exit NOTBLNK movf FADDRH,W ;get high address byte movwf SAVAH ;save beginning of valid data address movf FADDRL,W ;get low address byte movwf SAVAL ;save beginning of valid data address QLINE incf BYTECTR,F ;inc number of words to put in line movf FADDRL,W ;get low address movwf TEMP ;save it for DC check later incf FADDRL,F ;inc address low byte btfsc STATUS,Z ;did low byte carry out? incf FADDRH,F ;yes, inc address high byte movf TEMP,W ;get pre-incf low address xorwf FADDRL,W ;compare bitwise andlw 0F8h ;strip low 3 bits (8 word/16 byte boundary) btfss STATUS,Z ;have we crossed an 8 word boundary? goto BLSTLIN ;yes call RANGE ;no, range check btfsc FLAGS,NOK ;hit the end of memory? goto LASTLIN ;yes, send last data line call BLNKCHK ;no, check for a blank memory location btfss STATUS,Z ;is location blank? goto QLINE ;no, scan next memory location BLSTLIN call SENDLIN ;yes, BYTECTR holds number of words to send call RANGE ;range check btfsc FLAGS,NOK ;hit the end of memory? goto EORLIN ;yes, send last line goto DN_LOP ;no, go scan for another line to send LASTLIN call SENDLIN ;BYTECTR holds number of words to send EORLIN call SENDEE ;insert EEPROM data into hex file bcf FLAGS,NOK ;reset range error flag movlw ':' ;send colon to start BYTECTR counter call TXBYTE ;send character movlw 00h ;yes, send EOF record call DISPHEX ;send as a hex character movlw 00h ;yes, send EOF record call DISPHEX ;send as a hex character movlw 00h ;yes, send EOF record call DISPHEX ;send as a hex character movlw 01h ;send record type call DISPHEX ;send as a hex character movlw 0FFh ;send checksum call DISPHEX ;send as a hex character call CRLF ;send a CR/LF return ;done ;************************************************************************* ;SENDEE This routine adds all of EEPROM to the hex file. SENDEE clrf EADDR ;clear address pointer movlw 42h ;load high hex file address byte ('21x2') movwf EEPTR ;init pointer (x2) SNDEEL movlw ':' ;send colon to start line call TXBYTE ;send character movlw 10h ;load bytes to send in line movwf CHKSUM ;init the checksum reg movwf EECTR ;init loop counter bcf STATUS,C ;clear carry in bit rrf EECTR,F ;div byte count to word count call DISPHEX ;send byte count as a hex character bcf STATUS,C ;clear carry in bit rlf EADDR,W ;load low hex file address byte btfsc STATUS,C ;will this line be @ 43xxh (EADDR >= 80h)? bsf EEPTR,0 ;yes, set EEPTR to 43h movf EEPTR,W ;load high hex file address byte addwf CHKSUM,F ;keep running checksum call DISPHEX ;send as a hex character bcf STATUS,C ;clear carry in bit rlf EADDR,W ;load low hex file address byte addwf CHKSUM,F ;keep running checksum call DISPHEX ;send as a hex character clrw ;send record type (00h, no checksum effect) call DISPHEX ;send as a hex character EESLOP call EREAD ;get EEPROM data (low byte) movf EDATA,W ;load byte that was read out addwf CHKSUM,F ;keep running checksum call DISPHEX ;send as a hex character clrw ;send high byte (00h, no checksum effect) call DISPHEX ;send as a hex character incf EADDR,F ;inc address pointer decfsz EECTR,F ;done sending line? goto EESLOP ;no comf CHKSUM,F ;yes, complement checksum incf CHKSUM,W ;add 1 call DISPHEX ;send as a hex character call CRLF ;send a CR/LF if FULLMEM == ON ;big parts have 256 bytes EEPROM, small 128 movf EADDR,W ;range check btfss STATUS,Z ;at end of EEPROM (256 bytes)? goto SNDEEL ;no, get next line return ;yes, done else btfss EADDR,7 ;at end of EEPROM (128 bytes)? goto SNDEEL ;no, get next line return ;yes, done endif ;************************************************************************* ;SENDLIN This routine downloads a single hex file line from memory. ; ; It uses the shadow regs for source address and BYTECTR for # to send. ; No bounds checks are done, and BYTECTR (word count) must not be zero. SENDLIN movf SAVAH,W ;get high address of data to send movwf FADDRH ;store it movf SAVAL,W ;get low address of data to send movwf FADDRL ;store it movlw ':' ;send colon to start BYTECTR counter call TXBYTE ;send character bcf STATUS,C ;clear carry bit for carry in rlf BYTECTR,W ;turn word count into byte count (x2) movwf CHKSUM ;init the checksum reg call DISPHEX ;send as a hex character rlf FADDRL,W ;get high bit of low byte rlf FADDRH,W ;get start address high byte times 2 addwf CHKSUM,F ;keep running checksum call DISPHEX ;send as a hex character bcf STATUS,C ;clear carry in bit rlf FADDRL,W ;get start address low byte times 2 addwf CHKSUM,F ;keep running checksum call DISPHEX ;send as a hex character clrw ;send record type (00h, no checksum effect) call DISPHEX ;send as a hex character SENDLOP call FREAD ;read flash location incf FADDRL,F ;inc address low byte btfsc STATUS,Z ;did low byte carry out? incf FADDRH,F ;yes, inc address high byte movf FDATAL,W ;get low byte addwf CHKSUM,F ;keep running checksum call DISPHEX ;send as a hex character movf FDATAH,W ;get high byte addwf CHKSUM,F ;keep running checksum call DISPHEX ;send as a hex character decfsz BYTECTR,F ;done sending line? goto SENDLOP ;no comf CHKSUM,F ;yes, complement checksum incf CHKSUM,W ;add 1 call DISPHEX ;send as a hex character call CRLF ;send a CR/LF return ;done ;************************************************************************* ;WIPE This routine wipes all of user program and EEPROM memory. WIPE movlw 'E' ;send "Erasing" call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw 'a' ;load ASCII char call TXBYTE ;send character movlw 's' ;load ASCII char call TXBYTE ;send character movlw 'i' ;load ASCII char call TXBYTE ;send character movlw 'n' ;load ASCII char call TXBYTE ;send character movlw 'g' ;load ASCII char call TXBYTE ;send character call CRLF ;send a CR/LF bcf FLAGS,VALID ;clear the valid image flag clrf FADDRH ;store code start point high byte movlw 03h ;load a 3 movwf FADDRL ;store code start point low byte movlw 3Fh ;load high byte data movwf FDATAH ;store it movlw 0FFh ;load low byte data movwf FDATAL ;store it WIPFLOP call FWRITE ;nuke it btfsc FLAGS,NOK ;erase error? return ;yes, abort upload incf FADDRL,F ;no, inc address low byte btfss STATUS,Z ;did low byte carry out? goto WIPBOND ;no incf FADDRH,F ;yes, inc address high byte if MDROP == OFF movlw '#' ;load ASCII char call TXBYTE ;send character endif WIPBOND call RANGE ;check for end of memory btfss FLAGS,NOK ;hit end of memory? goto WIPFLOP ;no bcf FLAGS,NOK ;yes, clear error flag if FULLMEM == ON movlw 1Fh ;load rev string origin else movlw 0Fh ;load rev string origin endif movwf FADDRH ;store rev string start point high byte movlw 0C5h ;load rev string origin movwf FADDRL ;store rev string start point low byte movlw 34h ;load "retlw" instruction code movwf FDATAH ;store it clrf FDATAL ;store a null WIPFREV call FWRITE ;nuke it btfsc FLAGS,NOK ;erase error? return ;yes, abort upload incf FADDRL,F ;no, inc address low byte movlw 0E5h ;load end of rev string address subwf FADDRL,W ;compare to current flash pointer btfss STATUS,Z ;done clearing rev string? goto WIPFREV ;no if FULLMEM == ON movlw 0FFh ;load all 1's else movlw 07Fh ;load all 1's endif movwf EADDR ;load end of EEPROM memory movlw 0FFh ;load all 1's movwf EDATA ;load blank data value WIPELOP call EWRITE ;clear byte btfsc FLAGS,NOK ;erase error? return ;yes, abort upload decfsz EADDR,F ;no, done clearing EEPROM? goto WIPELOP ;no if MDROP == OFF call EWRITE ;almost, clear EADDRES byte if not MDROP call CRLF ;send a CR/LF endif return ;done ;************************************************************************* ;UPLOAD This routine uploads a hex file to program memory. UPLOAD bcf FLAGS,NOK ;clear upload error flag. bcf FLAGS,LOAD ;clear the upload flag clrf LINCTRO ;clear line counter 1's clrf LINCTRT ;clear line counter 10's clrf LINCTRH ;clear line counter 100's movlw 'A' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 'y' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'u' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 's' ;load ASCII char call TXBYTE ;send character movlw 'u' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw '?' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw '[' ;load ASCII char call TXBYTE ;send character movlw 'y' ;load ASCII char call TXBYTE ;send character movlw '/' ;load ASCII char call TXBYTE ;send character movlw 'N' ;load ASCII char call TXBYTE ;send character movlw ']' ;load ASCII char call TXBYTE ;send character movlw '>' ;load ASCII char call TXBYTE ;send character call GETLINE ;wait for a line of data movf INDF,W ;get the first char of line call UPCASE ;force to upper case sublw 'Y' ;compare btfss STATUS,Z ;did user press "Y/y"? return ;no, abort upload incf FSR,F ;yes, look for null movf INDF,W ;test for zero btfss STATUS,Z ;was that the null terminator? return ;no, abort upload bsf FLAGS,LOAD ;yes, set the upload flag call WIPE ;nuke all of user memory btfsc FLAGS,NOK ;erase error? return ;yes, abort upload movlw 'R' ;no, send "Ready" call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw 'a' ;load ASCII char call TXBYTE ;send character movlw 'd' ;load ASCII char call TXBYTE ;send character movlw 'y' ;load ASCII char call TXBYTE ;send character call CRLF ;send a CR/LF bsf FLAG2,PRGFL ;set the intercept user reset opcode flag LIN_LOP bsf FLAG2,ECHO ;set echo = off call GETLINE ;get a line from the hex file bcf FLAG2,ECHO ;set echo = on incf LINCTRO,F ;yes, inc line counter ones byte movlw D'10' ;load decimal carry out subwf LINCTRO,W ;compare btfss STATUS,C ;did ones byte carry out? goto CNT_OVR ;no clrf LINCTRO ;yes, clear line counter 1's incf LINCTRT,F ;inc line counter tens byte movlw D'10' ;load decimal carry out subwf LINCTRT,W ;compare btfss STATUS,C ;did tens byte carry out? goto CNT_OVR ;no clrf LINCTRT ;yes, clear line counter 10's incf LINCTRH,F ;inc line counter hundreds byte CNT_OVR movf INDF,W ;get the first char of line sublw ':' ;compare to a colon btfss STATUS,Z ;same? goto BAD_REC ;no incf FSR,F ;yes, point at first byte of data call HEXQ ;read byte count btfsc FLAGS,NOK ;was there an error? return ;yes, quit movwf BYTECTR ;no, store it movwf CHKSUM ;initialize line checksum bcf STATUS,C ;clear carry in rrf BYTECTR,W ;divide byte counter by 2 (word count) movwf BYTECTR ;save it movwf WRTCTR ;save it again (copy) call HEXQ ;read high byte of 16-bit address btfsc FLAGS,NOK ;was there an error? return ;yes, quit movwf FADDRH ;no, store it addwf CHKSUM,F ;add byte to line checksum call HEXQ ;read low byte of 16-bit address btfsc FLAGS,NOK ;was there an error? return ;yes, quit movwf FADDRL ;no, store it addwf CHKSUM,F ;add byte to line checksum bcf STATUS,C ;clear carry-in bit for address fix-up rrf FADDRH,F ;div address by 2 byte -> word address rrf FADDRL,F ;div address by 2 byte -> word address call HEXQ ;read record type btfsc FLAGS,NOK ;was there an error? return ;yes, quit movwf LINTYPE ;no, store it addwf CHKSUM,F ;add byte to line checksum movf LINTYPE,F ;check for data record (00h) btfss STATUS,Z ;is this a data record? goto EOF_REC ;no, check for EOF record type movf FSR,W ;yes, get pointer to start of data movwf DATAFSR ;save it DAT_LOP movf BYTECTR,F ;load byte count btfsc STATUS,Z ;have we read in the whole line? goto EOL_CHK ;yes, check checksum call HEXQ ;no, read low byte of data btfsc FLAGS,NOK ;was there an error? return ;yes, quit addwf CHKSUM,F ;no, add to line checksum call HEXQ ;read high byte of data btfsc FLAGS,NOK ;was there an error? return ;yes, quit addwf CHKSUM,F ;no, add to line checksum decf BYTECTR,F ;decrement bytecount (word counter really) goto DAT_LOP ;go get next word EOL_CHK call HEXQ ;read in checksum btfsc FLAGS,NOK ;was there an error? return ;yes, quit addwf CHKSUM,F ;no, add to line checksum btfss STATUS,Z ;is result 0? goto CHKBAD ;no, we're done movf DATAFSR,W ;yes, reload Que pointer to head movwf FSR ;init pointer WRT_LOP movf WRTCTR,F ;load byte count (copy) btfss STATUS,Z ;have we read in the whole line? goto WRT_NDN ;no if MDROP == OFF movlw '#' ;load ASCII char call TXBYTE ;send character endif goto LIN_LOP ;yes, get next line WRT_NDN call HEXQ ;get Qued and checksummed low data byte movwf FDATAL ;load low byte of program word call HEXQ ;get Qued and checksummed high data byte movwf FDATAH ;load high byte of program word call RANGE ;inhibit self overwrite by user code image btfsc FLAGS,NOK ;overwrite attempt? call ADRERR ;yes, send error message btfsc FLAGS,NOK ;overwrite attempt? return ;yes, quit btfss FLAGS,DIG ;no, is this an EEPROM line? call FWRITE ;no, write a word to program memory btfsc FLAGS,DIG ;is this an EEPROM line? call EHOOK ;yes, write a byte to EEPROM memory btfsc FLAGS,NOK ;was there an error? return ;yes, quit incf FADDRL,F ;no, increment low byte of address btfsc STATUS,Z ;carry out? incf FADDRH,F ;yes, inc high byte decf WRTCTR,F ;decrement bytecount (copy) goto WRT_LOP ;go get next word EOF_REC call CRLF ;send a CR/LF decf LINTYPE,W ;EOF record type = 01h btfsc STATUS,Z ;was it a 1? goto EOF_OK ;yes BAD_REC bsf FLAGS,NOK ;no, set the error flag call RECERR ;send "Invalid record type" error message return ;done EOF_OK call HEXQ ;read in checksum btfsc FLAGS,NOK ;was there an error? return ;yes, quit addwf CHKSUM,F ;no, add to line checksum btfss STATUS,Z ;result should be 0, is it? goto CHKBAD ;no, error bcf FLAG2,PRGFL ;yes, clear the intercept user reset opcode flag movf UDATAH,W ;get intercepted write data movwf FDATAH ;store the data movf UDATAL,W ;get intercepted write data movwf FDATAL ;store the data clrf FADDRH ;store high byte of address to write movlw 03h ;load low byte of target movwf FADDRL ;store low byte of address to write call FWRITE ;store the opcode at the user vector call USRSCAN ;set valid image flag if 0003h = goto btfsc FLAGS,VALID ;valid image? return ;yes, done with upload, success! bsf FLAGS,NOK ;no, set error flag call NOGOTO ;send error to tell why clrf LINCTRO ;clear line counter 1's clrf LINCTRT ;clear line counter 10's clrf LINCTRH ;clear line counter 100's incf LINCTRO,F ;line = 1 return ;done CHKBAD bsf FLAGS,NOK ;no, set the error flag call CRLF ;send a CR/LF call CHKERR ;send "Checksum error" error message return ;yes, done ;************************************************************************* ;EHOOK This routine redirects a FLASH write to EEPROM. EHOOK movf FDATAL,W ;get low byte to write movwf EDATA ;redirect to EEPROM movf FADDRL,W ;get address of byte to write movwf EADDR ;redirect to EEPROM call EWRITE ;store EEPROM byte return ;yes, done ;************************************************************************* ;PROMPT This routine sends a command prompt to the screen. PROMPT movlw 'P' ;load ASCII char call TXBYTE ;send character movlw 'I' ;load ASCII char call TXBYTE ;send character movlw 'C' ;load ASCII char call TXBYTE ;send character movlw '>' ;load ASCII char call TXBYTE ;send character return ;done ;************************************************************************* ;NOGOTO This routine sends "0003h != goto XX". NOGOTO movlw '0' ;load ASCII char call TXBYTE ;send character call TXBYTE ;send character (double) call TXBYTE ;send character (double) movlw '3' ;load ASCII char call TXBYTE ;send character movlw 'h' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw '!' ;load ASCII char call TXBYTE ;send character movlw '=' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 'g' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 't' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 'X' ;load ASCII char call TXBYTE ;send character call TXBYTE ;send character (double) call CRLF ;send a CR/LF return ;done ;************************************************************************* ;CRLF This routine sends a CR-LF pair to the screen. CRLF if MDROP == OFF movlw LFEED ;line feed call TXBYTE ;send character endif movlw CRETURN ;carriage return call TXBYTE ;send character return ;done ;************************************************************************* ;DASH This routine sends " - " to the screen. DASH movlw ' ' ;load ASCII char call TXBYTE ;send character movlw '-' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character return ;done ;************************************************************************* ;FWRITE This routine writes a word to program memory. ; ; The data and address must be passed in with the bank 0 regs ; FDATAH, FDATAL, FADDRH, and FADDRL. This hides all the paging needed. FWRITE bcf FLAGS,FL ;clear the bad flash bit clrwdt ;kick the dog movf FADDRH,W ;load start point high byte btfss STATUS,Z ;is this a write to the user code origin? goto WR_LOOP ;no movlw 03h ;maybe, load a 3 subwf FADDRL,W ;load start point low byte btfss STATUS,Z ;this a write to the user code origin? goto WR_LOOP ;no btfss FLAG2,PRGFL ;yes, is this word from the hex file? goto WR_LOOP ;no movf FDATAH,W ;yes, intercept the write movwf UDATAH ;save the data for later movf FDATAL,W ;intercept the write movwf UDATAL ;save the data for later return ;done WR_LOOP movf FADDRH,W ;get the high byte of the address bsf STATUS,RP1 ;switch to memory page 2 movwf EEADRH ;load it into control reg bcf STATUS,RP1 ;switch to memory page 0 movf FADDRL,W ;get the low byte of the address bsf STATUS,RP1 ;switch to memory page 2 movwf EEADR ;load it into control reg bcf STATUS,RP1 ;switch to memory page 0 movf FDATAH,W ;get the high byte of the data bsf STATUS,RP1 ;switch to memory page 2 movwf EEDATH ;load it into control reg bcf STATUS,RP1 ;switch to memory page 0 movf FDATAL,W ;get the low byte of the data bsf STATUS,RP1 ;switch to memory page 2 movwf EEDATA ;load it into control reg bsf STATUS,RP0 ;switch to memory page 3 bsf EECON1,EEPGD ;set EEPGD to indicate program memory bsf EECON1,WREN ;enable writes to memory movlw 55h ;write sequence movwf EECON2 ;first write movlw 0AAh ;second byte of sequence movwf EECON2 ;second write bsf EECON1,WR ;start write cycle data 3FFFh ;discharge memory lines rev B2 errata data 3FFFh ;discharge memory lines data 3FFFh ;discharge memory lines data 3FFFh ;discharge memory lines data 3FFFh ;discharge memory lines data 3FFFh ;discharge memory lines data 3FFFh ;discharge memory lines data 3FFFh ;discharge memory lines data 3FFFh ;discharge memory lines data 3FFFh ;discharge memory lines data 3FFFh ;discharge memory lines data 3FFFh ;discharge memory lines data 3FFFh ;discharge memory lines data 3FFFh ;discharge memory lines data 3FFFh ;discharge memory lines data 3FFFh ;discharge memory lines nop ;wait nop ;wait bcf EECON1,WREN ;disable writes bsf EECON1,RD ;start read cycle nop ;wait nop ;wait bcf STATUS,RP0 ;switch to memory page 2 movf EEDATH,W ;get high data byte bcf STATUS,RP1 ;switch to memory page 0 subwf FDATAH,W ;compare to what we wrote btfss STATUS,Z ;was write succesful? goto BAD_FL ;no bsf STATUS,RP1 ;yes, switch to memory page 2 movf EEDATA,W ;get low data byte bcf STATUS,RP1 ;switch to memory page 0 subwf FDATAL,W ;compare to what we wrote btfsc STATUS,Z ;was write succesful? return ;yes, done BAD_FL btfsc FLAGS,FL ;no, was this the first try? goto DEADFL ;no bsf FLAGS,FL ;yes, set flag for next pass goto WR_LOOP ;try again DEADFL call FLSHERR ;send "Flash programming error @ xxxh" bsf FLAGS,NOK ;set the programming error flag return ;done ;************************************************************************* ;FREAD This routine reads a word from program memory. ; ; The data must be passed in with the bank 0 regs FADDRH, and FADDRL. ; Data is returned in FDATAH & FDATAL. This hides all the paging needed. FREAD clrwdt ;kick the dog movf FADDRH,W ;get the high byte of the address bsf STATUS,RP1 ;switch to memory page 2 movwf EEADRH ;load it into control reg bcf STATUS,RP1 ;switch to memory page 0 movf FADDRL,W ;get the low byte of the address bsf STATUS,RP1 ;switch to memory page 2 movwf EEADR ;load it into control reg bsf STATUS,RP0 ;switch to memory page 3 bsf EECON1,EEPGD ;set EEPGD to indicate program memory bsf EECON1,RD ;set RD to start data read nop ;wait nop ;wait bcf STATUS,RP0 ;switch to memory page 2 movf EEDATH,W ;get high data byte bcf STATUS,RP1 ;switch to memory page 0 movwf FDATAH ;store it in bank 0 reg bsf STATUS,RP1 ;switch to memory page 2 movf EEDATA,W ;get low data byte bcf STATUS,RP1 ;switch to memory page 0 movwf FDATAL ;store it in bank 0 reg return ;done ;************************************************************************* ;EWRITE This routine write a byte to data memory. ; ; The data must be passed in with the bank 0 regs EADDR and EDATA. ; This hides all the paging needed. EWRITE bcf PIR2,EEIF ;clear write complete flag movf EADDR,W ;get the address bsf STATUS,RP1 ;switch to memory page 2 movwf EEADR ;load it into control reg bcf STATUS,RP1 ;switch to memory page 0 movf EDATA,W ;get the data bsf STATUS,RP1 ;switch to memory page 2 movwf EEDATA ;load it into control reg bsf STATUS,RP0 ;switch to memory page 3 bcf EECON1,EEPGD ;set EEPGD to indicate data memory bsf EECON1,WREN ;set WREN to allow data write movlw 55h ;write sequence movwf EECON2 ;first write movlw 0AAh ;second byte of sequence movwf EECON2 ;second write bsf EECON1,WR ;start write cycle bcf STATUS,RP0 ;switch to memory page 2 bcf STATUS,RP1 ;switch to memory page 0 EEW_LOP clrwdt ;kick the dog btfss PIR2,EEIF ;write completed? goto EEW_LOP ;no bsf STATUS,RP1 ;yes, switch to memory page 2 bsf STATUS,RP0 ;switch to memory page 3 bcf EECON1,WREN ;disable writes bsf EECON1,RD ;start read cycle bcf STATUS,RP0 ;switch to memory page 2 movf EEDATA,W ;get data byte bcf STATUS,RP1 ;switch to memory page 0 subwf EDATA,W ;compare to what we wrote btfss STATUS,Z ;same? call EEERR ;no return ;done ;************************************************************************* ;EREAD This routine reads a byte from data memory. ; ; The data must be passed in with the bank 0 reg EADDRS. ; Data is returned in EDATA. This hides all the paging needed. EREAD clrwdt ;kick the dog movf EADDR,W ;get the address bsf STATUS,RP1 ;switch to memory page 2 movwf EEADR ;load it into control reg bsf STATUS,RP0 ;switch to memory page 3 bcf EECON1,EEPGD ;set EEPGD to indicate data memory bsf EECON1,RD ;set RD to start data read bcf STATUS,RP0 ;switch to memory page 2 movf EEDATA,W ;get data byte bcf STATUS,RP1 ;switch to memory page 0 movwf EDATA ;store it in bank 0 reg return ;done ;************************************************************************* ;RANGE This routine range checks for valid a EEPROM or FLASH address. ; ; It uses the same regs as the FWRITE/FREAD routines. ; DIG flag set = EEPROM address. RANGE bcf FLAGS,DIG ;clear DIG flag (not an EEPROM write) clrw ;load a 0, check for reset hook overwrite subwf FADDRH,W ;compare to high byte of flash address btfss STATUS,Z ;is it 0? goto NOT_PZ ;no clrw ;yes, load a 0 subwf FADDRL,W ;compare to low byte of flash address btfsc STATUS,Z ;is it 0? goto OUT_BND ;yes, 0000h movlw 01h ;no, load a 1 subwf FADDRL,W ;compare to low byte of flash address btfsc STATUS,Z ;is it 1? goto OUT_BND ;yes, 0001h movlw 02h ;no, load a 2 subwf FADDRL,W ;compare to low byte of flash address btfsc STATUS,Z ;is it 2? goto OUT_BND ;yes, 0002h if FULLMEM == ON ;load our memory page to protect it NOT_PZ movlw 18h ;no, load last page address else NOT_PZ movlw 08h ;no, load last page address endif subwf FADDRH,W ;compare to high byte of address btfss STATUS,C ;write target in user code space (less than)? return ;yes, valid write address movf FADDRH,W ;no, load high byte of address sublw 21h ;compare to EEPROM address range btfss STATUS,Z ;is target in EEPROM? goto OUT_BND ;no if FULLMEM == OFF ;check for smaller EEPROM btfsc FADDRL,7 ;maybe, test for address > 80h goto OUT_BND ;no endif bsf FLAGS,DIG ;yes, valid EEPROM address return ;done OUT_BND bsf FLAGS,NOK ;set the error flag for out of bounds return ;done ;************************************************************************* ;ADRERR This routine sends the "Memory protection error". ADRERR movlw 'M' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw 'm' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw 'y' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 'p' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 't' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw 'c' ;load ASCII char call TXBYTE ;send character movlw 't' ;load ASCII char call TXBYTE ;send character movlw 'i' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'n' ;load ASCII char call TXBYTE ;send character call ERRSTR ;send " error " call LPRADR ;send the current flash address pointer return ;done ;************************************************************************* ;PRGMSTR This routine sends the " programming" string. PRGMSTR movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 'p' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'g' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw 'a' ;load ASCII char call TXBYTE ;send character movlw 'm' ;load ASCII char call TXBYTE ;send character call TXBYTE ;send character (double) movlw 'i' ;load ASCII char call TXBYTE ;send character movlw 'n' ;load ASCII char call TXBYTE ;send character movlw 'g' ;load ASCII char call TXBYTE ;send character return ;done ;************************************************************************* ;FLSHERR This routine sends the "FLASH programming error" message. FLSHERR movlw 'F' ;load ASCII char call TXBYTE ;send character movlw 'L' ;load ASCII char call TXBYTE ;send character movlw 'A' ;load ASCII char call TXBYTE ;send character movlw 'S' ;load ASCII char call TXBYTE ;send character movlw 'H' ;load ASCII char call TXBYTE ;send character call PRGMSTR ;send "programming" call ERRSTR ;send " error " call LPRADR ;send the current flash address pointer return ;done ;************************************************************************* ;EEERR This routine displays the "EERPOM programming error" message. EEERR movlw 'E' ;load ASCII char call TXBYTE ;send character call TXBYTE ;send character (double) movlw 'P' ;load ASCII char call TXBYTE ;send character movlw 'R' ;load ASCII char call TXBYTE ;send character movlw 'O' ;load ASCII char call TXBYTE ;send character movlw 'M' ;load ASCII char call TXBYTE ;send character call PRGMSTR ;send "programming" call CRLF ;send a CR/LF bsf FLAGS,NOK ;sent the error flag return ;done ;************************************************************************* ;CMDERR This routine sends the "Invalid command" error message. CMDERR call INVLDS ;send "Invalid " movlw 'c' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'm' ;load ASCII char call TXBYTE ;send character call TXBYTE ;send character (double) movlw 'a' ;load ASCII char call TXBYTE ;send character movlw 'n' ;load ASCII char call TXBYTE ;send character movlw 'd' ;load ASCII char call TXBYTE ;send character call CRLF ;send a CR/LF return ;done ;************************************************************************* ;HEXERR This routine sends the "Non-HEX char" error message. HEXERR movlw 'N' ;send "Non-HEX char" call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'n' ;load ASCII char call TXBYTE ;send character movlw '-' ;load ASCII char call TXBYTE ;send character movlw 'H' ;load ASCII char call TXBYTE ;send character movlw 'E' ;load ASCII char call TXBYTE ;send character movlw 'X' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 'c' ;load ASCII char call TXBYTE ;send character movlw 'h' ;load ASCII char call TXBYTE ;send character movlw 'a' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character call ERRSTR ;send " error " call CRLF ;send a CR/LF return ;done ;************************************************************************* ;INVLDS This routine sends "Invalid ". INVLDS movlw 'I' ;load ASCII char call TXBYTE ;send character movlw 'n' ;load ASCII char call TXBYTE ;send character movlw 'v' ;load ASCII char call TXBYTE ;send character movlw 'a' ;load ASCII char call TXBYTE ;send character movlw 'l' ;load ASCII char call TXBYTE ;send character movlw 'i' ;load ASCII char call TXBYTE ;send character movlw 'd' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character return ;done ;************************************************************************* ;RECERR This routine sends the "Invalid record type" error message. RECERR call INVLDS ;send "Invalid " movlw 'r' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw 'c' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw 'd' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 't' ;load ASCII char call TXBYTE ;send character movlw 'y' ;load ASCII char call TXBYTE ;send character movlw 'p' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character call ERRSTR ;send " error " call CRLF ;send a CR/LF return ;done ;************************************************************************* ;CHKERR This routine sends the "Checksum error" error message. CHKERR movlw 'C' ;load ASCII char call TXBYTE ;send character movlw 'h' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw 'c' ;load ASCII char call TXBYTE ;send character movlw 'k' ;load ASCII char call TXBYTE ;send character movlw 's' ;load ASCII char call TXBYTE ;send character movlw 'u' ;load ASCII char call TXBYTE ;send character movlw 'm' ;load ASCII char call TXBYTE ;send character call ERRSTR ;send " error " call CRLF ;send a CR/LF return ;done ;************************************************************************* ;ERRSTR This routine sends " error ". ERRSTR movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character call TXBYTE ;send character (double) movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character return ;done ;************************************************************************* ;LPRADR This routine prints the current flash address pointer. LPRADR movlw '@' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movf FADDRH,W ;get MSB of the address call DISPHEX ;display it as a hex movf FADDRL,W ;get LSB of the address call DISPHEX ;display it as a hex movlw 'h' ;load ASCII char call TXBYTE ;send character call CRLF ;send a CR/LF return ;done ;************************************************************************* ;LINNUM This displays the current hex file line number. LINNUM movlw 'L' ;load ASCII char call TXBYTE ;send character movlw 'i' ;load ASCII char call TXBYTE ;send character movlw 'n' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 'n' ;load ASCII char call TXBYTE ;send character movlw 'u' ;load ASCII char call TXBYTE ;send character movlw 'm' ;load ASCII char call TXBYTE ;send character movlw 'b' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character bcf FLAGS,DIG ;clear leading sig digit bit movf LINCTRH,W ;get hundreds digit btfsc STATUS,Z ;is it a zero, strip leading 0? goto LIN_TEN ;yes, skip this digit bsf FLAGS,DIG ;no, set leading sig digit bit call ASCII ;asciify digit call TXBYTE ;send character LIN_TEN movf LINCTRT,W ;get tens digit btfsc FLAGS,DIG ;is there a leading sig digit? goto NS_TEN ;yes, inhibit digit skip btfsc STATUS,Z ;no, is this a 0 also? goto LIN_ONE ;yes, skip this digit NS_TEN call ASCII ;no, asciify digit call TXBYTE ;send character LIN_ONE movf LINCTRO,W ;get ones digit call ASCII ;asciify digit call TXBYTE ;send character call CRLF ;send a CR/LF return ;done ;************************************************************************* ;REV_MSG This routine prompts for a user code rev string. REV_MSG movlw 'E' ;load ASCII char call TXBYTE ;send character movlw 'n' ;load ASCII char call TXBYTE ;send character movlw 't' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 'a' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character call REVSTR ;send "rev string" movlw '>' ;load ASCII char call TXBYTE ;send character return ;done ;************************************************************************* ;EXITMSG This routine sends the "Upload succesful" message. EXITMSG movlw 'U' ;load ASCII char call TXBYTE ;send character movlw 'p' ;load ASCII char call TXBYTE ;send character movlw 'l' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'a' ;load ASCII char call TXBYTE ;send character movlw 'd' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 's' ;load ASCII char call TXBYTE ;send character movlw 'u' ;load ASCII char call TXBYTE ;send character movlw 'c' ;load ASCII char call TXBYTE ;send character call TXBYTE ;send character (double) movlw 'e' ;load ASCII char call TXBYTE ;send character movlw 's' ;load ASCII char call TXBYTE ;send character movlw 'f' ;load ASCII char call TXBYTE ;send character movlw 'u' ;load ASCII char call TXBYTE ;send character movlw 'l' ;load ASCII char call TXBYTE ;send character call CRLF ;send a CR/LF return ;done ;************************************************************************* ;CMDLIST This routine sends a list of valid commands to the screen. ; ; ? - Display this list ; Q - Quit and jump to user code ; U - Upload user code to program memory ; D - Download user code from program memory ; W - Write to EEPROM, "W DD @ AA" ; R - Read from EEPROM, "R AA" ; V - Display user code revision string CMDLIST clrf TBLPTR ;clear all bits of the table pointer bsf TBLPTR,1 ;set index to this string call COPYRT ;send first 2 lines from DT table call USERST ;send " user code " call CRLF ;send a CR/LF movlw 'U' ;load ASCII char call TXBYTE ;send character call DASH ;send " - " movlw 'U' ;load ASCII char call TXBYTE ;send character movlw 'p' ;load ASCII char call TXBYTE ;send character movlw 'l' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'a' ;load ASCII char call TXBYTE ;send character movlw 'd' ;load ASCII char call TXBYTE ;send character call USERST ;send " user code " movlw 't' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character call PRGMEM ;" program memory" movlw 'D' ;load ASCII char call TXBYTE ;send character call DASH ;send " - " movlw 'D' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'w' ;load ASCII char call TXBYTE ;send character movlw 'n' ;load ASCII char call TXBYTE ;send character movlw 'l' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'a' ;load ASCII char call TXBYTE ;send character movlw 'd' ;load ASCII char call TXBYTE ;send character call USERST ;send " user code " movlw 'f' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'm' ;load ASCII char call TXBYTE ;send character call PRGMEM ;" program memory" clrf TBLPTR ;clear all bits of the table pointer bsf TBLPTR,2 ;set index to this string call COPYRT ;send last 3 lines from DT table call USERST ;send " user code " call REVSTR ;send "rev string" call CRLF ;send a CR/LF return ;done ;************************************************************************* ;REVSTR This routine displays "rev string". REVSTR movlw 'r' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw 'v' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 's' ;load ASCII char call TXBYTE ;send character movlw 't' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw 'i' ;load ASCII char call TXBYTE ;send character movlw 'n' ;load ASCII char call TXBYTE ;send character movlw 'g' ;load ASCII char call TXBYTE ;send character return ;done ;************************************************************************* ;PRGMEM This routine displays " program memory\n". PRGMEM movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 'p' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'g' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw 'a' ;load ASCII char call TXBYTE ;send character movlw 'm' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 'm' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw 'm' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw 'y' ;load ASCII char call TXBYTE ;send character call CRLF ;send a CR/LF return ;done ;************************************************************************* ;USRSCAN This routine scans for a user image. USRSCAN bcf FLAGS,VALID ;clear the valid user code flag clrf FADDRH ;store high byte of address to read movlw 03h ;load low byte of target movwf FADDRL ;store low byte of address to read call FREAD ;read from 0003h (user code reset vector) movf FDATAH,W ;get high byte of opcode @ 0003h andlw B'00111000' ;strip off unwanted bits xorlw B'00101000' ;compare to goto opcode bits btfsc STATUS,Z ;is opcode @ 0003h a goto? (valid user code)? bsf FLAGS,VALID ;yes, set valid user code flag return ;done ;************************************************************************* ;CODESTR This routine displays the revision string of the user code. CODESTR btfss FLAGS,VALID ;is there a user image? goto NO_CODE ;no, part is blank call CODETBL+1 ;yes, get first byte in code rev string andlw 0FFh ;mask all bits in returned byte btfsc STATUS,Z ;was a null(0) returned? goto NO_CSTR ;yes clrf TBLPTR ;no, clear all bits of the table pointer bsf TBLPTR,4 ;set index to this string call COPYRT ;send "Current image = " clrf TBLPTR ;clear all bits of the table pointer bsf TBLPTR,3 ;set index to this string call COPYRT ;send code rev string goto CSTREND ;done NO_CODE movlw 'N' ;send "No user code loaded" call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character call USERST ;send " user code " movlw 'l' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'a' ;load ASCII char call TXBYTE ;send character movlw 'd' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw 'd' ;load ASCII char call TXBYTE ;send character goto CSTREND ;done NO_CSTR movlw 'N' ;send "No user code revision string" call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character call USERST ;send " user code " call REVSTR ;send "revision string" CSTREND call CRLF ;send a CR/LF return ;done ;************************************************************************* ;HELPIST This routine displays "Press ? for help". HELPIST call PRESS ;send "Press " movlw '?' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 'f' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 'h' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw 'l' ;load ASCII char call TXBYTE ;send character movlw 'p' ;load ASCII char call TXBYTE ;send character call CRLF ;send a CR/LF return ;done ;************************************************************************* ;PRESS This routine displays "Press ". PRESS movlw 'P' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw 's' ;load ASCII char call TXBYTE ;send character call TXBYTE ;send character (double) movlw ' ' ;load ASCII char call TXBYTE ;send character return ;done ;************************************************************************* ;TMRMSG This routine displays "Jumping to user code". TMRMSG movlw 'J' ;load ASCII char call TXBYTE ;send character movlw 'u' ;load ASCII char call TXBYTE ;send character movlw 'm' ;load ASCII char call TXBYTE ;send character movlw 'p' ;load ASCII char call TXBYTE ;send character movlw 'i' ;load ASCII char call TXBYTE ;send character movlw 'n' ;load ASCII char call TXBYTE ;send character movlw 'g' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 't' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character call USERST ;send " user code " call CRLF ;send a CR/LF return ;done ;************************************************************************* ;USERST This routine displays " user code ". USERST movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 'u' ;load ASCII char call TXBYTE ;send character movlw 's' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw 'r' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character movlw 'c' ;load ASCII char call TXBYTE ;send character movlw 'o' ;load ASCII char call TXBYTE ;send character movlw 'd' ;load ASCII char call TXBYTE ;send character movlw 'e' ;load ASCII char call TXBYTE ;send character movlw ' ' ;load ASCII char call TXBYTE ;send character return ;done ;************************************************************************* ;HEXIFY This routine turns a ASCII digit in "W" into a HEX value. ; The char must be uppercase. HEXIFY movwf TEMP ;save input value movlw '0' ;load a zero subwf TEMP,F ;subtract zero offset btfss STATUS,C ;underflow = non hex? goto BADHEX ;yes, bail movlw '9'-'0'+1 ;no, load 9 + 1 subwf TEMP,W ;subtract 10 offset btfss STATUS,C ;underflow = 0 to 9? goto RTNHEX ;yes, return result movlw 'A'-'0' ;no, load value to make 'A' = 0 subwf TEMP,F ;subtract offset btfss STATUS,C ;underflow = non hex? goto BADHEX ;yes, bail movlw 'F'-'A'+1 ;no, load max offset subwf TEMP,W ;subtract 10 offset btfsc STATUS,C ;underflow = A to F? goto BADHEX ;no, bail movlw 0Ah ;yes, load 10 addwf TEMP,F ;set value to 10-15 RTNHEX movf TEMP,W ;send hex result return ;done BADHEX bsf FLAGS,NOK ;no, set the error flag call HEXERR ;send "Non-HEX char" error message retlw 00h ;return a null ;************************************************************************* ;COPYRT This routine retrieves a message from table space. ; ; TBLPTR is a bitwise selector for the message to retrieve. COPYRT clrf TEMP ;clear index COPLOP movf TEMP,W ;get loop index btfsc TBLPTR,0 ;bit 0 set = copyright selected? call COPYTBL ;yes, get char from table btfsc TBLPTR,1 ;no, bit 1 set = helpstr selected? call HLPTBL ;yes, get char from table btfsc TBLPTR,2 ;no, bit 2 set = insstr selected? call INSTBL ;yes, get char from table btfsc TBLPTR,3 ;no, bit 3 set = code rev str selected? call CODETBL ;yes, get char from table btfsc TBLPTR,4 ;no, bit 4 set = "Current image = "? call IMGTBL ;yes, get char from table andlw 0FFh ;test for zero btfsc STATUS,Z ;was a null returned? return ;yes, done call TXBYTE ;no, send character incf TEMP,F ;inc the index goto COPLOP ;get next char in table ;************************************************************************* ;INSTBL This routine hold the beginning of the help message. ; "W - Write to EEPROM, "W DD @ AA" ; "R - Read from EEPROM, "R AA" ; "V - Display" if FULLMEM == ON ;load origin for table data org 1F03h ;page 1Fh is for all table jumps else org 0F03h ;page 0Fh is for all table jumps endif INSTBL addwf PCL,F ;add to PCL to jump into table DT "W - Write to EEPROM, " ;string retlw '"' ;quote char DT "W DD @ AA" ;string retlw '"' ;quote char if MDROP == OFF retlw LFEED ;line feed char endif retlw CRETURN ;carriage return DT "R - Read from EEPROM, " ;string retlw '"' ;quote char DT "R AA" ;string retlw '"' ;quote char if MDROP == OFF retlw LFEED ;line feed char endif retlw CRETURN ;carriage return DT "V - Display" ;string retlw 00h ;null teminator ;************************************************************************* ;IMGTBL This routine displays the image identifier. IMGTBL addwf PCL,F ;add to PCL to jump into table DT "Current image = " ;string retlw 00h ;null teminator ;************************************************************************* ;HLPTBL This routine hold the beginning of the help message. ; ; "? - Display this list" ; "Q - Quit and jump to" HLPTBL addwf PCL,F ;add to PCL to jump into table DT "? - Display this list" ;string if MDROP == OFF retlw LFEED ;line feed char endif retlw CRETURN ;carriage return DT "Q - Quit and jump to" ;string retlw 00h ;null teminator ;************************************************************************* ;COPYTBL This routine stores the startup splash screen. ; ; "xK PICLOADER vX.Y" ; "Copyright Rick Farmer 1999" COPYTBL addwf PCL,F ;add to PCL to jump into table retlw ESCAPE ;used to start an escape sequence retlw '[' ;second character of an escape sequence retlw '2' ;ESC[2J = clear screen retlw 'J' ;ESC[2J = clear screen if FULLMEM == ON ;display processor FLASH memory size retlw '8' ;# Kbytes of total memory else retlw '4' ;# Kbytes of total memory endif DT "K PICLOADER v1.1" if MDROP == OFF retlw LFEED ;line feed char endif retlw CRETURN ;carriage return char DT "Copyright Rick Farmer 1999" if MDROP == OFF retlw LFEED ;line feed char endif retlw CRETURN ;carriage return char retlw 00h ;null terminator ;************************************************************************* ;CODETBL This table stores a revision string for the user code. ; ; Data is indexed by "W", the string is null terminated. if FULLMEM == ON ;set fixed origin for user code rev string org 1FC4h ;page 1Fh is for all table jumps else org 0FC4h ;page 0Fh is for all table jumps endif CODETBL addwf PCL,F ;add to PCL to jump into the table retlw 00h ;null char (org xFC5h) retlw 00h ;null char , init string to null retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char retlw 00h ;null char, 32nd char ;************************************************************************* ;ASCII This routine turns a 0-F value in "W" to an ascii char. ASCII addwf PCL,F ;add to PCL to jump into table retlw '0' ;0 - load ASCII char retlw '1' ;1 retlw '2' ;2 retlw '3' ;3 retlw '4' ;4 retlw '5' ;5 retlw '6' ;6 retlw '7' ;7 retlw '8' ;8 retlw '9' ;9 retlw 'A' ;A retlw 'B' ;B retlw 'C' ;C retlw 'D' ;D retlw 'E' ;E retlw 'F' ;F ;************************************************************************* ;PASTBL This hold the 8 char user pasword. Index by "W". PASTBL addwf PCL,F ;add to PCL to jump into table DT "Hello123" ;user password string, max 8 chars retlw 00h ;return a null terminator (org xFFFh) ;************************************************************************* ; EEPROM Presets org 2100h ;EEPROM origin if MDROP == ON ;check to see if line is addressed to us DE 00h ;default unit address (00h) endif end ;compiler finish ;*************************************************************************