;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; QB4520v1.0, the QwikBug monitor for the PIC18F4520 ; ; Converted to the 4520 by Jason Uher Summer 06 ; Rawin Rajvanit 1-7-03 ; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; list P=PIC18F4520, F=INHX32, C=160, N=0, ST=OFF, MM=OFF, R=DEC, X=ON #include P18F4520.inc ;Config 1H CONFIG OSC=HS, IESO=OFF, FCMEN=OFF ;Config 2L CONFIG PWRT=ON, BOREN=OFF ;Config 2H CONFIG WDT=OFF ;Config 3H CONFIG CCP2MX=PORTC ;Config 4L CONFIG DEBUG=ON, LVP=OFF, STVREN=OFF ;Config 5L CONFIG CP0=OFF, CP1=OFF, CP2=OFF, CP3=OFF ;Config 5H CONFIG CPB=OFF, CPD=OFF ;Config 6L CONFIG WRT0=OFF, WRT1=OFF, WRT2=OFF, WRT3=OFF ;Config 6H CONFIG WRTB=OFF, WRTC=OFF, WRTD=OFF ;Config 7L CONFIG EBTR3=OFF, EBTR2=OFF, EBTR1=OFF, EBTR0=OFF ;Config 7H CONFIG EBTRB=OFF errorlevel -314, -315 ;Ignore lfsr messages ;;;;;;; Constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;------Banked addressing of Background debug variables ------------------------ Debug_Bank equ 5 ;Last bank for 452 = 5 ;----- Watch flags in watchTest byte ------------------------------------------ #define ISALPHA watchTest,0 #define CurrDispWatchFlag watchTest,1 #define LastDispWatchFlag watchTest,2 #define ValidAsciiVar watchTest,3 ;----- Background debug register addresses ------------------------------------ DEBUG equ 0XFD4 ICKBUG equ 0xFB9 BIGBUG equ 0xFB8 GLOBUG equ 0XFB7 SLGBUG equ 0XFB5 ;----- DEBUG register BITS ---------------------------------------------------- INBUG equ 7 FREEZ equ 6 SSTEP equ 5 SHDW equ 4 BRB7 equ 3 BRB6 equ 2 BTRIS7 equ 1 BTRIS6 equ 0 ;----WATCH VARIABLE BITS------------ REVERSE_BIT equ 6 ;Bit position ;set if reverse RADIX_BITS equ 0x30 ;Bit position(5 and 4) HEX_DEFAULT equ 0x00 ;Bit values HEX_RADIX equ 0x10 BIN_RADIX equ 0x20 ASCII_RADIX equ 0x30 LENGTH_BITS equ 0x0F ;Bit position ;0 default length of 1 ;2-9 valid (single digit only) ;----WATCH_FLAG------------------------- INVALID_COMMAND equ 7 VALID_HEX equ 6 ;----REGISTER_TEMP---------------------- TMR0_ON equ 0 TMR1_ON equ 1 TMR2_ON equ 2 TMR3_ON equ 3 ;-----QB_FLAGS--------------------------- ;bit 0 to 4 of QB_FLAGS are reserved for reset conditon BOR equ 0 POR equ 1 PD equ 2 TO equ 3 RI equ 4 FROM_USER equ 5 ;set b4 calling tret , on enter, if set - don't show QB welcome,show watch var instead. SHOW_LABEL equ 6 ;if set, must show watch label, clear after display.., set by ;a: running w/o valid breakpoint ;b: modify bp.. INVALID_BP equ 7 ;only display running... when invalid bp... ;-----QB_FLAGS2------------------------------- FROM_MAINLINE equ 0 FNKEYS equ 1 ;set if key pressed was a function key, clear if not (see GetCommand) LOAD_RESET equ 2 ;1 = load reset occur, 0 = no load reset occur ;-----STKPRT----------------------------------- STKFUL equ 7 STKUNF equ 6 ;----- Miscellaneous Constants ------------------------------------------------ BaudConstant equ 0x1E ;Constant for baud generator for 19200 baud ;0x40 - 9600 baud with Fosc is 10MHz ;0x1F - 19200 baud with Fosc is 10MHz ;UserMemory equ 0x2000 UserMemory equ 0x6000 ;User memory extends from 0x0000 to 0x5FFF ;(QwikBug resides in 0x6000 to < 0x7DC0 Num_64_blocks equ UserMemory/64 ;Number of 64 byte blocks for user ;64 x 128 = 8192 bytes (0x0000 - 0x2000) ; = (8192-64) = 8128 bytes for user code StartDelay equ 2 ;Number in seconds before auto start WatchDepth equ 10 ;Sets the number of watch variables available BUFFER_SIZE equ 12 ;for LINEBUF ;;;;;;; Variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; cblock Debug_Bank*256 ;256 = 0x100 WREG_TEMP ;Storage for user-program state information STATUS_TEMP FSR0H_TEMP FSR0L_TEMP FSR1H_TEMP FSR1L_TEMP FSR2H_TEMP FSR2L_TEMP PCLATU_TEMP PCLATH_TEMP BSR_TEMP TBLPTRU_TEMP TBLPTRH_TEMP TBLPTRL_TEMP PCH_TEMP PCL_TEMP PCH_TEMP_NEXT PCL_TEMP_NEXT REGISTER_TEMP ;holds misc bits ie: GIEH (bit 7) ;TMR0_ON bit 0 ;TMR1_ON bit 1 ;TMR2_ON bit 2 ;TMR3_ON bit 3 TEMP0 ;Temporary variables TEMP1 COUNT ;General-purpose 8-bit counters COUNT1 COUNT2 FKEY ;Numeric value of selected function key ;LINEBUF:BUFFER_SIZE+1 ;Line buffer (# chars allow + 0x00[null]) LINEBUF:16 ;DON'T CHANGE!! MUST BE 16, for writing to flash memory PDATA:8 ;Data to be written to program memory NUMWORDS ;Five variables used in F3 "Load" code CHECKSUM ADDRH ADDRL RECTYPE TRAM ;Three variables used by F9 "Modify" code HEXBYTE ; and F3 "Load" code BNK MONITORSTATE ;Used in BDM as a flag tester ;Bit - Description ; 0 - Run User Code ; 1 - Single Step Code ; 4 - No User Reset Code ; 6 - User Code Transmit Error ; 7 - User Code Impinges on Bootloader Code WATCHARRAY:3*WatchDepth ;Buffer for storing watch data ;(WatchDepth specifies total number of variables) ;Each watch variable needs 3 bytes in WATCHARRAY. ;First byte (bits separated by colon) ; INVALID_COMMAND (1) : REVERSE (1) : RADIX (2) : LENGTH (4) ; bit7: INVALID - 1 means invalid WATCHARRAY element ; - 0 means valid WATCHARRAY element ; bit6: reverse - 1 means reverse the byte order ; - 0 means display the bytes in the order ; bits5,4: display format ; - 00 is unused ; - 01 means hex format ; - 10 means binary format ; - 11 means ASCII formata ; bits3,2,1,0: length in bytes up to 9 ; ;Second byte is the higher byte of the watch address ;Third byte is the lower byte of the watch address ;initial value of QB_FLAGS should be 0x11011101 (set all reset flag,except POR, set INVALID_BP,set SHOW_LABEL, clear FROM_USER) QB_FLAGS ;Bit - Description - Values ;1 power on reset 0:power on reset 1:not power on reset ;6 breakpoint set 0:not set 1:set ;5 show label? 0:don't show 1:show QB_FLAGS2 ;Bit ;0 when hitting a key for (DEL_INPUT) WATCH_FLAG WATCH_TEMP0 WATCH_TEMP1 WATCH_TEMP2 MAX_USER_CODE_HIGH MAX_USER_CODE_LOW WORD_OFFSET COUNT_H COUNT_L endc ;;;;;;; Macro definitions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; MOVLF macro literal,dest ;Move literal to register movlw literal movwf dest endm INC0 macro ;Increment FSR0 movf POSTINC0,F endm DEC0 macro ;Decrement FSR0 movf POSTDEC0,F endm INC1 macro ;Increment FSR1 movf POSTINC1,F endm DEC1 macro ;Decrement FSR1 movf POSTDEC1,F endm POINT macro stringname ;Point TBLPTR to stringname MOVLF high stringname, TBLPTRH MOVLF low stringname, TBLPTRL endm WATCHCOMMAND macro param movf INDF0,W ;Get command iorlw B'00100000' ;Convert to lower case xorlw param endm SAVEREGISTERS macro ;Save special function registers movff WREG, WREG_TEMP ;Restore WREG (without changing flags) movff STATUS, STATUS_TEMP ;Save STATUS movff FSR0H, FSR0H_TEMP ;Save FSR regs movff FSR0L, FSR0L_TEMP movff FSR1H, FSR1H_TEMP movff FSR1L, FSR1L_TEMP movff FSR2H, FSR2H_TEMP movff FSR2L, FSR2L_TEMP movff PCLATU, PCLATU_TEMP ;Save PCLATH movff PCLATH, PCLATH_TEMP movff TBLPTRU,TBLPTRU_TEMP ;Save TBLPTR movff TBLPTRH,TBLPTRH_TEMP movff TBLPTRL,TBLPTRL_TEMP endm RESTREGISTERS macro ;Restore special function regs movff TBLPTRU_TEMP,TBLPTRU;Restore TBLPTR movff TBLPTRH_TEMP,TBLPTRH movff TBLPTRL_TEMP,TBLPTRL movff BSR_TEMP, BSR movff PCLATH_TEMP, PCLATH ;Restore PCLATH movff PCLATU_TEMP, PCLATU movff FSR0H_TEMP, FSR0H ;Restore FSR regs movff FSR0L_TEMP, FSR0L movff FSR1H_TEMP, FSR1H movff FSR1L_TEMP, FSR1L movff FSR2H_TEMP, FSR2H movff FSR2L_TEMP, FSR2L movff STATUS_TEMP,STATUS ;Restore STATUS movff WREG_TEMP, WREG ;Restore WREG (without changing flags) endm ;;;;;;; Reset vector ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; org 0x0000 ;Reset vector ;;;;;;; The following code gets executed by ICD2 as user code ;;;;;;;;;;;;;;;;; ;;;;;;; It inserts a "goto QwikBug" instruction at 0x7dc0 ;;;;;;;;;;;;;;;;;;;;; ;;;;;;; and turns on the right LED ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;; Subsequent vectoring to BDM will goto QwikBug ;;;;;;;;;;;;;;;;;;;;;;;;; ModifyBDMvector ;First erase 64 locations beginning at 0x007dc0 clrf TBLPTRU ; which is starting address of ICD2's BDM code MOVLF 0x7d,TBLPTRH MOVLF 0xc0,TBLPTRL bsf EECON1,EEPGD ;Point to FLASH program memory bcf EECON1,CFGS ; and not configuration bytes bsf EECON1,WREN ;Enable write to memory bsf EECON1,FREE ;Enable row erase operation bcf INTCON,GIEH ;Disable interrupts MOVLF 0x55,EECON2 ;Write Hex 55 to EECON2 Reg MOVLF 0xaa,EECON2 ;Write Hex AA to EECON2 Reg bsf EECON1,WR ;Start erase nop bsf INTCON,GIEH ;Next, write new vectoring instruction, ; goto QwikBug ;Note that goto QwikBug assembles to ; EF01 F030 ;and that the hex file has the resulting bytes ; 01EF30F0 MOVLF 0x01,TABLAT ;Write 0x00 to latch for address 0x007dc0 tblwt*+ MOVLF 0xef,TABLAT ;Write 0xef to latch for address 0x007dc1 tblwt*+ MOVLF 0x30,TABLAT ;Write 0x10 to next latch tblwt*+ MOVLF 0xf0,TABLAT ;Write 0xf0 to next latch clrf TABLAT tblwt*+ ;Fill remaining four latches tblwt*+ ; with two nop instructions tblwt*+ tblwt* bsf EECON1,EEPGD ;Point to FLASH program memory bcf EECON1,CFGS ; and not configuration bytes bsf EECON1,WREN ;Enable write to memory bcf EECON1,FREE ;Program, not erase bcf INTCON,GIEH MOVLF 0x55,EECON2 ;Write Hex 55 to EECON2 Reg MOVLF 0xaa,EECON2 ;Write Hex AA to EECON2 Reg bsf EECON1,WR ;Start write nop bsf INTCON,GIEH bcf EECON1,WREN ;Disable further writes to program memory ;Now, turn on right LED to signal completion MOVLF B'10001110',ADCON1 ;Enable PORTA & PORTE digital I/O pins MOVLF B'11100001',TRISA ;Set I/O for PORTA MOVLF B'00010000',PORTA ;Turn off all four LEDs driven from PORTA bsf PORTA,1 ;Turn on right LED bra $ ; and stop goto QwikBug ; Needed just to get machine code for above ;;;;;;; START OF BDM CODE IN PROGRAM MEMORY ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;; QwikBug program ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; org 64*(Num_64_blocks) ;0x6000 for PIC18F452 QwikBug_Trap bra $ ;Trap to insure QwikBug is reached with a goto ;and not with an incrementing program counter QwikBug movff BSR, BSR_TEMP movlb Debug_Bank ;switch bank only after saving BSR IF_ INTCON,GIEH == 1 ;save interrupt status bcf INTCON,GIEH ;if set, disable it. bsf REGISTER_TEMP,GIEH ELSE_ bcf REGISTER_TEMP,GIEH ENDIF_ SAVEREGISTERS ;save registers IF_ T0CON,TMR0ON == 1 bcf T0CON,TMR0ON bsf REGISTER_TEMP,TMR0_ON ELSE_ bcf REGISTER_TEMP,TMR0_ON ENDIF_ IF_ T1CON,TMR1ON == 1 bcf T1CON,TMR1ON bsf REGISTER_TEMP,TMR1_ON ELSE_ bcf REGISTER_TEMP,TMR1_ON ENDIF_ IF_ T2CON,TMR2ON == 1 bcf T2CON,TMR2ON bsf REGISTER_TEMP,TMR2_ON ELSE_ bcf REGISTER_TEMP,TMR2_ON ENDIF_ IF_ T3CON,TMR3ON == 1 bcf T3CON,TMR3ON bsf REGISTER_TEMP,TMR3_ON ELSE_ bcf REGISTER_TEMP,TMR3_ON ENDIF_ bcf DEBUG,FREEZ ;unfreeze peripherals (all timers have been turned off) call SerialSetup IF_ QB_FLAGS2,LOAD_RESET == 0 clrf MONITORSTATE ;Clear BDM flags ENDIF_ call CheckResetCondition ;and update QB_FLAGS accordingly movf QB_FLAGS,W andlw 0x13 ;mask out every bit except BOR,POR, and RI xorlw 0x13 ;if not zero, POR,BOR, or RI occured. IF_ .NZ. ;following should be called by any reset IF_ QB_FLAGS2,LOAD_RESET == 0 clrf QB_FLAGS2 ;Initialize QB_FLAGS2 call WatchClear ;Initialize WATCHARRAY call BreakPoint_Clear ;clear breakpoint ELSE_ bcf QB_FLAGS2,LOAD_RESET ENDIF_ clrf REGISTER_TEMP clrf PCH_TEMP clrf PCL_TEMP clrf PCH_TEMP_NEXT clrf PCL_TEMP_NEXT IF_ QB_FLAGS,POR == 0 ;only execute next section for POR 3+ POINT sQwikBug ;Display menu call StringDisplay POINT sFnKeyMenu call StringDisplay call AutoStart call MonitorFlags ENDIF_ ENDIF_ ;next section cannot be in a subroutine, don't move it! IF_ QB_FLAGS,FROM_USER == 1 IF_ DEBUG,SSTEP == 1 movff PCH_TEMP_NEXT,PCH_TEMP movff PCL_TEMP_NEXT,PCL_TEMP ELSE_ IF_ QB_FLAGS,INVALID_BP == 1 call CheckForUserCode IF_ MONITORSTATE,4 == 0 bsf MONITORSTATE,1 ;Single Step User Code ENDIF_ movff TOSH,PCH_TEMP_NEXT movff TOSL,PCL_TEMP_NEXT ;movf RCREG,W bcf RCSTA, SPEN ; then clear it bsf RCSTA, SPEN ; and reenable receives ;I cant get this prompt to display correctly for the life of me, here is the fix: movlw 'Q' REPEAT_ UNTIL_ PIR1,TXIF == 1 ;Wait for previous transfer to be completed movwf TXREG ;Transmit byte movlw '!' ;This makes no sense, somehome the but it getting recieved incorrectly only here, but it ; is consistent, so I can just make my own prompt by sending "Q!>" (it seems that the 2 bit gets ; to the 4 bit each time, but only here. REPEAT_ UNTIL_ PIR1,TXIF == 1 ;Wait for previous transfer to be completed movwf TXREG ;Transmit byte movlw '>' REPEAT_ UNTIL_ PIR1,TXIF == 1 ;Wait for previous transfer to be completed movwf TXREG ;Transmit byte ;POINT sshortPrompt ;must show prompt, because below, we check SSTEP before RCIF ;call StringDisplay ;and therefore the extra sPrompt will not be shown, so must display here call MonitorFlags ELSE_ ;we got here cause of a breakpoint bsf DEBUG,SHDW movf BIGBUG,W movwf PCH_TEMP movf GLOBUG,W movwf PCL_TEMP bcf DEBUG,SHDW bcf STATUS,C ;BKA is half the PC to break, must x2 to display rlcf PCL_TEMP,F rlcf PCH_TEMP,F ENDIF_ ENDIF_ ENDIF_ movff TOSH,PCH_TEMP_NEXT ;store next PC movff TOSL,PCL_TEMP_NEXT IF_ QB_FLAGS,FROM_USER == 1 ;check if from user bcf QB_FLAGS,FROM_USER ;clear flag IF_ QB_FLAGS,INVALID_BP == 1 ;if from user and not a breakpoint, IF_ DEBUG,SSTEP == 0 ;not a single step, IF_ PIR1,RCIF == 1 ;and a key was pressed POINT sPrompt ;must be an interrupt key call StringDisplay ;show extra prompt for WatchLabels ENDIF_ ENDIF_ ENDIF_ ;movlw ':' ;call TXbyte IF_ QB_FLAGS,SHOW_LABEL == 1 bcf QB_FLAGS,SHOW_LABEL call DisplayWatchLabels POINT sshortPrompt ;print prompt for WatchData call StringDisplay ENDIF_ call DisplayWatchData ;always show WatchData if from user ENDIF_ MainLoop LOOP_ POINT sPrompt ;Display prompt call StringDisplay bsf QB_FLAGS2,FROM_MAINLINE call GetCommand ;Get line bcf QB_FLAGS2,FROM_MAINLINE call CommandInterpreter After_Interpreter call MonitorFlags ;MONITORSTATE is clear upon return from this call. ENDLOOP_ ;;;;;;; CheckResetCondition subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CheckResetCondition IF_ STKPTR,STKUNF == 1 ;detect stack underflow by checking STKFUL, purpose to show error only bcf STKPTR,STKUNF POINT sSTKUNF call StringDisplay ENDIF_ IF_ STKPTR,STKFUL == 1 ;detect stack overflow by checking STKUNF bcf STKPTR,STKFUL POINT sSTKFUL call StringDisplay ENDIF_ movf RCON,W andlw 0x1F ;mask out bit 5,6 and 7 xorlw 0x1F ;see if all one, if not, a reset condition occured IF_ .Z. ;not a detectable reset, see if MCLR, or from user IF_ QB_FLAGS,FROM_USER == 1 ;from user(run or sstep), but by key interrupt? or MCLR? breakpoint? or SSTEP? IF_ QB_FLAGS,INVALID_BP == 1;if not breakpoint, then check if it is a single step IF_ DEBUG,SSTEP == 0 ;if not SSTEP,then check if a key was pressed call LoopTime ;wait incase it is a key (fill up RCREG) IF_ PIR1,RCIF == 0 ;if no key pressed, must be MCLR bcf RCON,POR ;MCLR from user is equivalent to POR ENDIF_ ENDIF_ ENDIF_ ELSE_ ;not from user, not a detectable reset; must be MCLR bcf RCON,POR ;MCLR from BDM is equivalent to POR ENDIF_ ENDIF_ IF_ RCON,POR == 0 ;Power-on Reset MOVLF 0xDD,QB_FLAGS ;Initialize QB_FLAGS (with POR clear) clrf QB_FLAGS2 ;Initialize QB_FLAGS2 bsf RCON,POR bsf RCON,BOR ;BOR bit is low on POR also, must set back high ELSE_ bsf QB_FLAGS,POR ENDIF_ IF_ RCON,BOR == 0 ;Brown-out Reset bcf QB_FLAGS,BOR bsf RCON,BOR POINT sBOR call StringDisplay ELSE_ bsf QB_FLAGS,BOR ENDIF_ IF_ RCON,RI == 0 ;Reset Instruction bcf QB_FLAGS,RI bsf RCON,RI IF_ QB_FLAGS2,LOAD_RESET == 0 POINT sReset call StringDisplay ENDIF_ ELSE_ bsf QB_FLAGS,RI ENDIF_ return ;;;;;;; StringDisplay subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine displays the string that is pointed to by TBLPTR. It sends ; the string to the PC via the UART. Must use the macro POINT to ; point to the correct string. ; ; Syntax: ; POINT string ; call StringDisplay StringDisplay tblrd* ;Read byte from string movf TABLAT,W ; into WREG REPEAT_ call TXbyte ;Send byte via UART tblrd+* ;Increment pointer movf TABLAT,W ;Load next byte UNTIL_ .Z. return ;;;;;;; TXbyte subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine waits for the completion of the last transfer. Then it ; takes whatever is in W and sends it to the PC via the UART. TXbyte andlw 0x7F ;clear out that top bit REPEAT_ UNTIL_ PIR1,TXIF == 1 ;Wait for previous transfer to be completed movwf TXREG ;Transmit byte return ;;;;;;; RXbyte subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine waits for a byte to be received in UART. It returns with ; the byte in WREG. RXbyte IF_ RCSTA,OERR == 1 ;If an overrun has occurred bcf RCSTA, CREN ; then clear it bsf RCSTA, CREN ; and reenable receives ENDIF_ REPEAT_ UNTIL_ PIR1,RCIF == 1 ;Wait for byte to be received movf RCREG,W ;Put received byte into W IF_ PIR1, RCIF == 1 bcf RCSTA, SPEN ; then clear it bsf RCSTA, SPEN ; and reenable receives ENDIF_ return ;;;;;;; CR_LF subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine sends a carriage return, line feed to the PC. CR_LF movlw 0x0d ;Send out CR call TXbyte movlw 0x0a ;and newline call TXbyte return ;;;;;;; SerialSetup subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine initializes the UART. SerialSetup movlw BaudConstant ;set baud rate movwf SPBRG bsf TXSTA,BRGH ;baud rate high speed option bsf TXSTA,TXEN ;enable transmission bcf TXSTA, SYNC bsf RCSTA,CREN ;enable reception bsf RCSTA,SPEN ;enable serial port return ;;;;;;; AutoStart subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Upon reset, this subroutine times out if no key is pressed. ; It runs user code (if loaded) in StartDelay seconds AutoStart call CheckForUserCode ;Check if User Code is Loaded btfsc MONITORSTATE,4 ;User code loaded? bra Exit_Delay StartDelay1 ;Code added 1/7/03 ;Following fixes the problem of fault autostart when max232 chip is not ;present. It clears the flag that probably has been set by PIC's ;misinterpretation of low voltage signal at startup (transient power?) ;A more proper fix is to have pull up resistors on RX and TX lines. IF_ PIR1,RCIF == 1 REPEAT_ movf RCREG,W bcf RCSTA, SPEN ; then clear it bsf RCSTA, SPEN ; and reenable receives UNTIL_ PIR1,RCIF == 0 ENDIF_ ;end code added MOVLF StartDelay,TEMP0;Load delay seconds movf TEMP0,F btfsc STATUS,Z bra RunCode ;If zero delay is requested then exit POINT sDelay call StringDisplay POINT sAbort call StringDisplay movlw '[' ;display '[ ]' for startup call TXbyte SPCLOOP movlw ' ' call TXbyte decfsz TEMP0,F bra SPCLOOP movlw ']' call TXbyte MOVLF StartDelay+1,TEMP0;Load delay seconds BSPCLOOP movlw 0x08 ;backspace char call TXbyte decfsz TEMP0,F bra BSPCLOOP MOVLF StartDelay,TEMP0;Load delay seconds ChkByte btfss RCSTA,OERR bra ChkByte_1 bcf RCSTA,CREN ;Check for key press bsf RCSTA,CREN ChkByte_1 btfsc PIR1,RCIF bra Exit_DelayCR ;Key pressed, jump to monitor MOVLF 100,TEMP1 ;Wait for 1 Second Loop1Sec call LoopTime decf TEMP1,F bnz Loop1Sec movlw A'#' ; send out # character call TXbyte decfsz TEMP0,F ;decrement counter bra ChkByte ;Keep counting RunCode call CR_LF ;send out CR POINT sPrompt call StringDisplay bsf MONITORSTATE,0 ;Set the run user code flag. bra Exit_Delay Exit_DelayCR call CR_LF Exit_Delay return ;;;;;;; ChkForUserResetVector subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine checks for a proper user reset vector stored at UserResetCode. ; It is considered to have the proper form if it complies to: ; zero or one or two "nop" instructions followed by a ; "goto" instruction. ; It clears MONITORSTATE,4 if a reset vector is found, setting it otherwise. ; ; Note that the opcode of a goto instruction is efhh fhhh ; and the opcode of a nop instruction is 0000 ; Also note that the lower byte of each two-byte instruction is located at an ; even address (i.e., at the lower of the two addresses for the instruction). ChkForUserResetVector bsf MONITORSTATE,4 ;Initialize as No User Reset Vector POINT 0x0000 MOVLF 3,COUNT REPEAT_ tblrd*+ movf TABLAT,F ;Check for zero IF_ .NZ. ;If not zero, check next byte for 0xef tblrd*+ movf TABLAT,W sublw 0xef IF_ .Z. bcf MONITORSTATE,4 ;Found "goto" instruction ENDIF_ BREAK_ ;Done in either case ELSE_ tblrd*+ movf TABLAT,W sublw 0xef IF_ .Z. bcf MONITORSTATE,4 ;Found "goto" instruction BREAK_ ;Exit in this case ELSE_ movf TABLAT,W IF_ .NZ. BREAK_ ;Not a "nop", so exit in this case also ENDIF_ ENDIF_ ENDIF_ decf COUNT,F UNTIL_ .Z. return CheckForUserCode ;No user code loaded.. ;idea is to loop from 0x0000 to UserMemory - 1 ;read from each byte address and check if 0xFF, if not stop loop ;clrf TBLPTRU bsf MONITORSTATE,4 ;Initialize as No User Reset Vector clrf TBLPTRH clrf TBLPTRL MOVLF HIGH UserMemory,COUNT_H MOVLF LOW UserMemory,COUNT_L ; lfsr 2,UserMemory can't use FSR2 as counter, it's only 12 bits! LOOP_ tblrd*+ movf TABLAT,W xorlw 0xFF IF_ .NZ. BREAK_ ENDIF_ decf COUNT_L,F IF_ .B. decf COUNT_H,F ENDIF_ movf COUNT_L,F IF_ .Z. movf COUNT_H,F IF_ .Z. BREAK_ ENDIF_ ENDIF_ ENDLOOP_ ;if COUNT_H,L are non-zero, code exists.. ;to make checking simpler, COUNT_H will be modify to 1 if code exists ;and 0 if code does not exist movf COUNT_H,F IF_ .Z. movf COUNT_L,F IF_ .NZ. ;MOVLF 0x01,COUNT_H bcf MONITORSTATE,4 ENDIF_ ELSE_ ;MOVLF 0x01,COUNT_H bcf MONITORSTATE,4 ENDIF_ return ;;;;;;; MonitorFlags subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine checks flags from nested subroutines and acts accordingly. ; It is called after CommandInterpreter. ; The go-to-user-program flag bit is tested in the mainline loop. MonitorFlags IF_ MONITORSTATE,6 == 1 ;CHECKSUM Transmit error call UserMemoryErase ;Erase user memory block POINT sTError ;Display message call StringDisplay ELSE_ IF_ MONITORSTATE,7 == 1 ;User code outside of User addr space call UserMemoryErase ;Erase user memory block POINT sLargeCode ;Display message call StringDisplay ELSE_ IF_ MONITORSTATE,4 == 1 ;No user reset vector is loaded POINT sNoUserVector ;Display message call StringDisplay ELSE_ IF_ MONITORSTATE,1 == 1 ;Single step call SingleStepCode ELSE_ IF_ MONITORSTATE,0 == 1 ;run user code call RunUserCode ENDIF_ ENDIF_ ENDIF_ ENDIF_ ENDIF_ clrf MONITORSTATE return SingleStepCode pop ;pop RunUserCode pop ;pop MonitorFlags movf QB_FLAGS,W andlw 0x13 ;mask out every bit except BOR,POR, and RI xorlw 0x13 ;if not zero, POR,BOR, or RI occured. IF_ .NZ. ;must push stack after reset incf STKPTR,F MOVLF H'00',TOSU MOVLF H'00',TOSH MOVLF H'00',TOSL ENDIF_ bsf QB_FLAGS,FROM_USER bsf DEBUG,SSTEP bsf DEBUG,FREEZ ;freeze peripheral IF_ REGISTER_TEMP,TMR0_ON == 1 bsf T0CON,TMR0ON ENDIF_ IF_ REGISTER_TEMP,TMR1_ON == 1 bsf T1CON,TMR1ON ENDIF_ IF_ REGISTER_TEMP,TMR2_ON == 1 bsf T2CON,TMR2ON ENDIF_ IF_ REGISTER_TEMP,TMR3_ON == 1 bsf T3CON,TMR3ON ENDIF_ IF_ REGISTER_TEMP,GIEH == 1 bsf INTCON,GIEH ENDIF_ RESTREGISTERS tret ;this will restart the timers,etc. RunUserCode pop ;pop RunUserCode pop ;pop MonitorFlags IF_ QB_FLAGS,INVALID_BP == 1 POINT sRunning call StringDisplay ENDIF_ movf QB_FLAGS,W andlw 0x13 ;mask out every bit except BOR,POR, and RI xorlw 0x13 ;if not zero, POR,BOR, or RI occured. IF_ .NZ. ;must push stack after reset incf STKPTR,F MOVLF H'00',TOSU MOVLF H'00',TOSH MOVLF H'00',TOSL ENDIF_ bsf QB_FLAGS,FROM_USER bcf DEBUG,SSTEP ;must have this line! if sstep was executed, ;SSTEP is not automatically cleared when returning to BDM bsf DEBUG,FREEZ ;freeze peripheral IF_ REGISTER_TEMP,TMR0_ON == 1 bsf T0CON,TMR0ON ENDIF_ IF_ REGISTER_TEMP,TMR1_ON == 1 bsf T1CON,TMR1ON ENDIF_ IF_ REGISTER_TEMP,TMR2_ON == 1 bsf T2CON,TMR2ON ENDIF_ IF_ REGISTER_TEMP,TMR3_ON == 1 bsf T3CON,TMR3ON ENDIF_ IF_ REGISTER_TEMP,GIEH == 1 bsf INTCON,GIEH ENDIF_ RESTREGISTERS tret ;;;;;;; GetCommand subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine waits for a line to be entered by the user. ; ; Line is stored in LINEBUF (limited to 12 byte line, for our purposes) ; ; Exiting Conditions: ; - Function Key is pressed: FKEY holds non-zero value of key ; - User presses Enter: LINEBUF is returned with entered string ; - User exceeds bytes in line: LINEBUF is returned with null string GetCommand ;IF_ PIR1,RCIF == 1 ;this is to trap interrupt key from autostart and BDM entry from user ; REPEAT_ ;might be more than one char, remove all ; movf RCREG,W ; UNTIL_ PIR1,RCIF == 0 ;ENDIF_ ;See note about "cheap hack" in the RXbyte subroutine: (note that this removes all keys so we dont need above code) bcf RCSTA, SPEN bsf RCSTA, SPEN lfsr 0,LINEBUF ;Set up pointer into LINEBUF MOVLF BUFFER_SIZE, COUNT1 ;Counter forces 12 chars available for input clrf FKEY ;Initialize Function Key Status REPEAT_ call RXbyte ;Receive a character movwf INDF0 ; and put it into LINEBUF ;since 'b' and 'd' are shortcuts, can only call CheckShortcut if from 'QB>' IF_ QB_FLAGS2,FROM_MAINLINE == 1 call CheckShortcut ;Check for shortcut keys movf FKEY,F ;Did we receive a function key (NZ=1)? IF_ .NZ. ;Yes; exit BREAK_ ENDIF_ ENDIF_ movf INDF0,W call CheckFuncKeys ;Check for function keys movf FKEY,F ;Did we receive a function key (NZ=1)? IF_ .NZ. ;Yes; exit bsf QB_FLAGS2,FNKEYS BREAK_ ELSE_ bcf QB_FLAGS2,FNKEYS ENDIF_ movf INDF0,W ;Is it the enter key (CR)? xorlw 0x0D IF_ .Z. ;Yes MOVLF 0,INDF0 ;Null terminate the string rcall CR_LF ;Send CR, LF and exit BREAK_ ENDIF_ movf INDF0,W ;Is it a backspace? xorlw 0x08 IF_ .Z. ;Yes movlw low LINEBUF ;Look for empty LINEBUF xorwf FSR0L,W IF_ .NZ. ;Backspace if not at beginning of the line movlw 0x08 ;Backspace call TXbyte movlw A' ' ;Erase previous character call TXbyte movlw 0x08 ;Backspace call TXbyte DEC0 ;Decrement FSR0 (due to backspace) incf COUNT1,F ;Increment counter ENDIF_ ELSE_ movf POSTINC0,W ;Echo received character call TXbyte decf COUNT1,F ;Decrement space remaining in LINEBUF IF_ .Z. MOVLF 0,INDF0 ;Null terminate the string rcall CR_LF ;and send CR, LF ENDIF_ ENDIF_ movf COUNT1,F ;Repeat until line buffer is full UNTIL_ .Z. return ;;;;;;; FkeyDecode subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This function looks at the character(s) that follow ESC and [ ; The coding of the F1-F12 keys are, in order, 11-15,17-19,20-21,23-24. ; Following the two digits is the ASCII code for ~ (i.e., 0x7E). ; For F1-F12, FKEY will be returned as 1-12 (decimal). ; For anything other than these, FKEY will be returned as 0. FkeyDecode clrf FKEY ;Initialize the offset variable with 0 xorlw A'1' ;Set Z=1 if first character equals 1 IF_ .Z. MOVLF 10,FKEY ELSE_ xorlw 3 ;Original A'2' now sets Z=1 IF_ .Z. MOVLF 20,FKEY ENDIF_ ENDIF_ call RXbyte ;Get the next byte into WREG movf FKEY,F ;FKEY now contains 0, 10, or 20 IF_ .NZ. andlw 0x0f ;and strip ASCII to binary addwf FKEY,F ;Add to previous 10 or 20 movlw 10 subwf FKEY,F ;Convert to 1 to 14 for function keys POINT sFKey ;Point to start of table movf FKEY,W bcf STATUS,C addwf TBLPTRL,F ;Get Fkey value from table IF_ .C. incf TBLPTRH,F ENDIF_ tblrd* movff TABLAT,FKEY ENDIF_ call RXbyte ;Get the ~ and ignore it return ;;;;;;; LoopTime subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine uses a two-byte counter to give an approximate ten ; millisecond delay without using any of the user's timer resources. LoopTime clrf WREG MOVLF 32,COUNT REPEAT_ REPEAT_ ;Inner loop takes 256x3x0.4 = 303 microsec decf WREG,F UNTIL_ .Z. decf COUNT,F UNTIL_ .Z. ;Outer loop takes 303x32 = 9830 microsec return ;;;;;;; CheckShortcut subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CheckShortcut iorlw B'00100000' ;Convert to lower case movwf TEMP0 xorlw A'h' IF_ .Z. MOVLF 0x01,FKEY ENDIF_ movf TEMP0,W xorlw A't' IF_ .Z. MOVLF 0x02,FKEY ENDIF_ movf TEMP0,W xorlw A'l' IF_ .Z. MOVLF 0x03,FKEY ENDIF_ movf TEMP0,W xorlw A'd' IF_ .Z. MOVLF 0x04,FKEY ENDIF_ movf TEMP0,W xorlw A'b' IF_ .Z. MOVLF 0x05,FKEY ENDIF_ movf TEMP0,W xorlw A'w' IF_ .Z. MOVLF 0x06,FKEY ENDIF_ movf TEMP0,W xorlw A'r' IF_ .Z. MOVLF 0x07,FKEY ENDIF_ movf TEMP0,W xorlw A's' IF_ .Z. MOVLF 0x08,FKEY ENDIF_ movf TEMP0,W xorlw A'm' IF_ .Z. MOVLF 0x09,FKEY ENDIF_ return ;;;;;;; CheckFuncKeys subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Function keys (F1-F12) are of the form ESC [ ~ ; where : ; "11" F1 ; "12" F2 ; "13" F3 ; "14" F4 ; "15" F5 ; "17" F6 ; "18" F7 ; "19" F8 ; "20" F9 ; "21" F10 ; "23" F11 ; "24" F12 ; ; This function will look for ESC. If the next character is not a '[', ; it will return. However, if the sequence ESC [ is detected, it will ; assume an escape sequence is coming and will wait for the ~ to return. ; FKEY = 0 if none of the above keys is detected. Otherwise, it is ; returned as 1-12. CheckFuncKeys xorlw 0x1b ;XOR WREG with 0x1b (which is ESC) IF_ .Z. ;If ESC, then look for [ call RXbyte ;Get Next byte movwf INDF0 ;write to current position in INDF xorlw A'[' ;XOR WREG with '[' IF_ .Z. call RXbyte ;Get next byte movwf INDF0 ; write to current position in INDF0 call FkeyDecode ; F1-F12 -> 0x00-0x0E in FKEY ENDIF_ ENDIF_ return ; return, wait for next character ;;;;;;; CommandInterpreter subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine processes the input command from the PC. ; It is entered from MainLoop. CommandInterpreter movf FKEY,F ;Z=0 if not a function key IF_ .NZ. call FnKeyAction ;Take appropriate action for function key ; by going to KeyAct1...KeyAct12 below ; via a table of goto's at start of monitor ENDIF_ clrf LINEBUF ;Clear first byte in line buffer (stop) return ;;;;;;; FnKeyAction jump table ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Table to get to an action routine in response to the press of a ; function key. FnKeyAction clrf PCLATU MOVLF HIGH FnKeyAction1,PCLATH bcf STATUS,C rlcf FKEY, F bcf STATUS,C rlcf FKEY, F ;x4 movlw LOW FnKeyAction1 bcf STATUS,C addwf FKEY,W IF_ .C. incf PCLATH,F ENDIF_ movf FKEY,W addwf PCL, F ;PCL is not the PCL of this line (addwf), but the following instruction FnKeyAction1 nop ;nops needed for proper alignment of goto's nop ;smallenst FKEY is 1, no zero, therefore two nop to offset. goto KeyAct1 goto KeyAct2 goto KeyAct3 goto KeyAct4 goto KeyAct5 goto KeyAct6 goto KeyAct7 goto KeyAct8 goto KeyAct9 goto KeyAct10 goto KeyAct11 goto KeyAct12 ;;;;;;; Function key action routines;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; The instruction "call FnKeyAction" will vector to one of the following ; routines via the FnKeyAction lookup table at the beginning of the monitor code. KeyAct1 POINT sFnKeyMenu call StringDisplay return ;return address on STACK so pop off. KeyAct2 bcf QB_FLAGS,SHOW_LABEL bcf QB_FLAGS,FROM_USER reset return KeyAct3 bsf QB_FLAGS,SHOW_LABEL rcall UserMemoryErase ;erases user memory block rcall Loader ;call code to load user code bsf QB_FLAGS2,LOAD_RESET MOVLF 40,TEMP1 ;Wait for 0.4 Second (could be less) REPEAT_ call LoopTime decf TEMP1,F UNTIL_ .Z. reset ;reset is a good solution to reinitialize stack AND SFRs ; clrf STKPTR ; bcf QB_FLAGS,RI ;have to fake reset flag, RunUserProgram checks this to force 0x0000 push into stack ; goto After_Interpreter KeyAct4 call DisplayWatchLabels POINT sshortPrompt ;Display prompt call StringDisplay call DisplayWatchData return KeyAct5 call GetBreakPtInput return KeyAct6 POINT sWatchMenu rcall StringDisplay ;display watch menu rcall GetWatchInput return KeyAct7 call CheckForUserCode ;Check if User Code is Loaded IF_ MONITORSTATE,4 == 0 bsf MONITORSTATE,0 ;Run User Code ENDIF_ return KeyAct8 call CheckForUserCode ;Check if User Code is Loaded IF_ MONITORSTATE,4 == 0 bsf MONITORSTATE,1 ;Single Step User Code ENDIF_ return KeyAct9 call Modify return KeyAct10 POINT sF10 call StringDisplay return KeyAct11 POINT sF11 call StringDisplay return KeyAct12 POINT sF12 call StringDisplay call ShowFlashMemory return ShowFlashMemory call GetCommand MOVLF high LINEBUF,FSR0H ;pointed to by FSR0 MOVLF low LINEBUF,FSR0L POINT sTitles call StringDisplay MOVLF H'00',TBLPTRU ;MOVLF H'00',TBLPTRH call ASC2toBinary movff HEXBYTE,TBLPTRH MOVLF H'00',TBLPTRL MOVLF 255,COUNT MOVLF D'16',COUNT1 REPEAT_ movlw D'16' xorwf COUNT1,W IF_ .Z. movf TBLPTRU,W call DisplayHexByte movlw A' ' call TXbyte movf TBLPTRH,W call DisplayHexByte swapf TBLPTRL,W call BinarytoASC call TXbyte movlw A' ' call TXbyte ENDIF_ tblrd*+ ;movf TABLAT,W ;call DisplayHexByte IF_ TBLPTRL,0 == 1 movff TABLAT,TEMP1 ELSE_ movf TABLAT,W call DisplayHexByte movf TEMP1,W call DisplayHexByte ENDIF_ IF_ TBLPTRL,0 == 0 movlw A' ' call TXbyte ENDIF_ decf COUNT1,F movlw 0x00 xorwf COUNT1,W IF_ .Z. movlw A'\r' call TXbyte movlw A'\n' call TXbyte MOVLF D'16',COUNT1 ENDIF_ decf COUNT,F UNTIL_ .Z. call CR_LF return ;--------------------------KEY ACT 3 SUBROUTINES--------------------- ;------------------------------------------------------------------------------ ;UserMemoryErase - routine erases memory in 64 byte segments. ;Erases Num_64_blocks 64 byte blocks UserMemoryErase movlw H'00' movwf TBLPTRU ;Start erasing at address 0 movwf TBLPTRH movwf TBLPTRL lfsr 2,Num_64_blocks ;load FSR2 with Num_64_blocks (128 for 0x2000) REPEAT_ REPEAT_ rcall FlashErase64 ;Erase Current 64 byte segment movlw 64 ;Increment TBLPTR to next 64 addwf TBLPTRL,F ;byte segment btfsc STATUS,C ;If rollover then increment incf TBLPTRH,F ;high byte movf POSTDEC2,F movf FSR2L,F UNTIL_ .Z. movf FSR2H,F UNTIL_ .Z. return ;----------------------------------------------------------------------------- ;This Loader subroutine responds to the command to load user code. Loader clrf MONITORSTATE ;indicate no troublesome addresses yet call SerialSetup ;set up serial port POINT sStartDL ;display Start to Send Message call StringDisplay ;display message to send hex file GetNewLine ;Get new line of hex file starting with ':' ;Extracts # of bytes, address, & record type call RXbyte ;get new byte from serial port xorlw ':' ;check if ':' received btfss STATUS,Z bra GetNewLine ;if not then wait for next byte clrf CHECKSUM ;start with checksum zero call GetHEXBYTE ;get number of program data bytes in line andlw 0x1F ;limit number in case of error in file movwf NUMWORDS bcf STATUS,C rrcf NUMWORDS,F ;divide by 2 to get number of words (range 1 to 8) call GetHEXBYTE ;get upper half of program start address movwf ADDRH call GetHEXBYTE ;get lower half of program start address movwf ADDRL call GetHEXBYTE ;get record type for testing movwf RECTYPE xorlw 0x01 btfsc STATUS,Z ;Z=1 if end of file record (0x01) bra Filedone movf RECTYPE,W xorlw 0x02 btfsc STATUS,Z ;Z=1 if Segment Record (0x02) bra GNLdone call GetData_CS ;get and write data bytes and checksum byte btfsc MONITORSTATE,7 bra GNLwithError ;if set, "Overwrite monitor error" occurred btfsc MONITORSTATE,6 bra GNLwithError ;if set, "CHECKSUM error" occurred ;capture program words received from PC ;into line buffer, then program them ;into flash (up to four words), then ;send back a period to PC and handle ;a new line (needs 50 ms line delay) bra GetNewLine Filedone: call GetHEXBYTE ;Get CheckSum from 0x01 (EOF) GNLdone: call CheckForUserCode btfsc MONITORSTATE,4 bra GNLwithError ;if set, "No User Reset Code" POINT sSuccess ;display Successful load call StringDisplay call RXbyte ;Clean out UART call DisplayDownloadInfo GNLwithError ;exit with error flag 6 or 7 ;movf MONITORSTATE,W ;call DisplayHexByte ;movlw A'a' ;call TXbyte return ;Loader ;----------------------------------------------------------------------------- ;Receive two ascii digits and convert into one hex byte GetHEXBYTE call RXbyte ;get new byte from serial port addlw 0xbf ;add -'A' to Ascii high byte btfss STATUS,C ;check if positive addlw 0x07 ;if not, add 17 ('0' to '9') addlw 0x0a ;else add 10 ('A' to 'F') movwf HEXBYTE ;save nibble swapf HEXBYTE,F ;move nibble to high position call RXbyte ;get new byte from serial port addlw 0xbf ;add -'A' to Ascii low byte btfss STATUS,C ;check if positive addlw 0x07 ;if not, add 17 ('0' to '9') addlw 0x0a ;else add 10 ('A' to 'F') iorwf HEXBYTE,F ;add low nibble to high nibble movf HEXBYTE,W ;put result in W reg addwf CHECKSUM,F ;add to cumulative checksum return ;------------------------------------------------------------------------------ ;GetData_CS subroutine gets data bytes and checksum from line into linebuffer. ;If constraints are OK, it writes the entire line into flash. GetData_CS ;start of Data Array to FSR, so we can receive lfsr 0,LINEBUF ;set FSR0 pointer to LINEBUF movff NUMWORDS, COUNT1 ;set counter to number of words GetDataFromLine: ;Receive a line of data and put it into ;linebuffer, then program the line call GetHEXBYTE ;get low data byte movwf POSTINC0 ;point to high byte destination call GetHEXBYTE ;get high data byte movwf POSTINC0 ;point to next low byte destination decfsz COUNT1,F bra GetDataFromLine call GetHEXBYTE ;get checksum movf CHECKSUM,F ;Z=1 if checksum correct btfsc STATUS,Z bra CheckType bsf MONITORSTATE,6 ;CHECKSUM error bra GDCSdone CheckType: movf RECTYPE,W ;get record type for testing xorlw 0x04 btfsc STATUS,Z ;Z=1 if Linear Record Type (0x04) bra LinearRec ;Handle Linear Record Case movf RECTYPE,W ;Z=1 if Data Record Type (0x00) btfss STATUS,Z bra GDCSdone ;Z=0 if invalid Record Type rcall ChkProgramSpc ;checks if overwriting address space btfss MONITORSTATE,7 ;are we into the monitor addresses? rcall ProgramFlash ;No, then program the line received bra GDCSdone LinearRec: ;If Record Type is Linear Record movf LINEBUF+1,W ;Move Upper Address Byte into W xorlw H'30' ;Test if Configuration Info btfss STATUS,Z bra GDCSdone ;NOT 30 SkipLine call RXbyte ;get new byte from serial port xorlw ':' ;check if ':' received btfss STATUS,Z bra SkipLine ;if not then wait for next byte bra GDCSdone GDCSdone: movlw '.' ;line has been saved in flash call TXbyte return ;------------------------------------------------------------------------------ ;ProgramFlash - routine takes the AddressXand programs the line recieved ;into flash. ProgramFlash ;Get data 4 words (8 bytes) at a time from ;buffer (LINEBUF) to program into flash MOVLF 0x00,TBLPTRU movff ADDRH,TBLPTRH movlw B'11111000' ;Clear lower 3 bits to get 8 byte andwf ADDRL,W ;block start address at B'xxxxx000' movwf TBLPTRL MOVLF HIGH PDATA,FSR0H ;Data Mem address to store data read MOVLF LOW PDATA,FSR0L MOVLF 4,COUNT1 ;Read 4 words from Flash to RAM call FlashRead movlw B'00000111' ;Save Lower 3 bits to get FSR0 offset andwf ADDRL,W ;(Values either:0,2,4,6) movwf TEMP1 movlw LOW PDATA addwf TEMP1,W movwf FSR0L MOVLF HIGH PDATA,FSR0H ;Get Offset into PDATA MOVLF HIGH LINEBUF, FSR1H ;start of Data Array to FSR1 MOVLF LOW LINEBUF, FSR1L bcf STATUS,C rrcf TEMP1,F ;Get TEMP1 in Word Format (0,1,2 or 3 quit @ 4) movff NUMWORDS, COUNT1 ;set counter to half number of bytes (8 -> 1) Data_2_Prog movff POSTINC1,POSTINC0 ;LINEBUF --> PDATA (low byte) movff POSTINC1,POSTINC0 ;LINEBUF --> PDATA (high byte) incf TEMP1,F decf COUNT1,F btfsc STATUS,Z bra Done_Data_2_Prog ;If counter is 0, done transfering movf TEMP1,W xorlw 4 ;If TEMP1 = 4, done transfering btfss STATUS,Z bra Data_2_Prog Done_Data_2_Prog ;Now Ready to Write MOVLF 0x00,TBLPTRU movff ADDRH,TBLPTRH movlw B'11111000' ;Clear lower 3 bits to get 8 byte andwf ADDRL,W ;block start address at B'xxxxx000' movwf TBLPTRL MOVLF HIGH PDATA,FSR0H ;Data mem to be written MOVLF LOW PDATA,FSR0L StartWrite: call FlashWrite ;write 8 bytes to flash movf COUNT1,F btfsc STATUS,Z ;If Counter is 0 then No more data on line ;Else write next set of bytes addresses with ; lower address 8 - 15 bra GDCSdone ;Done Writing Line to Program Memory TBLRD*+ ;Dummy Read Increment MOVLF HIGH PDATA,FSR0H ;Point to beginning of PDATA MOVLF LOW PDATA,FSR0L movff COUNT1,TEMP1 ;Save Off Counter MOVLF 4,COUNT1 ;Read 4 words from Flash to RAM call FlashRead movff TEMP1,COUNT1 ;Restore Counter MOVLF HIGH PDATA,FSR0H ;Data Mem address to store data read MOVLF LOW PDATA,FSR0L ;LINEBUF is already at correct spot via FSR1 Data_2_Prog2 movff POSTINC1,POSTINC0 ;LINEBUF --> PDATA (low byte) movff POSTINC1,POSTINC0 ;LINEBUF --> PDATA (high byte) decfsz COUNT1,F goto Data_2_Prog2 ;TBLPTR is already at correct spot MOVLF HIGH PDATA,FSR0H ;Data mem to be written MOVLF LOW PDATA,FSR0L clrf TBLPTRU movff ADDRH,TBLPTRH movlw B'11111000' ;Clear lower 3 bits to get 8 byte andwf ADDRL,W ;block start address at B'xxxxx000' movwf TBLPTRL movlw 8 addwf TBLPTRL,F btfsc STATUS,C incf TBLPTRH,F call FlashWrite ;write 2 8 bytes of line to flash return ;----------------------------------------------------------------------------- ;ChkProgramSpc checks to see if address is above program space. VERIFIED. ChkProgramSpc movff ADDRH,MAX_USER_CODE_HIGH movff ADDRL,MAX_USER_CODE_LOW movff NUMWORDS,WORD_OFFSET bcf MONITORSTATE,7 ;default to no conflict movlw high QwikBug_Trap subwf ADDRH,W IF_ .NN. IF_ .Z. ;ADDRH = QwikBug(high), check ADDRL movlw low QwikBug_Trap subwf ADDRL,W IF_ .NN. ;ADDRL >= QwikBug(low) bsf MONITORSTATE,7 ;set flag for error ENDIF_ ELSE_ ;positive result bsf MONITORSTATE,7 ;ADDRH > QwikBug(high) ENDIF_ ENDIF_ return ;DisplayDownloadInfo displays the maximum user memory address used. ;Accomplish by adding the number of byte to the starting byte address ;in the last valid data line of intel hex code. DisplayDownloadInfo POINT sHighAddress call StringDisplay movf MAX_USER_CODE_HIGH,W call DisplayHexByte bcf STATUS,C rlcf WORD_OFFSET,W ;adjust to byte offset addlw -0x02 ;subtract two bytes off addwf MAX_USER_CODE_LOW,W ;update low only, adding byte offset will never roll over call DisplayHexByte call CR_LF return ;;;;;;; FlashWrite ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Subroutine Writes 4 words (8 bytes) from Data Memory to Program Memory ; precond: Program Memory address contained in TBLPTR ; RAM address pointed to by FSR0 FlashWrite MOVLF 4,TEMP1 ;Read 4 words (8 bytes) to write TBLRD*- ;Dummy Read Decrement WriteWord movff POSTINC0,TABLAT ;Get low byte and present data to table latch TBLWT+* ;Write data movff POSTINC0,TABLAT ;Get high byte and present data to table latch TBLWT+* ;Write data decfsz TEMP1 ;Loop until buffers are full goto WriteWord WriteMemory bsf EECON1,EEPGD ;point to FLASH program memory bcf EECON1,CFGS ;point away from config memory ;;;9/15/04 bsf EECON1,WREN ;enable write to memory call WriteInitiateSequence bcf EECON1,WREN ;disable write to memory return ;;;;;;; FlashRead ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Subroutine Reads in a Word (2 bytes) from Program Memory to Data Memory ; precond: Program Memory address contained in TBLPTR ; RAM address pointed to by FSR0 ; COUNT1 = Number of Words to be Read FlashRead TBLRD*+ movff TABLAT,POSTINC0 ;Get low byte TBLRD*+ movff TABLAT,POSTINC0 ;Get high byte decfsz COUNT1 ;Decrement counter (in Words not Bytes) goto FlashRead ;Read Bext Byte return ;;;;;;; FlashErase64 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Subroutine Erases a 64-byte block starting at an TBLPTR ; precond: Program Memory address contained in TBLPTR FlashErase64 bsf EECON1,EEPGD ;point to FLASH program memory bcf EECON1,CFGS ;point away from config memory ;;;9/15/04 bsf EECON1,WREN ;enable write to memory bsf EECON1,FREE ;enable row erase operation call WriteInitiateSequence return WriteInitiateSequence IF_ DEBUG,INBUG == 1 ;erase or write only while in BDM MOVLF 0x55,EECON2 ;write Hex 55 to EECON2 Reg MOVLF 0xAA,EECON2 ;write Hex AA to EECON2 Reg bsf EECON1,WR ;start erase or write operation nop ENDIF_ return ;;;;;;;;;;;;;;;;;;;;;;;;;;; KEYACT5 SUBROUTINES ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;; GetBreakPtInput subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine displays the Breakpoint prompt. Then it looks for either a ; four-hex-character address to set a breakpoint or N for no breakpoint. GetBreakPtInput POINT sBreakPtMenu call StringDisplay LOOP_ POINT sF5 call StringDisplay call GetCommand ;Get user input from LINEBUF ;MOVLF high LINEBUF,FSR0H ;pointed to by FSR0 ;MOVLF low LINEBUF,FSR0L lfsr 0,LINEBUF ;if 4 valid hex follows by 0x00...then call Breakpoint_Set ;else, if 'r' follow by 0x00, call Breakpoint_Clear ;else, if 'd' follow by 0x00, call DisplayBreakPoint ;else show invalid command. IF_ QB_FLAGS2,FNKEYS == 0 ;make sure not a function key (which could be A'1' or A'2', see FkeyDecode) movf INDF0,F IF_ .NZ. ;if an enter key, do not show invalid command INC0 movf INDF0,F IF_ .Z. ;if 2nd is 0x00 DEC0 WATCHCOMMAND 'r' IF_ .Z. call BreakPoint_Clear POINT sNoBreakpointSet call StringDisplay BREAK_ ;Quit loop if valid command ELSE_ WATCHCOMMAND 'd' IF_ .Z. call DisplayBreakPoint BREAK_ ;Quit loop if valid command ELSE_ ;neither 'r' or 'd' POINT sInvalidCommand call StringDisplay ENDIF_ ENDIF_ ELSE_ ;2nd not 0x00, see if valid bka DEC0 MOVLF 0x04,COUNT bsf WATCH_FLAG,VALID_HEX REPEAT_ IF_ WATCH_FLAG,VALID_HEX == 1 movf POSTINC0,W call CheckHexDigit ENDIF_ decf COUNT,F UNTIL_ .Z. IF_ WATCH_FLAG,VALID_HEX == 1 movf INDF0,F ;if valid_hex, INDF0 is at 5th position IF_ .Z. ;check for 0x00 call BreakPoint_Set call DisplayBreakPoint BREAK_ ;Quit loop if valid command ELSE_ POINT sInvalidCommand call StringDisplay ENDIF_ ELSE_ POINT sInvalidCommand call StringDisplay ENDIF_ ENDIF_ ELSE_ ;else 0x00 at 1st position BREAK_ ENDIF_ ;end if not 0x00 at 1st position ELSE_ call CR_LF POINT sInvalidCommand call StringDisplay ENDIF_ ;end if function key ENDLOOP_ return BreakPoint_Clear bsf QB_FLAGS,SHOW_LABEL bsf QB_FLAGS,INVALID_BP bsf DEBUG,SHDW movlw 0x0F ;Modify only lower nibble of ICKBUG iorwf ICKBUG,F MOVLF 0xFF,BIGBUG MOVLF 0xFF,GLOBUG bcf DEBUG,SHDW return BreakPoint_Set bsf QB_FLAGS,SHOW_LABEL ;set breakpoint set flag, not quite right yet...(FFFF) bcf QB_FLAGS,INVALID_BP ;MOVLF high LINEBUF,FSR0H ;pointed to by FSR0 ;MOVLF low LINEBUF,FSR0L lfsr 0,LINEBUF ;MOVLF high BIGBUG,FSR1H ;MOVLF low BIGBUG,FSR1L lfsr 1,BIGBUG ;lower nibble of ICKBUG has to be cleared. bsf DEBUG,SHDW movlw 0xF0 andwf ICKBUG,F MOVLF H'2',COUNT REPEAT_ ;FSR0 already points to correct position call ASC2toBinary movff HEXBYTE,POSTDEC1 movf POSTINC0,W decf COUNT,F UNTIL_ .Z. ;now must shift right one (divide by 2) bcf STATUS,C rrcf BIGBUG,F rrcf GLOBUG,F bcf DEBUG,SHDW return ;;;;;;;DisplayBreakPoint subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine first displays an installed breakpoint address (if there is one). ; Then it offers a menu. DisplayBreakPoint ;if FFFF, don't have to times by 2, ;but if between (0000-7FFE)/2, then must times by 2 first before display bsf DEBUG,SHDW movlw H'0F' ;check lower nibble of ICKBUG andwf ICKBUG,W xorlw H'0F' IF_ .NZ. ;Display Breakpoint POINT sBreakpoint call StringDisplay ;Breakpoint = movlw A'0' call TXbyte movlw A'x' call TXbyte bcf STATUS,C ;Double BKA to form address rlcf GLOBUG,F rlcf BIGBUG,F movf BIGBUG,W call DisplayHexByte movf GLOBUG,W call DisplayHexByte bcf STATUS,C rrcf BIGBUG,F rrcf GLOBUG,F call CR_LF ;send CR ELSE_ POINT sNoBreakpointSet call StringDisplay ENDIF_ bcf DEBUG,SHDW return ;;;;;;;;;;;;;;;;;;;;;;;;;;; KEYACT6 SUBROUTINES ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;; GetWatchInput subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine gets input for watch variables. It determines whether the ; first character was a C H B S R or Q and then calls the appropriate ; subroutine using ; FSR0 to access LINEBUF ; FSR1 to access WATCHARRAY GetWatchInput LOOP_ POINT sF6 call StringDisplay call GetCommand IF_ QB_FLAGS2,FNKEYS == 0 ;ParseCommand cannot detect function keys, must check flag! ;MOVLF high LINEBUF,FSR0H ;pointed to by FSR0 ;MOVLF low LINEBUF,FSR0L lfsr 0,LINEBUF movf INDF0,F IF_ .NZ. ;if 0x00 at 1st position, do not show invalid command call ParseCommand IF_ WATCH_FLAG,INVALID_COMMAND == 0 call UpdateWatchArray BREAK_ ELSE_ POINT sInvalidCommand call StringDisplay ENDIF_ ;end if INVALID_COMMAND ELSE_ ;else 0x00 at 1st position BREAK_ ENDIF_ ;end if not 0x00 at 1st position ELSE_ call CR_LF POINT sInvalidCommand call StringDisplay ENDIF_ ;end if funtion keys ENDLOOP_ return UpdateWatchArray ;Break on following situation: ;ran out of WatchDepth, display watch array full message ;ran into an invalid command flag, update that location w/ WATCH_TEMP ;ran into same address, if WATCH_TEMP2 = 0, delete the element, else just update w/ WATCH_TEMP2 MOVLF WatchDepth,COUNT ;MOVLF high WATCHARRAY,FSR1H ;Point FSR1 to WATCHARRAY ;MOVLF low WATCHARRAY,FSR1L lfsr 1,WATCHARRAY LOOP_ ;at this point, INDF1 should point to the first byte of the 3 bytes watch variables IF_ INDF1,INVALID_COMMAND == 1 ;add new watch variable movff WATCH_TEMP2,POSTINC1 movff WATCH_TEMP1,POSTINC1 movff WATCH_TEMP0,INDF1 BREAK_ ENDIF_ ;check address movf PREINC1,W ;point to second byte xorwf WATCH_TEMP1,W IF_ .Z. ;if zero, first part of address matches movf PREINC1,W xorwf WATCH_TEMP0,W IF_ .Z. ;if zero, second part of address matches, now check options movf WATCH_TEMP2,F IF_ .Z. ;if zero, default mode, delete this element DEC1 DEC1 ;change pointer 1 to points at first byte of variable that has address match movff FSR1H,FSR0H movff FSR1L,FSR0L INC0 INC0 INC0 ;now FSR0 points to 3 bytes ahead ;check COUNT value... LOOP_ decf COUNT,F IF_ .Z. bsf INDF1,INVALID_COMMAND BREAK_ ENDIF_ IF_ INDF0,INVALID_COMMAND == 0 movff POSTINC0,POSTINC1 movff POSTINC0,POSTINC1 movff POSTINC0,POSTINC1 ELSE_ bsf INDF1,INVALID_COMMAND BREAK_ ENDIF_ ENDLOOP_ ELSE_ ;else, update the option byte DEC1 DEC1 movff WATCH_TEMP2,INDF1 ENDIF_ BREAK_ ;quit loop in either case ENDIF_ ELSE_ ;2nd byte doesn't match WATCH_TEMP1 INC1 ;point to third byte ENDIF_ decf COUNT,F IF_ .Z. POINT sWatchArrayFull call StringDisplay BREAK_ ELSE_ INC1 ;adjust FSR1 to point to correct element. ENDIF_ ENDLOOP_ return ParseCommand clrf WATCH_FLAG clrf WATCH_TEMP0 ;lower 2 bytes of address clrf WATCH_TEMP1 ;upper byte of address clrf WATCH_TEMP2 ;options flag ;MOVLF high LINEBUF,FSR0H ;pointed to by FSR0 ;MOVLF low LINEBUF,FSR0L lfsr 0,LINEBUF MOVLF 0x03,COUNT ;check if address is valid bsf WATCH_FLAG,VALID_HEX ;assume all valid REPEAT_ ;update TEMP1 and TEMP0, no matter if valid or not.. ;first copy top nibble of TEMP0 to low nibble of TEMP1, ;then swap nibble in TEMP0 (this copy lower nibble of TEMP0 to high nibble) ;then write new value to lower nibble of TEMP0 swapf WATCH_TEMP0,W andlw 0x0F ;mask out top nibble movwf WATCH_TEMP1 ;write to WATCH_TEMP1 swapf WATCH_TEMP0,W andlw 0xF0 ;mask out lower nibble movwf WATCH_TEMP0 ;now lower nibble is 0, write to WATCH_TEMP0 movf INDF0,W ;load new ASCII (if ascii at all) call ASCtoBinary ;binary value is returned in WREG iorwf WATCH_TEMP0,F ;write new ASCII value to lower nibble of WATCH_TEMP0 IF_ WATCH_FLAG,VALID_HEX == 1 movf POSTINC0,W call CheckHexDigit ;if one is invalid, VALID_HEX will be set to 0 ENDIF_ decf COUNT,F UNTIL_ .Z. ;now check if ADDRESS is in valid bank (valid only bank0 to (max bank-1) and bankF) ;adequate to just check WATCH_TEMP1 , check only if VALID_HEX = 1 ;if not in a valid bank, VALID_HEX is set to 0 IF_ WATCH_FLAG,VALID_HEX == 1 movlw Debug_Bank+1 subwf WATCH_TEMP1,W ;WATCH_TEMP1-W = W IF_ .NN. ;if not negative Debug_Bank <= TEMP1 movf WATCH_TEMP1,W xorlw 0x0F IF_ .NZ. ;if not zero, not BANK F, and therefore invalid bcf WATCH_FLAG,VALID_HEX ENDIF_ ENDIF_ ENDIF_ ;at this point current pointer is pointing to 4th position (could be 0x00) if VALID_HEX = 1 IF_ WATCH_FLAG,VALID_HEX == 1 ;if valid hex, store in WTEMP1 and WTEMP0, then check for others. ;loop until 0x00, look for 'h','a','b','-',#1-#9, ignore spaces. REPEAT_ ;check current ascii movf INDF0,F ;if 0x00 , break IF_ .Z. BREAK_ ENDIF_ WATCHCOMMAND ' ' IF_ .NZ. ;if not a space, keep checking WATCHCOMMAND 'a' IF_ .Z. ;set ASCII mode ;if mode set (not 00), invalid command movlw RADIX_BITS andwf WATCH_TEMP2,W IF_ .Z. ;if zero, radix=00,and can be set. movlw RADIX_BITS andlw ASCII_RADIX iorwf WATCH_TEMP2,F ELSE_ bsf WATCH_FLAG,INVALID_COMMAND ENDIF_ ELSE_ WATCHCOMMAND 'b' ;set BINARY mode IF_ .Z. movlw RADIX_BITS andwf WATCH_TEMP2,W IF_ .Z. ;if zero, radix=00,and can be set. movf WATCH_TEMP2,W andlw LENGTH_BITS IF_ .Z. ;if zero, length = 0, ok to set to BIN mode IF_ WATCH_TEMP2,REVERSE_BIT == 0 ;cannot do reverse in binary mode movlw RADIX_BITS andlw BIN_RADIX iorwf WATCH_TEMP2,F ELSE_ bsf WATCH_FLAG,INVALID_COMMAND ENDIF_ ELSE_ bsf WATCH_FLAG,INVALID_COMMAND ENDIF_ ELSE_ bsf WATCH_FLAG,INVALID_COMMAND ENDIF_ ELSE_ WATCHCOMMAND 'h' ;set HEX mode IF_ .Z. movlw RADIX_BITS andwf WATCH_TEMP2,W IF_ .Z. ;if zero, radix=00,and can be set. movlw RADIX_BITS andlw HEX_RADIX iorwf WATCH_TEMP2,F ELSE_ bsf WATCH_FLAG,INVALID_COMMAND ENDIF_ ELSE_ WATCHCOMMAND '-' ;set reverse order mode IF_ .Z. IF_ WATCH_TEMP2,REVERSE_BIT == 0 movf WATCH_TEMP2,W andlw RADIX_BITS xorlw BIN_RADIX IF_ .NZ. ;can't have both reverse and binary bsf WATCH_TEMP2,REVERSE_BIT ELSE_ bsf WATCH_FLAG,INVALID_COMMAND ENDIF_ ELSE_ ;more then one '-' bsf WATCH_FLAG,INVALID_COMMAND ENDIF_ ELSE_ movf WATCH_TEMP2,W andlw LENGTH_BITS IF_ .Z. ;check if more than one digit of length, if yes->invalid call UpdateLengthValue ;if invalid value, invalid bit will be set ELSE_ bsf WATCH_FLAG,INVALID_COMMAND ENDIF_ ENDIF_ ENDIF_ ENDIF_ ENDIF_ ENDIF_ ;if running into any invalid, BREAK... IF_ WATCH_FLAG,INVALID_COMMAND == 1 BREAK_ ;exit repeat loop ENDIF_ movf PREINC0,F ;LINEBUF always have a null 0x00 UNTIL_ .Z. ;if content is 0x00 (null) then quit ELSE_ bsf WATCH_FLAG,INVALID_COMMAND ENDIF_ ;If length == 0, and reverse bit is set, invalid command! (at this point, it can be either 1 or 0) IF_ WATCH_FLAG,INVALID_COMMAND == 0 movf WATCH_TEMP2,W andlw LENGTH_BITS IF_ .Z. IF_ WATCH_TEMP2,REVERSE_BIT == 1 bsf WATCH_FLAG,INVALID_COMMAND ENDIF_ ENDIF_ ENDIF_ return ;UpdateLengthValue makes sure that only single digit value between 2-9 is accepted. ;Also check if binary mode has been set, if set, invalid command. UpdateLengthValue ;valid for ascii 2-9 only (0x32-0x39) movf INDF0,W iorlw B'00100000' ;Convert to lower case sublw A'2'-1 ;0x30 - W = W IF_ .N. ;if negative 0x32 <= W addlw 0x09 ;W + 0x09 = W IF_ .NN. ;if not negative 0x32 <= W <= 0x39 movf WATCH_TEMP2,W andlw RADIX_BITS xorlw BIN_RADIX ;if also binary mode, invalid command IF_ .Z. bsf WATCH_FLAG,INVALID_COMMAND ELSE_ ;else, the current value of length is 0 movf INDF0,W ;write length value andlw 0x0F ;mask out top nibble iorwf WATCH_TEMP2,F ENDIF_ ELSE_ bsf WATCH_FLAG,INVALID_COMMAND ENDIF_ ELSE_ bsf WATCH_FLAG,INVALID_COMMAND ENDIF_ return ;CheckHexDigit makes sure that value in WREG is a valid ASCII character. CheckHexDigit ;valid if ASCII value: (30-39),(61-66)[lowercase] bcf WATCH_FLAG,VALID_HEX iorlw B'00100000' ;Convert to lower case movwf TEMP1 ;store converted value sublw A'0'-1 ;0x2F-TEMP1 = W IF_ .N. ;if negative 0x30 <= TEMP1 addlw 0x0A ;W + 0x0A = W IF_ .NN. ;if not negative 0x30 <= TEMP1 <= 0x39 bsf WATCH_FLAG,VALID_HEX ELSE_ ;if negative TEMP1 > 0x39 movf TEMP1,W sublw A'a'-1 ;0x60-TEMP1 = W IF_ .N. ;if negative 0x61 <= TEMP1 addlw 0x06 ;W + 0x06 = W IF_ .NN. ;if not negative 0x61 <= TEMP1 <= 0x66 bsf WATCH_FLAG,VALID_HEX ENDIF_ ENDIF_ ENDIF_ ENDIF_ return ;;;;;;; WatchClear subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine clears all two-byte elements in WATCHARRAY to 0x00. ; WatchClear MOVLF high WATCHARRAY,FSR1H ;Set up pointer MOVLF low WATCHARRAY,FSR1L MOVLF WatchDepth,TEMP1 ;Set counter to WatchDepth Clear_Loop REPEAT_ movlw 0x80 ;MSB must be set (signify invalid) movwf POSTINC1 ;Clear three-byte element clrf POSTINC1 clrf POSTINC1 decf TEMP1,F UNTIL_ .Z. return ;;;;;;;;;;;;;;;;;;;;;;;;;; DISPLAY WATCH VARIABLES ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;; subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine displays labels for the variables that will be displayed by ; the DisplayWatchData subroutine. ; Uses FSR1 as a pointer into WATCHARRAY. DisplayWatchLabels POINT sWatchVarNames ;Display prompt call StringDisplay rcall VarLabels return ;;;;;; VarLabels subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine displays a label for each entry in WATCHARRAY. ;ok, LOOP_, check WatchDepth and INVALID_COMMAND FLAG, either case, quit. ;else, display address. 3 digit hex... ;first convert value is 2nd byte to ascii, call txbyte to display it ;display second address byte, using DisplayHexByte. ;now that 3 digits space... ;check if reverse mode, if yes, display '-', else a space ' ' ;now...check mode. ;if binary, insert 5 empty spaces (3+1+4+1 = 9).., no need to check length, last space is for empty space for next variable ;if ascii, grab length, subtract by 4+1, if .NN. add 2 to WREG, else WREG =1 ;else,must be hex, grab length, subtract by 3, if .NN. add 1 to WREG else WREG = 1 endif, then times two and add one. ;pattern for ascii ;length spaces needed ID ;0-2 1 0 ;3 3 1 ;4 5 2 ;5 7 3 ;(ID*2)+1 = spaces needed VarLabels lfsr 1,WATCHARRAY MOVLF WatchDepth, COUNT LOOP_ IF_ INDF1,INVALID_COMMAND == 1 BREAK_ ELSE_ movff INDF1,WATCH_TEMP2 ;save options byte for future checking (don't have to decrement INDF) movf PREINC1,W call BinarytoASC ;Convert lower nibble to ASCII call TXbyte ;display first hex digit of address movf PREINC1,W call DisplayHexByte ;display address 0xAAA IF_ WATCH_TEMP2,REVERSE_BIT == 1 movlw A'-' ELSE_ movlw A' ' ENDIF_ call TXbyte movf WATCH_TEMP2,W ;check radix mode andlw RADIX_BITS xorlw ASCII_RADIX IF_ .Z. ;ascii mode movf WATCH_TEMP2,W andlw LENGTH_BITS addlw -0x05 ;WREG = WREG - 5 IF_ .NN. addlw 0x02 ELSE_ movlw 0x01 ENDIF_ ELSE_ movf WATCH_TEMP2,W andlw RADIX_BITS xorlw BIN_RADIX IF_ .Z. ;bin mode movlw 0x05 ELSE_ ;else, must be hex mode movf WATCH_TEMP2,W andlw LENGTH_BITS addlw -0x03 IF_ .NN. addlw 0x01 ELSE_ movlw 0x00 ENDIF_ rlncf WREG,F addlw 0x01 ENDIF_ ENDIF_ call DisplaySpaces ;display the number of spaces in WREG ENDIF_ decf COUNT,F IF_ .Z. BREAK_ ELSE_ INC1 ;set up pointer for next Watch Variable ENDIF_ ENDLOOP_ call CR_LF ;send out CR return ;;;;;;; DisplayWatchData subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine displays PC, WREG, STATUS, FSR0, and any added variables. ; Uses FSR1 as a pointer into WATCHARRAY. ; Uses FSR0 to hold the address of the watch variable. DisplayWatchData movf PCH_TEMP,W rcall DisplayHexByte ;Display upper byte of PC movf PCL_TEMP,W rcall DisplayHexByte ;Display lower byte movlw 1 rcall DisplaySpaces movf WREG_TEMP,W ;Display W data rcall DisplayHexByte movlw 1 rcall DisplaySpaces IF_ STATUS_TEMP,2 == 1 ;Display Z movlw A'1' ELSE_ movlw A'0' ENDIF_ call TXbyte movlw A' ' ;Space call TXbyte IF_ STATUS_TEMP,0 == 1 ;Display C movlw A'1' ELSE_ movlw A'0' ENDIF_ call TXbyte movlw A' ' call TXbyte IF_ STATUS_TEMP,4 == 1 ;Display N movlw A'1' ELSE_ movlw A'0' ENDIF_ call TXbyte movlw 1 rcall DisplaySpaces movf FSR0H_TEMP,W ;Display FSR0[11:8] rcall DisplayHexNibble movf FSR0L_TEMP,W ;Display FSR0[[7:0] rcall DisplayHexByte movlw 2 rcall DisplaySpaces movf FSR1H_TEMP,W ;Display FSR1[11:8] rcall DisplayHexNibble movf FSR1L_TEMP,W ;Display FSR1[[7:0] rcall DisplayHexByte movlw 2 rcall DisplaySpaces movf FSR2H_TEMP,W ;Display FSR2[11:8] rcall DisplayHexNibble movf FSR2L_TEMP,W ;Display FSR2[[7:0] rcall DisplayHexByte movlw 2 rcall DisplaySpaces call DisplayUserWatchData call CR_LF return ;DisplayUserWatchData load the address in WATCH_TEMP1,WATCH_TEMP0 into FSR1. ;And shows the content of that memory address according to the option specified ;in WATCH_TEMP2 variable. DisplayUserWatchData lfsr 1,WATCHARRAY ;Set up pointer into WATCHARRAY MOVLF WatchDepth,COUNT LOOP_ IF_ INDF1,INVALID_COMMAND == 1 BREAK_ ELSE_ ;else, display according to radix,length, and reverse option movff INDF1,WATCH_TEMP2 ;save options byte for future checking (don't have to decrement INDF) movff PREINC1,FSR0H ;load FSR0 with address from WATCHARRAY movff PREINC1,FSR0L movf WATCH_TEMP2,W ;if length == 0, add 1 andlw LENGTH_BITS IF_ .Z. incf WATCH_TEMP2,F ;should not affect the upper nibble, lower was 0, add 1, becomes 1. ENDIF_ movlw 0x05 ;temp var to keep track of the number of spaces to display movwf TEMP1 ;see subroutine DisplayRadix, TEMP1 is modified there IF_ WATCH_TEMP2,REVERSE_BIT == 0 ;increment mode REPEAT_ call DisplayRadix INC0 decf WATCH_TEMP2,F movf WATCH_TEMP2,W andlw LENGTH_BITS UNTIL_ .Z. ELSE_ ;decrement mode ;ParseCommand guarantees that if reverse bit is set, length is at least 2 or more. ;if reverse bit set, increment FSR0 'length' times first.. movf WATCH_TEMP2,W andlw LENGTH_BITS addlw -0x01 ;decrement by 1 first. addwf FSR0L,F IF_ .C. incf FSR0H,F ENDIF_ REPEAT_ call DisplayRadix DEC0 decf WATCH_TEMP2,F movf WATCH_TEMP2,W andlw LENGTH_BITS UNTIL_ .Z. ENDIF_ ;end if reverse = 0 or =1 ;now output correct number of spaces movf TEMP1,W call DisplaySpaces ENDIF_ ;end if invalid command decf COUNT,F IF_ .Z. BREAK_ ELSE_ INC1 ;set up pointer for next Watch Variable ENDIF_ ENDLOOP_ return ;display the value in INDF0, in radix format specified in WATCH_TEMP2 ;Also updates TEMP1 varible, which keeps track of the number of spaces ;to be displayed after this WATCHARRAY element. DisplayRadix movf WATCH_TEMP2,W ;check radix mode andlw RADIX_BITS xorlw ASCII_RADIX IF_ .Z. ;ascii mode movf INDF0,W call CheckValidAscii call TXbyte movf TEMP1,W addlw -0x01 IF_ .NZ. decf TEMP1,F ENDIF_ ELSE_ movf WATCH_TEMP2,W andlw RADIX_BITS xorlw BIN_RADIX IF_ .Z. ;bin mode movf INDF0,W call DisplayBinaryByte movlw 0x01 movwf TEMP1 ELSE_ ;else, must be hex mode movf INDF0,W call DisplayHexByte movf TEMP1,W xorlw 0x05 IF_ .Z. movlw 0x03 ELSE_ movlw 0x01 ENDIF_ movwf TEMP1 ENDIF_ ENDIF_ return ;;;;;;; DisplayHexNibble subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine displays the lower nibble in WREG as one hex character. ; It sends the ASCII representation of the byte to the PC. DisplayHexNibble rcall BinarytoASC ;Convert lower nibble to ASCII call TXbyte ; and display it return ;;;;;;; DisplayHexByte subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine displays the byte in WREG as two hex characters. ; It sends the ASCII representation of the byte to the PC. DisplayHexByte movwf TEMP0 ;Set aside lower nibble swapf TEMP0,W ;Deal first with upper nibble, moved to bits 3..0 rcall BinarytoASC ;Convert lower nibble to ASCII call TXbyte ; and display it movf TEMP0,W ;Get lower nibble rcall BinarytoASC ; and convert it call TXbyte ; and display it return ;;;;;;; DisplayBinaryByte subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine displays the byte in WREG as eight bits. ; It sends the ASCII representation of the byte to the PC. DisplayBinaryByte movwf TEMP0 ;Set aside byte MOVLF 8,COUNT1 ;Count eight bits REPEAT_ rlcf TEMP0,F ;Get bits MSb first and send them one by one IF_ .C. movlw A'1' ELSE_ movlw A'0' ENDIF_ call TXbyte decf COUNT1,F UNTIL_ .Z. return ;;;;;;; CheckValidAscii subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine looks at the character in W. If it is not in the range ; from 0x20 to 0x7f, the character is changed to 0x2a (asterisk). Otherwise it ; it is returned unchanged. CheckValidAscii movwf TEMP0 ;Set aside value addlw -0x7f ;Carry will be set for WREG >= 0x7f IF_ .C. ;Out of range (high) movlw A'*' ELSE_ movf TEMP0,W addlw -0x20 ;Carry will be set for WREG >= 0x20 IF_ .NC. ;Out of range (low) movlw A'*' ELSE_ movf TEMP0,W ENDIF_ ENDIF_ return ;;;;;;; DisplaySpaces subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine sends WREG space characters to the PC. DisplaySpaces movwf TEMP0 movlw A' ' ;Get space character REPEAT_ call TXbyte ;Send space character to PC decf TEMP0,F UNTIL_ .Z. return ;;;;;;;;;;;;;;;;;;;;;;;;;; KEYACT9 SUBROUTINES ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;; Modify subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine carries out the F9 function key action. ; It expects input of the following form in LINEBUF, the line buffer. ; hhh to view the contents of address hhh ; hhh dd to write dd to address hhh ; where is the end-of-string code, 0x00. ; ; FSR0 is used as a pointer into the LINEBUF line buffer. Modify POINT sDisMod ;Display display/modify message call StringDisplay call GetCommand ;Load LINEBUF lfsr 0,LINEBUF ;Point to beginning of LINEBUF movf INDF0,W ;Get first character rcall ASCtoBinary ;Convert to binary movwf BNK ;Put value of first ASCII character in BNK movf PREINC0,W ;Get next character call ASC2toBinary ;Convert 2 ascii digits to a hex byte movff HEXBYTE,TRAM ;Save address to TRAM movf PREINC0,W ;Check for end-of-string designator IF_ .Z. call DisplayRAMorRegister ELSE_ call ModifyRAMorRegister ENDIF_ return ;;;;;;; DisplayRAMorRegister subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine displays the contents of the address entered in response ; to the F9 key. ; FSR1 is used as a pointer to the RAM/register address to be displayed. DisplayRAMorRegister POINT sContents ;Display RAM or register contents message call StringDisplay movff BNK, FSR1H ;Move hex address to FSR0 movff TRAM, FSR1L swapf INDF1,W ;Convert and display upper nibble rcall BinarytoASC call TXbyte movf INDF1,W ;Convert and display lower nibble rcall BinarytoASC call TXbyte call CR_LF ;New line return ;;;;;;; BinarytoASC subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine converts the lower nibble (0 to 15) in WREG to ASCII in WREG. BinarytoASC andlw B'00001111' ;Remove upper nibble addlw -10 ;Set carry for 10 to 15 IF_ .NC. ;Fix numbers addlw 10+A'0' ELSE_ ;Fix letters addlw A'A' ENDIF_ return ;;;;;;; ModifyRAMorRegister subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; Modify contents of locations starting at TRAM. ; FSR0 is a pointer into LINEBUF to pick up the new value. ; FSR1 is a pointer to the RAM/register address to be changed. ModifyRAMorRegister movff BNK,FSR1H ;Set up address to be changed movff TRAM,FSR1L movf PREINC0,W ;Get first of two ASCII characters call ASC2toBinary ;Form HEXBYTE from the two ASCII characters movff HEXBYTE,INDF1 ;Update the value pointed to by FSR1 return ;;;;;;; ASC2toBinary subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine converts a two-character string of ASCII-coded hex digits ; pointed to by FSR0 into a one byte number in HEXBYTE. ; It increments FSR0 once, leaving FSR0 pointing to the second character. ASC2toBinary movf INDF0,W ;Get first ASCII-coded hex digit rcall ASCtoBinary ;Convert to binary movwf HEXBYTE ;Save nibble swapf HEXBYTE,F ;Move nibble to high position movf PREINC0,W ;Get second ASCII-coded hex digit rcall ASCtoBinary ;Convert to binary iorwf HEXBYTE,F ;add low nibble to high nibble movf HEXBYTE,W ;put result in W reg return ;;;;;;; ASCtoBinary subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine takes the ASCII-coded digit in WREG ; and converts it to its numeric value in WREG. ASCtoBinary iorlw B'00100000' ;Convert to lower case addlw 0x100-A'a' ;Add -'a' (i.e., 159) to ASCII IF_ .C. ;If 'a' to 'f' addlw 10 ELSE_ ;If '0' to '9' addlw 49 ENDIF_ return ShowCurrentStack decf STKPTR,F ;ingore top stack, call to ShowCurrentStack movlw A'\r' call TXbyte movlw A'\n' call TXbyte movf STKPTR,W call DisplayHexByte movlw A':' call TXbyte movf TOSH,W call DisplayHexByte movf TOSL,W call DisplayHexByte movlw A'\r' call TXbyte movlw A'\n' call TXbyte incf STKPTR,F return ;;;;;;; Messages ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; placeholder db "a" sQwikBug db "\r\n\t\t\tQwikBug for PIC18F4520 v1.0\r\nx00" sFnKeyMenu db "\r\n","F1\tF2\tF3\tF4\tF5\tF6\tF7\tF8\tF9\r\n","Help\treseT\tLoad\tDisplay\tBreak\tWatch\tRun\tStep\tModify\r\n\x00" sDisMod db "Display/Modify contents of RAM or register address: \r\n to Display , to Modify\r\n\x00" sPrompt db "QB> \x00" sshortPrompt db "QB>\x00" sContents db "Contents (Hex): \x00" sEraseDone db "Erase Complete...\x00" sStartDL db "User program memory erased - Send HEX file now\r\n\x00" sRunning db "Running...\r\n\x00" sSuccess db "\r\nDownload successful\r\n\\r\nx00" sTError db "\r\nTransmit Error - Checksum\r\n\x00" sLargeCode db "\r\nWriting outside User Address Space\r\n\x00" sNoUserVector db "\r\nNo user code reset vector is loaded\r\n\x00" sNoUserCode db "No User Code Loaded\r\n\x00" sWatchMenu db "Enter watch variable. Examples: | 012 | 012 b | 012 a 8 | 012 2 | 012 2 - |\r\n\x00" sWatchAccess db "Must be an Access Bank address\r\n\x00" sWatchVarNames db " PC W Z C N FSR0 FSR1 FSR2 \x00" sF6 db " F6>\x00" sDelay db "\t\tQwikBug Autostarting User Code\r\n\x00" sAbort db "\t\tPress any key to abort \x00" sFKey db 0x00,0x01,0x02,0x03,0x04,0x05,0x00,0x06,0x07,0x08,0x09,0x0A,0x00,0x0B,0x0C ;new messages sF5 db " F5> \x00" sBP db " BP> \x00" sBreakpoint db " Breakpoint = " sNoBreakpointSet db " No breakpoint is set\r\n\x00" sBreakPtMenu db " to set breakpoint, 'R' to remove breakpoint, 'D' to display breakpoint\r\n\x00" sReset db "Reset Complete. \r\n\x00" sF10 db "F10 \r\n\x00" sF11 db "F11 \r\n\x00" sF12 db "F12:Enter High Address(2 hex digits)> \x00" sBOR db "Brown-out Reset Detected\r\n\x00" sSTKFUL db "Stack Overflow\r\n\x00" sSTKUNF db "Stack Underflow\r\n\x00" sInvalidCommand db " F6>Invalid Command\r\n\x00" sWatchArrayFull db " F6>Watch Array Full\r\n\x00" sHighAddress db "Highest Address Programmed: 0x\x00" sTitles db " 0 1 2 3 4 5 6 7 8 9 A B C D E F\r\n\x00" Done equ $ end