;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; 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 btfss INTCON,GIEH bra L1 bcf INTCON,GIEH ;if set, disable it. bsf REGISTER_TEMP,GIEH ;ELSE_ bra L2 L1 bcf REGISTER_TEMP,GIEH ;ENDIF_ L2 SAVEREGISTERS ;save registers ;IF_ T0CON,TMR0ON == 1 btfss T0CON,TMR0ON bra L3 bcf T0CON,TMR0ON bsf REGISTER_TEMP,TMR0_ON ;ELSE_ bra L4 L3 bcf REGISTER_TEMP,TMR0_ON ;ENDIF_ L4 ;IF_ T1CON,TMR1ON == 1 btfss T1CON,TMR1ON bra L5 bcf T1CON,TMR1ON bsf REGISTER_TEMP,TMR1_ON ;ELSE_ bra L6 L5 bcf REGISTER_TEMP,TMR1_ON ;ENDIF_ L6 ;IF_ T2CON,TMR2ON == 1 btfss T2CON,TMR2ON bra L7 bcf T2CON,TMR2ON bsf REGISTER_TEMP,TMR2_ON ;ELSE_ bra L8 L7 bcf REGISTER_TEMP,TMR2_ON ;ENDIF_ L8 ;IF_ T3CON,TMR3ON == 1 btfss T3CON,TMR3ON bra L9 bcf T3CON,TMR3ON bsf REGISTER_TEMP,TMR3_ON ;ELSE_ bra L10 L9 bcf REGISTER_TEMP,TMR3_ON ;ENDIF_ L10 bcf DEBUG,FREEZ ;unfreeze peripherals (all timers have been turned off) call SerialSetup ;IF_ QB_FLAGS2,LOAD_RESET == 0 btfsc QB_FLAGS2,LOAD_RESET bra L11 clrf MONITORSTATE ;Clear BDM flags ;ENDIF_ L11 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. bz L12 ;following should be called by any reset ;IF_ QB_FLAGS2,LOAD_RESET == 0 btfsc QB_FLAGS2,LOAD_RESET bra L13 clrf QB_FLAGS2 ;Initialize QB_FLAGS2 call WatchClear ;Initialize WATCHARRAY call BreakPoint_Clear ;clear breakpoint ;ELSE_ bra L14 L13 bcf QB_FLAGS2,LOAD_RESET ;ENDIF_ L14 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+ btfsc QB_FLAGS,POR bra L15 POINT sQwikBug ;Display menu call StringDisplay POINT sFnKeyMenu call StringDisplay call AutoStart call MonitorFlags ;ENDIF_ L15 ;ENDIF_ L12 ;next section cannot be in a subroutine, don't move it! ;IF_ QB_FLAGS,FROM_USER == 1 btfss QB_FLAGS,FROM_USER bra L16 ;IF_ DEBUG,SSTEP == 1 btfss DEBUG,SSTEP bra L17 movff PCH_TEMP_NEXT,PCH_TEMP movff PCL_TEMP_NEXT,PCL_TEMP ;ELSE_ bra L18 L17 ;IF_ QB_FLAGS,INVALID_BP == 1 btfss QB_FLAGS,INVALID_BP bra L19 call CheckForUserCode ;IF_ MONITORSTATE,4 == 0 btfsc MONITORSTATE,4 bra L20 bsf MONITORSTATE,1 ;Single Step User Code ;ENDIF_ L20 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_ L21 ;UNTIL_ PIR1,TXIF == 1 ;Wait for previous transfer to be completed btfss PIR1,TXIF bra L21 RL21 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_ L22 ;UNTIL_ PIR1,TXIF == 1 ;Wait for previous transfer to be completed btfss PIR1,TXIF bra L22 RL22 movwf TXREG ;Transmit byte movlw '>' ;REPEAT_ L23 ;UNTIL_ PIR1,TXIF == 1 ;Wait for previous transfer to be completed btfss PIR1,TXIF bra L23 RL23 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 bra L24 L19 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_ L24 ;ENDIF_ L18 ;ENDIF_ L16 movff TOSH,PCH_TEMP_NEXT ;store next PC movff TOSL,PCL_TEMP_NEXT ;IF_ QB_FLAGS,FROM_USER == 1 ;check if from user btfss QB_FLAGS,FROM_USER bra L25 bcf QB_FLAGS,FROM_USER ;clear flag ;IF_ QB_FLAGS,INVALID_BP == 1 ;if from user and not a breakpoint, btfss QB_FLAGS,INVALID_BP bra L26 ;IF_ DEBUG,SSTEP == 0 ;not a single step, btfsc DEBUG,SSTEP bra L27 ;IF_ PIR1,RCIF == 1 ;and a key was pressed btfss PIR1,RCIF bra L28 POINT sPrompt ;must be an interrupt key call StringDisplay ;show extra prompt for WatchLabels ;ENDIF_ L28 ;ENDIF_ L27 ;ENDIF_ L26 ;movlw ':' ;call TXbyte ;IF_ QB_FLAGS,SHOW_LABEL == 1 btfss QB_FLAGS,SHOW_LABEL bra L29 bcf QB_FLAGS,SHOW_LABEL call DisplayWatchLabels POINT sshortPrompt ;print prompt for WatchData call StringDisplay ;ENDIF_ L29 call DisplayWatchData ;always show WatchData if from user ;ENDIF_ L25 MainLoop ;LOOP_ L30 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_ bra L30 PL30 ;;;;;;; CheckResetCondition subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CheckResetCondition ;IF_ STKPTR,STKUNF == 1 ;detect stack underflow by checking STKFUL, purpose to show error only btfss STKPTR,STKUNF bra L31 bcf STKPTR,STKUNF POINT sSTKUNF call StringDisplay ;ENDIF_ L31 ;IF_ STKPTR,STKFUL == 1 ;detect stack overflow by checking STKUNF btfss STKPTR,STKFUL bra L32 bcf STKPTR,STKFUL POINT sSTKFUL call StringDisplay ;ENDIF_ L32 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 bnz L33 ;IF_ QB_FLAGS,FROM_USER == 1 ;from user(run or sstep), but by key interrupt? or MCLR? breakpoint? or SSTEP? btfss QB_FLAGS,FROM_USER bra L34 ;IF_ QB_FLAGS,INVALID_BP == 1 ;if not breakpoint, then check if it is a single step btfss QB_FLAGS,INVALID_BP bra L35 ;IF_ DEBUG,SSTEP == 0 ;if not SSTEP,then check if a key was pressed btfsc DEBUG,SSTEP bra L36 call LoopTime ;wait incase it is a key (fill up RCREG) ;IF_ PIR1,RCIF == 0 ;if no key pressed, must be MCLR btfsc PIR1,RCIF bra L37 bcf RCON,POR ;MCLR from user is equivalent to POR ;ENDIF_ L37 ;ENDIF_ L36 ;ENDIF_ L35 ;ELSE_ ;not from user, not a detectable reset; must be MCLR bra L38 L34 bcf RCON,POR ;MCLR from BDM is equivalent to POR ;ENDIF_ L38 ;ENDIF_ L33 ;IF_ RCON,POR == 0 ;Power-on Reset btfsc RCON,POR bra L39 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_ bra L40 L39 bsf QB_FLAGS,POR ;ENDIF_ L40 ;IF_ RCON,BOR == 0 ;Brown-out Reset btfsc RCON,BOR bra L41 bcf QB_FLAGS,BOR bsf RCON,BOR POINT sBOR call StringDisplay ;ELSE_ bra L42 L41 bsf QB_FLAGS,BOR ;ENDIF_ L42 ;IF_ RCON,RI == 0 ;Reset Instruction btfsc RCON,RI bra L43 bcf QB_FLAGS,RI bsf RCON,RI ;IF_ QB_FLAGS2,LOAD_RESET == 0 btfsc QB_FLAGS2,LOAD_RESET bra L44 POINT sReset call StringDisplay ;ENDIF_ L44 ;ELSE_ bra L45 L43 bsf QB_FLAGS,RI ;ENDIF_ L45 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_ L46 call TXbyte ;Send byte via UART tblrd+* ;Increment pointer movf TABLAT,W ;Load next byte ;UNTIL_ .Z. bnz L46 RL46 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_ L47 ;UNTIL_ PIR1,TXIF == 1 ;Wait for previous transfer to be completed btfss PIR1,TXIF bra L47 RL47 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 btfss RCSTA,OERR bra L48 bcf RCSTA, CREN ; then clear it bsf RCSTA, CREN ; and reenable receives ;ENDIF_ L48 ;REPEAT_ L49 ;UNTIL_ PIR1,RCIF == 1 ;Wait for byte to be received btfss PIR1,RCIF bra L49 RL49 movf RCREG,W ;Put received byte into W ;IF_ PIR1, RCIF == 1 btfss PIR1,RCIF bra L50 bcf RCSTA, SPEN ; then clear it bsf RCSTA, SPEN ; and reenable receives ;ENDIF_ L50 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 btfss PIR1,RCIF bra L51 ;REPEAT_ L52 movf RCREG,W bcf RCSTA, SPEN ; then clear it bsf RCSTA, SPEN ; and reenable receives ;UNTIL_ PIR1,RCIF == 0 btfsc PIR1,RCIF bra L52 RL52 ;ENDIF_ L51 ;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_ L53 tblrd*+ movf TABLAT,F ;Check for zero ;IF_ .NZ. ;If not zero, check next byte for 0xef bz L54 tblrd*+ movf TABLAT,W sublw 0xef ;IF_ .Z. bnz L55 bcf MONITORSTATE,4 ;Found "goto" instruction ;ENDIF_ L55 ;BREAK_ ;Done in either case bra RL53 ;ELSE_ bra L56 L54 tblrd*+ movf TABLAT,W sublw 0xef ;IF_ .Z. bnz L57 bcf MONITORSTATE,4 ;Found "goto" instruction ;BREAK_ ;Exit in this case bra RL53 ;ELSE_ bra L58 L57 movf TABLAT,W ;IF_ .NZ. bz L59 ;BREAK_ ;Not a "nop", so exit in this case also bra RL53 ;ENDIF_ L59 ;ENDIF_ L58 ;ENDIF_ L56 decf COUNT,F ;UNTIL_ .Z. bnz L53 RL53 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_ L60 tblrd*+ movf TABLAT,W xorlw 0xFF ;IF_ .NZ. bz L61 ;BREAK_ bra PL60 ;ENDIF_ L61 decf COUNT_L,F ;IF_ .B. bc L62 decf COUNT_H,F ;ENDIF_ L62 movf COUNT_L,F ;IF_ .Z. bnz L63 movf COUNT_H,F ;IF_ .Z. bnz L64 ;BREAK_ bra PL60 ;ENDIF_ L64 ;ENDIF_ L63 ;ENDLOOP_ bra L60 PL60 ;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. bnz L65 movf COUNT_L,F ;IF_ .NZ. bz L66 ;MOVLF 0x01,COUNT_H bcf MONITORSTATE,4 ;ENDIF_ L66 ;ELSE_ bra L67 L65 ;MOVLF 0x01,COUNT_H bcf MONITORSTATE,4 ;ENDIF_ L67 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 btfss MONITORSTATE,6 bra L68 call UserMemoryErase ;Erase user memory block POINT sTError ;Display message call StringDisplay ;ELSE_ bra L69 L68 ;IF_ MONITORSTATE,7 == 1 ;User code outside of User addr space btfss MONITORSTATE,7 bra L70 call UserMemoryErase ;Erase user memory block POINT sLargeCode ;Display message call StringDisplay ;ELSE_ bra L71 L70 ;IF_ MONITORSTATE,4 == 1 ;No user reset vector is loaded btfss MONITORSTATE,4 bra L72 POINT sNoUserVector ;Display message call StringDisplay ;ELSE_ bra L73 L72 ;IF_ MONITORSTATE,1 == 1 ;Single step btfss MONITORSTATE,1 bra L74 call SingleStepCode ;ELSE_ bra L75 L74 ;IF_ MONITORSTATE,0 == 1 ;run user code btfss MONITORSTATE,0 bra L76 call RunUserCode ;ENDIF_ L76 ;ENDIF_ L75 ;ENDIF_ L73 ;ENDIF_ L71 ;ENDIF_ L69 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 bz L77 incf STKPTR,F MOVLF H'00',TOSU MOVLF H'00',TOSH MOVLF H'00',TOSL ;ENDIF_ L77 bsf QB_FLAGS,FROM_USER bsf DEBUG,SSTEP bsf DEBUG,FREEZ ;freeze peripheral ;IF_ REGISTER_TEMP,TMR0_ON == 1 btfss REGISTER_TEMP,TMR0_ON bra L78 bsf T0CON,TMR0ON ;ENDIF_ L78 ;IF_ REGISTER_TEMP,TMR1_ON == 1 btfss REGISTER_TEMP,TMR1_ON bra L79 bsf T1CON,TMR1ON ;ENDIF_ L79 ;IF_ REGISTER_TEMP,TMR2_ON == 1 btfss REGISTER_TEMP,TMR2_ON bra L80 bsf T2CON,TMR2ON ;ENDIF_ L80 ;IF_ REGISTER_TEMP,TMR3_ON == 1 btfss REGISTER_TEMP,TMR3_ON bra L81 bsf T3CON,TMR3ON ;ENDIF_ L81 ;IF_ REGISTER_TEMP,GIEH == 1 btfss REGISTER_TEMP,GIEH bra L82 bsf INTCON,GIEH ;ENDIF_ L82 RESTREGISTERS tret ;this will restart the timers,etc. RunUserCode pop ;pop RunUserCode pop ;pop MonitorFlags ;IF_ QB_FLAGS,INVALID_BP == 1 btfss QB_FLAGS,INVALID_BP bra L83 POINT sRunning call StringDisplay ;ENDIF_ L83 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 bz L84 incf STKPTR,F MOVLF H'00',TOSU MOVLF H'00',TOSH MOVLF H'00',TOSL ;ENDIF_ L84 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 btfss REGISTER_TEMP,TMR0_ON bra L85 bsf T0CON,TMR0ON ;ENDIF_ L85 ;IF_ REGISTER_TEMP,TMR1_ON == 1 btfss REGISTER_TEMP,TMR1_ON bra L86 bsf T1CON,TMR1ON ;ENDIF_ L86 ;IF_ REGISTER_TEMP,TMR2_ON == 1 btfss REGISTER_TEMP,TMR2_ON bra L87 bsf T2CON,TMR2ON ;ENDIF_ L87 ;IF_ REGISTER_TEMP,TMR3_ON == 1 btfss REGISTER_TEMP,TMR3_ON bra L88 bsf T3CON,TMR3ON ;ENDIF_ L88 ;IF_ REGISTER_TEMP,GIEH == 1 btfss REGISTER_TEMP,GIEH bra L89 bsf INTCON,GIEH ;ENDIF_ L89 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_ L90 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 btfss QB_FLAGS2,FROM_MAINLINE bra L91 call CheckShortcut ;Check for shortcut keys movf FKEY,F ;Did we receive a function key (NZ=1)? ;IF_ .NZ. ;Yes; exit bz L92 ;BREAK_ bra RL90 ;ENDIF_ L92 ;ENDIF_ L91 movf INDF0,W call CheckFuncKeys ;Check for function keys movf FKEY,F ;Did we receive a function key (NZ=1)? ;IF_ .NZ. ;Yes; exit bz L93 bsf QB_FLAGS2,FNKEYS ;BREAK_ bra RL90 ;ELSE_ bra L94 L93 bcf QB_FLAGS2,FNKEYS ;ENDIF_ L94 movf INDF0,W ;Is it the enter key (CR)? xorlw 0x0D ;IF_ .Z. ;Yes bnz L95 MOVLF 0,INDF0 ;Null terminate the string rcall CR_LF ;Send CR, LF and exit ;BREAK_ bra RL90 ;ENDIF_ L95 movf INDF0,W ;Is it a backspace? xorlw 0x08 ;IF_ .Z. ;Yes bnz L96 movlw low LINEBUF ;Look for empty LINEBUF xorwf FSR0L,W ;IF_ .NZ. ;Backspace if not at beginning of the line bz L97 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_ L97 ;ELSE_ bra L98 L96 movf POSTINC0,W ;Echo received character call TXbyte decf COUNT1,F ;Decrement space remaining in LINEBUF ;IF_ .Z. bnz L99 MOVLF 0,INDF0 ;Null terminate the string rcall CR_LF ;and send CR, LF ;ENDIF_ L99 ;ENDIF_ L98 movf COUNT1,F ;Repeat until line buffer is full ;UNTIL_ .Z. bnz L90 RL90 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. bnz L100 MOVLF 10,FKEY ;ELSE_ bra L101 L100 xorlw 3 ;Original A'2' now sets Z=1 ;IF_ .Z. bnz L102 MOVLF 20,FKEY ;ENDIF_ L102 ;ENDIF_ L101 call RXbyte ;Get the next byte into WREG movf FKEY,F ;FKEY now contains 0, 10, or 20 ;IF_ .NZ. bz L103 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. bnc L104 incf TBLPTRH,F ;ENDIF_ L104 tblrd* movff TABLAT,FKEY ;ENDIF_ L103 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_ L105 ;REPEAT_ ;Inner loop takes 256x3x0.4 = 303 microsec L106 decf WREG,F ;UNTIL_ .Z. bnz L106 RL106 decf COUNT,F ;UNTIL_ .Z. ;Outer loop takes 303x32 = 9830 microsec bnz L105 RL105 return ;;;;;;; CheckShortcut subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CheckShortcut iorlw B'00100000' ;Convert to lower case movwf TEMP0 xorlw A'h' ;IF_ .Z. bnz L107 MOVLF 0x01,FKEY ;ENDIF_ L107 movf TEMP0,W xorlw A't' ;IF_ .Z. bnz L108 MOVLF 0x02,FKEY ;ENDIF_ L108 movf TEMP0,W xorlw A'l' ;IF_ .Z. bnz L109 MOVLF 0x03,FKEY ;ENDIF_ L109 movf TEMP0,W xorlw A'd' ;IF_ .Z. bnz L110 MOVLF 0x04,FKEY ;ENDIF_ L110 movf TEMP0,W xorlw A'b' ;IF_ .Z. bnz L111 MOVLF 0x05,FKEY ;ENDIF_ L111 movf TEMP0,W xorlw A'w' ;IF_ .Z. bnz L112 MOVLF 0x06,FKEY ;ENDIF_ L112 movf TEMP0,W xorlw A'r' ;IF_ .Z. bnz L113 MOVLF 0x07,FKEY ;ENDIF_ L113 movf TEMP0,W xorlw A's' ;IF_ .Z. bnz L114 MOVLF 0x08,FKEY ;ENDIF_ L114 movf TEMP0,W xorlw A'm' ;IF_ .Z. bnz L115 MOVLF 0x09,FKEY ;ENDIF_ L115 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 [ bnz L116 call RXbyte ;Get Next byte movwf INDF0 ;write to current position in INDF xorlw A'[' ;XOR WREG with '[' ;IF_ .Z. bnz L117 call RXbyte ;Get next byte movwf INDF0 ; write to current position in INDF0 call FkeyDecode ; F1-F12 -> 0x00-0x0E in FKEY ;ENDIF_ L117 ;ENDIF_ L116 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. bz L118 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_ L118 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. bnc L119 incf PCLATH,F ;ENDIF_ L119 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_ L120 call LoopTime decf TEMP1,F ;UNTIL_ .Z. bnz L120 RL120 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 btfsc MONITORSTATE,4 bra L121 bsf MONITORSTATE,0 ;Run User Code ;ENDIF_ L121 return KeyAct8 call CheckForUserCode ;Check if User Code is Loaded ;IF_ MONITORSTATE,4 == 0 btfsc MONITORSTATE,4 bra L122 bsf MONITORSTATE,1 ;Single Step User Code ;ENDIF_ L122 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_ L123 movlw D'16' xorwf COUNT1,W ;IF_ .Z. bnz L124 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_ L124 tblrd*+ ;movf TABLAT,W ;call DisplayHexByte ;IF_ TBLPTRL,0 == 1 btfss TBLPTRL,0 bra L125 movff TABLAT,TEMP1 ;ELSE_ bra L126 L125 movf TABLAT,W call DisplayHexByte movf TEMP1,W call DisplayHexByte ;ENDIF_ L126 ;IF_ TBLPTRL,0 == 0 btfsc TBLPTRL,0 bra L127 movlw A' ' call TXbyte ;ENDIF_ L127 decf COUNT1,F movlw 0x00 xorwf COUNT1,W ;IF_ .Z. bnz L128 movlw A'\r' call TXbyte movlw A'\n' call TXbyte MOVLF D'16',COUNT1 ;ENDIF_ L128 decf COUNT,F ;UNTIL_ .Z. bnz L123 RL123 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_ L129 ;REPEAT_ L130 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. bnz L130 RL130 movf FSR2H,F ;UNTIL_ .Z. bnz L129 RL129 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. bn L131 ;IF_ .Z. ;ADDRH = QwikBug(high), check ADDRL bnz L132 movlw low QwikBug_Trap subwf ADDRL,W ;IF_ .NN. ;ADDRL >= QwikBug(low) bn L133 bsf MONITORSTATE,7 ;set flag for error ;ENDIF_ L133 ;ELSE_ ;positive result bra L134 L132 bsf MONITORSTATE,7 ;ADDRH > QwikBug(high) ;ENDIF_ L134 ;ENDIF_ L131 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 btfss DEBUG,INBUG bra L135 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_ L135 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_ L136 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) btfsc QB_FLAGS2,FNKEYS bra L137 movf INDF0,F ;IF_ .NZ. ;if an enter key, do not show invalid command bz L138 INC0 movf INDF0,F ;IF_ .Z. ;if 2nd is 0x00 bnz L139 DEC0 WATCHCOMMAND 'r' ;IF_ .Z. bnz L140 call BreakPoint_Clear POINT sNoBreakpointSet call StringDisplay ;BREAK_ ;Quit loop if valid command bra PL136 ;ELSE_ bra L141 L140 WATCHCOMMAND 'd' ;IF_ .Z. bnz L142 call DisplayBreakPoint ;BREAK_ ;Quit loop if valid command bra PL136 ;ELSE_ ;neither 'r' or 'd' bra L143 L142 POINT sInvalidCommand call StringDisplay ;ENDIF_ L143 ;ENDIF_ L141 ;ELSE_ ;2nd not 0x00, see if valid bka bra L144 L139 DEC0 MOVLF 0x04,COUNT bsf WATCH_FLAG,VALID_HEX ;REPEAT_ L145 ;IF_ WATCH_FLAG,VALID_HEX == 1 btfss WATCH_FLAG,VALID_HEX bra L146 movf POSTINC0,W call CheckHexDigit ;ENDIF_ L146 decf COUNT,F ;UNTIL_ .Z. bnz L145 RL145 ;IF_ WATCH_FLAG,VALID_HEX == 1 btfss WATCH_FLAG,VALID_HEX bra L147 movf INDF0,F ;if valid_hex, INDF0 is at 5th position ;IF_ .Z. ;check for 0x00 bnz L148 call BreakPoint_Set call DisplayBreakPoint ;BREAK_ ;Quit loop if valid command bra PL136 ;ELSE_ bra L149 L148 POINT sInvalidCommand call StringDisplay ;ENDIF_ L149 ;ELSE_ bra L150 L147 POINT sInvalidCommand call StringDisplay ;ENDIF_ L150 ;ENDIF_ L144 ;ELSE_ ;else 0x00 at 1st position bra L151 L138 ;BREAK_ bra PL136 ;ENDIF_ ;end if not 0x00 at 1st position L151 ;ELSE_ bra L152 L137 call CR_LF POINT sInvalidCommand call StringDisplay ;ENDIF_ ;end if function key L152 ;ENDLOOP_ bra L136 PL136 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_ L153 ;FSR0 already points to correct position call ASC2toBinary movff HEXBYTE,POSTDEC1 movf POSTINC0,W decf COUNT,F ;UNTIL_ .Z. bnz L153 RL153 ;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 bz L154 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_ bra L155 L154 POINT sNoBreakpointSet call StringDisplay ;ENDIF_ L155 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_ L156 POINT sF6 call StringDisplay call GetCommand ;IF_ QB_FLAGS2,FNKEYS == 0 ;ParseCommand cannot detect function keys, must check flag! btfsc QB_FLAGS2,FNKEYS bra L157 ;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 bz L158 call ParseCommand ;IF_ WATCH_FLAG,INVALID_COMMAND == 0 btfsc WATCH_FLAG,INVALID_COMMAND bra L159 call UpdateWatchArray ;BREAK_ bra PL156 ;ELSE_ bra L160 L159 POINT sInvalidCommand call StringDisplay ;ENDIF_ ;end if INVALID_COMMAND L160 ;ELSE_ ;else 0x00 at 1st position bra L161 L158 ;BREAK_ bra PL156 ;ENDIF_ ;end if not 0x00 at 1st position L161 ;ELSE_ bra L162 L157 call CR_LF POINT sInvalidCommand call StringDisplay ;ENDIF_ ;end if funtion keys L162 ;ENDLOOP_ bra L156 PL156 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_ L163 ;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 btfss INDF1,INVALID_COMMAND bra L164 movff WATCH_TEMP2,POSTINC1 movff WATCH_TEMP1,POSTINC1 movff WATCH_TEMP0,INDF1 ;BREAK_ bra PL163 ;ENDIF_ L164 ;check address movf PREINC1,W ;point to second byte xorwf WATCH_TEMP1,W ;IF_ .Z. ;if zero, first part of address matches bnz L165 movf PREINC1,W xorwf WATCH_TEMP0,W ;IF_ .Z. ;if zero, second part of address matches, now check options bnz L166 movf WATCH_TEMP2,F ;IF_ .Z. ;if zero, default mode, delete this element bnz L167 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_ L168 decf COUNT,F ;IF_ .Z. bnz L169 bsf INDF1,INVALID_COMMAND ;BREAK_ bra PL168 ;ENDIF_ L169 ;IF_ INDF0,INVALID_COMMAND == 0 btfsc INDF0,INVALID_COMMAND bra L170 movff POSTINC0,POSTINC1 movff POSTINC0,POSTINC1 movff POSTINC0,POSTINC1 ;ELSE_ bra L171 L170 bsf INDF1,INVALID_COMMAND ;BREAK_ bra PL168 ;ENDIF_ L171 ;ENDLOOP_ bra L168 PL168 ;ELSE_ ;else, update the option byte bra L172 L167 DEC1 DEC1 movff WATCH_TEMP2,INDF1 ;ENDIF_ L172 ;BREAK_ ;quit loop in either case bra PL163 ;ENDIF_ L166 ;ELSE_ ;2nd byte doesn't match WATCH_TEMP1 bra L173 L165 INC1 ;point to third byte ;ENDIF_ L173 decf COUNT,F ;IF_ .Z. bnz L174 POINT sWatchArrayFull call StringDisplay ;BREAK_ bra PL163 ;ELSE_ bra L175 L174 INC1 ;adjust FSR1 to point to correct element. ;ENDIF_ L175 ;ENDLOOP_ bra L163 PL163 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_ L176 ;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 btfss WATCH_FLAG,VALID_HEX bra L177 movf POSTINC0,W call CheckHexDigit ;if one is invalid, VALID_HEX will be set to 0 ;ENDIF_ L177 decf COUNT,F ;UNTIL_ .Z. bnz L176 RL176 ;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 btfss WATCH_FLAG,VALID_HEX bra L178 movlw Debug_Bank+1 subwf WATCH_TEMP1,W ;WATCH_TEMP1-W = W ;IF_ .NN. ;if not negative Debug_Bank <= TEMP1 bn L179 movf WATCH_TEMP1,W xorlw 0x0F ;IF_ .NZ. ;if not zero, not BANK F, and therefore invalid bz L180 bcf WATCH_FLAG,VALID_HEX ;ENDIF_ L180 ;ENDIF_ L179 ;ENDIF_ L178 ;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. btfss WATCH_FLAG,VALID_HEX bra L181 ;loop until 0x00, look for 'h','a','b','-',#1-#9, ignore spaces. ;REPEAT_ L182 ;check current ascii movf INDF0,F ;if 0x00 , break ;IF_ .Z. bnz L183 ;BREAK_ bra RL182 ;ENDIF_ L183 WATCHCOMMAND ' ' ;IF_ .NZ. ;if not a space, keep checking bz L184 WATCHCOMMAND 'a' ;IF_ .Z. ;set ASCII mode bnz L185 ;if mode set (not 00), invalid command movlw RADIX_BITS andwf WATCH_TEMP2,W ;IF_ .Z. ;if zero, radix=00,and can be set. bnz L186 movlw RADIX_BITS andlw ASCII_RADIX iorwf WATCH_TEMP2,F ;ELSE_ bra L187 L186 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L187 ;ELSE_ bra L188 L185 WATCHCOMMAND 'b' ;set BINARY mode ;IF_ .Z. bnz L189 movlw RADIX_BITS andwf WATCH_TEMP2,W ;IF_ .Z. ;if zero, radix=00,and can be set. bnz L190 movf WATCH_TEMP2,W andlw LENGTH_BITS ;IF_ .Z. ;if zero, length = 0, ok to set to BIN mode bnz L191 ;IF_ WATCH_TEMP2,REVERSE_BIT == 0 ;cannot do reverse in binary mode btfsc WATCH_TEMP2,REVERSE_BIT bra L192 movlw RADIX_BITS andlw BIN_RADIX iorwf WATCH_TEMP2,F ;ELSE_ bra L193 L192 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L193 ;ELSE_ bra L194 L191 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L194 ;ELSE_ bra L195 L190 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L195 ;ELSE_ bra L196 L189 WATCHCOMMAND 'h' ;set HEX mode ;IF_ .Z. bnz L197 movlw RADIX_BITS andwf WATCH_TEMP2,W ;IF_ .Z. ;if zero, radix=00,and can be set. bnz L198 movlw RADIX_BITS andlw HEX_RADIX iorwf WATCH_TEMP2,F ;ELSE_ bra L199 L198 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L199 ;ELSE_ bra L200 L197 WATCHCOMMAND '-' ;set reverse order mode ;IF_ .Z. bnz L201 ;IF_ WATCH_TEMP2,REVERSE_BIT == 0 btfsc WATCH_TEMP2,REVERSE_BIT bra L202 movf WATCH_TEMP2,W andlw RADIX_BITS xorlw BIN_RADIX ;IF_ .NZ. ;can't have both reverse and binary bz L203 bsf WATCH_TEMP2,REVERSE_BIT ;ELSE_ bra L204 L203 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L204 ;ELSE_ ;more then one '-' bra L205 L202 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L205 ;ELSE_ bra L206 L201 movf WATCH_TEMP2,W andlw LENGTH_BITS ;IF_ .Z. ;check if more than one digit of length, if yes->invalid bnz L207 call UpdateLengthValue ;if invalid value, invalid bit will be set ;ELSE_ bra L208 L207 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L208 ;ENDIF_ L206 ;ENDIF_ L200 ;ENDIF_ L196 ;ENDIF_ L188 ;ENDIF_ L184 ;if running into any invalid, BREAK... ;IF_ WATCH_FLAG,INVALID_COMMAND == 1 btfss WATCH_FLAG,INVALID_COMMAND bra L209 ;BREAK_ ;exit repeat loop bra RL182 ;ENDIF_ L209 movf PREINC0,F ;LINEBUF always have a null 0x00 ;UNTIL_ .Z. ;if content is 0x00 (null) then quit bnz L182 RL182 ;ELSE_ bra L210 L181 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L210 ;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 btfsc WATCH_FLAG,INVALID_COMMAND bra L211 movf WATCH_TEMP2,W andlw LENGTH_BITS ;IF_ .Z. bnz L212 ;IF_ WATCH_TEMP2,REVERSE_BIT == 1 btfss WATCH_TEMP2,REVERSE_BIT bra L213 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L213 ;ENDIF_ L212 ;ENDIF_ L211 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 bnn L214 addlw 0x09 ;W + 0x09 = W ;IF_ .NN. ;if not negative 0x32 <= W <= 0x39 bn L215 movf WATCH_TEMP2,W andlw RADIX_BITS xorlw BIN_RADIX ;if also binary mode, invalid command ;IF_ .Z. bnz L216 bsf WATCH_FLAG,INVALID_COMMAND ;ELSE_ ;else, the current value of length is 0 bra L217 L216 movf INDF0,W ;write length value andlw 0x0F ;mask out top nibble iorwf WATCH_TEMP2,F ;ENDIF_ L217 ;ELSE_ bra L218 L215 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L218 ;ELSE_ bra L219 L214 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L219 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 bnn L220 addlw 0x0A ;W + 0x0A = W ;IF_ .NN. ;if not negative 0x30 <= TEMP1 <= 0x39 bn L221 bsf WATCH_FLAG,VALID_HEX ;ELSE_ ;if negative TEMP1 > 0x39 bra L222 L221 movf TEMP1,W sublw A'a'-1 ;0x60-TEMP1 = W ;IF_ .N. ;if negative 0x61 <= TEMP1 bnn L223 addlw 0x06 ;W + 0x06 = W ;IF_ .NN. ;if not negative 0x61 <= TEMP1 <= 0x66 bn L224 bsf WATCH_FLAG,VALID_HEX ;ENDIF_ L224 ;ENDIF_ L223 ;ENDIF_ L222 ;ENDIF_ L220 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_ L225 movlw 0x80 ;MSB must be set (signify invalid) movwf POSTINC1 ;Clear three-byte element clrf POSTINC1 clrf POSTINC1 decf TEMP1,F ;UNTIL_ .Z. bnz L225 RL225 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_ L226 ;IF_ INDF1,INVALID_COMMAND == 1 btfss INDF1,INVALID_COMMAND bra L227 ;BREAK_ bra PL226 ;ELSE_ bra L228 L227 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 btfss WATCH_TEMP2,REVERSE_BIT bra L229 movlw A'-' ;ELSE_ bra L230 L229 movlw A' ' ;ENDIF_ L230 call TXbyte movf WATCH_TEMP2,W ;check radix mode andlw RADIX_BITS xorlw ASCII_RADIX ;IF_ .Z. ;ascii mode bnz L231 movf WATCH_TEMP2,W andlw LENGTH_BITS addlw -0x05 ;WREG = WREG - 5 ;IF_ .NN. bn L232 addlw 0x02 ;ELSE_ bra L233 L232 movlw 0x01 ;ENDIF_ L233 ;ELSE_ bra L234 L231 movf WATCH_TEMP2,W andlw RADIX_BITS xorlw BIN_RADIX ;IF_ .Z. ;bin mode bnz L235 movlw 0x05 ;ELSE_ ;else, must be hex mode bra L236 L235 movf WATCH_TEMP2,W andlw LENGTH_BITS addlw -0x03 ;IF_ .NN. bn L237 addlw 0x01 ;ELSE_ bra L238 L237 movlw 0x00 ;ENDIF_ L238 rlncf WREG,F addlw 0x01 ;ENDIF_ L236 ;ENDIF_ L234 call DisplaySpaces ;display the number of spaces in WREG ;ENDIF_ L228 decf COUNT,F ;IF_ .Z. bnz L239 ;BREAK_ bra PL226 ;ELSE_ bra L240 L239 INC1 ;set up pointer for next Watch Variable ;ENDIF_ L240 ;ENDLOOP_ bra L226 PL226 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 btfss STATUS_TEMP,2 bra L241 movlw A'1' ;ELSE_ bra L242 L241 movlw A'0' ;ENDIF_ L242 call TXbyte movlw A' ' ;Space call TXbyte ;IF_ STATUS_TEMP,0 == 1 ;Display C btfss STATUS_TEMP,0 bra L243 movlw A'1' ;ELSE_ bra L244 L243 movlw A'0' ;ENDIF_ L244 call TXbyte movlw A' ' call TXbyte ;IF_ STATUS_TEMP,4 == 1 ;Display N btfss STATUS_TEMP,4 bra L245 movlw A'1' ;ELSE_ bra L246 L245 movlw A'0' ;ENDIF_ L246 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_ L247 ;IF_ INDF1,INVALID_COMMAND == 1 btfss INDF1,INVALID_COMMAND bra L248 ;BREAK_ bra PL247 ;ELSE_ ;else, display according to radix,length, and reverse option bra L249 L248 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. bnz L250 incf WATCH_TEMP2,F ;should not affect the upper nibble, lower was 0, add 1, becomes 1. ;ENDIF_ L250 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 btfsc WATCH_TEMP2,REVERSE_BIT bra L251 ;REPEAT_ L252 call DisplayRadix INC0 decf WATCH_TEMP2,F movf WATCH_TEMP2,W andlw LENGTH_BITS ;UNTIL_ .Z. bnz L252 RL252 ;ELSE_ ;decrement mode bra L253 L251 ;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. bnc L254 incf FSR0H,F ;ENDIF_ L254 ;REPEAT_ L255 call DisplayRadix DEC0 decf WATCH_TEMP2,F movf WATCH_TEMP2,W andlw LENGTH_BITS ;UNTIL_ .Z. bnz L255 RL255 ;ENDIF_ ;end if reverse = 0 or =1 L253 ;now output correct number of spaces movf TEMP1,W call DisplaySpaces ;ENDIF_ ;end if invalid command L249 decf COUNT,F ;IF_ .Z. bnz L256 ;BREAK_ bra PL247 ;ELSE_ bra L257 L256 INC1 ;set up pointer for next Watch Variable ;ENDIF_ L257 ;ENDLOOP_ bra L247 PL247 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 bnz L258 movf INDF0,W call CheckValidAscii call TXbyte movf TEMP1,W addlw -0x01 ;IF_ .NZ. bz L259 decf TEMP1,F ;ENDIF_ L259 ;ELSE_ bra L260 L258 movf WATCH_TEMP2,W andlw RADIX_BITS xorlw BIN_RADIX ;IF_ .Z. ;bin mode bnz L261 movf INDF0,W call DisplayBinaryByte movlw 0x01 movwf TEMP1 ;ELSE_ ;else, must be hex mode bra L262 L261 movf INDF0,W call DisplayHexByte movf TEMP1,W xorlw 0x05 ;IF_ .Z. bnz L263 movlw 0x03 ;ELSE_ bra L264 L263 movlw 0x01 ;ENDIF_ L264 movwf TEMP1 ;ENDIF_ L262 ;ENDIF_ L260 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_ L265 rlcf TEMP0,F ;Get bits MSb first and send them one by one ;IF_ .C. bnc L266 movlw A'1' ;ELSE_ bra L267 L266 movlw A'0' ;ENDIF_ L267 call TXbyte decf COUNT1,F ;UNTIL_ .Z. bnz L265 RL265 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) bnc L268 movlw A'*' ;ELSE_ bra L269 L268 movf TEMP0,W addlw -0x20 ;Carry will be set for WREG >= 0x20 ;IF_ .NC. ;Out of range (low) bc L270 movlw A'*' ;ELSE_ bra L271 L270 movf TEMP0,W ;ENDIF_ L271 ;ENDIF_ L269 return ;;;;;;; DisplaySpaces subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine sends WREG space characters to the PC. DisplaySpaces movwf TEMP0 movlw A' ' ;Get space character ;REPEAT_ L272 call TXbyte ;Send space character to PC decf TEMP0,F ;UNTIL_ .Z. bnz L272 RL272 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. bnz L273 call DisplayRAMorRegister ;ELSE_ bra L274 L273 call ModifyRAMorRegister ;ENDIF_ L274 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 bc L275 addlw 10+A'0' ;ELSE_ ;Fix letters bra L276 L275 addlw A'A' ;ENDIF_ L276 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' bnc L277 addlw 10 ;ELSE_ ;If '0' to '9' bra L278 L277 addlw 49 ;ENDIF_ L278 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