;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; QB452v1.2.4, the QwikBug monitor for the PIC18F452 ; Rawin Rajvanit 1-7-03 ; ;;;;;;; Program Hierarchy ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; QwikBug ; Initial ; SerialSetup ; WatchClear ; BreakPoint_Clear ; StringDisplay ; TXbyte ; AutoStart ; CheckForUserCode ; StringDisplay ; TXbyte ; TXbyte ; LoopTime ; MonitorFlags ; UserMemoryErase ; EraseMemory ; FlashErase64 ; StringDisplay ; TXbyte ; SingleStepCode ; StringDisplay ; TXbyte ; RXbyte ; CheckFuncKeys ; RXbyte ; FkeyDecode ; RXbyte ; CR_LF ; TXbyte ; TXbyte ; RunUserCode ; StringDisplay ; TXbyte ; RXbyte ; CheckFuncKeys ; RXbyte ; FkeyDecode ; RXbyte ; CR_LF ; TXbyte ; TXbyte ; DisplayWatchLabels ; StringDisplay ; TXbyte ; VarLabels ; GetWatchAddress ; DisplayHexNibble ; BinarytoASC ; TXbyte ; DisplayHexByte ; BinarytoASC ; TXbyte ; DisplaySpaces ; TXbyte ; GetNumBytes ; TXbyte ; CR_LF ; TXbyte ; DisplayWatchData ; DisplayHexByte ; BinarytoASC ; TXbyte ; DisplaySpaces ; TXbyte ; TXbyte ; DisplayHexNibble ; BinarytoASC ; TXbyte ; GetWatchAddress ; DisplayBinaryByte ; TXbyte ; GetNumBytes ; CheckValidAscii ; CR_LF ; TXbyte ; GetCommand ; RXbyte ; CheckFuncKeys ; RXbyte ; FkeyDecode ; RXbyte ; CR_LF ; TXbyte ; TXbyte ; CommandInterpreter ; FnKeyAction ; SerialSetup ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; list P=PIC18F452, F=INHX32, C=160, N=0, ST=OFF, MM=OFF, R=DEC, X=ON #include P18F452.inc __CONFIG _CONFIG1H, _HS_OSC_1H & _OSCS_OFF_1H ;HS osc.; osc. switch disabled __CONFIG _CONFIG2L, _BOR_OFF_2L & _PWRT_ON_2L ;BOR disabled; PWRT enabled __CONFIG _CONFIG2H, _WDT_OFF_2H ;Watchdog timer disabled __CONFIG _CONFIG3H, _CCP2MX_ON_3H ;CCP2 to RC1 __CONFIG _CONFIG4L, _DEBUG_ON_4L & _LVP_OFF_4L ;Background debugger enabled ; Low-voltage ICSP disabled __CONFIG _CONFIG5L, 0xFF ;No code protection, as required __CONFIG _CONFIG5H, 0xFF ; for background debug mode to work __CONFIG _CONFIG6L, 0xFF ; __CONFIG _CONFIG6H, 0XFF ; __CONFIG _CONFIG7L, 0xFF ; __CONFIG _CONFIG7H, 0xFF ; 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 0x1F ;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 ; movf REGISTER_TEMP,W ; call DisplayHexByte ; 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 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 ;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 btfsc QB_FLAGS,POR bra L15 POINT sQwikBug ;Display menu call StringDisplay POINT sFnKeyMenu call StringDisplay call AutoStart call MonitorFlags ;ENDIF_ L15 ;ENDIF_ L12 ;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 POINT sPrompt ;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_ bra L21 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_ L21 ;ENDIF_ L18 ;ENDIF_ L16 movff TOSH,PCH_TEMP_NEXT ;store next PC movff TOSL,PCL_TEMP_NEXT ; movf STKPTR,W ; call DisplayHexByte ; movlw A':' ; call TXbyte ; movf PCH_TEMP_NEXT,W ; call DisplayHexByte ; movf PCL_TEMP_NEXT,W ; call DisplayHexByte ;IF_ QB_FLAGS,FROM_USER == 1 ;check if from user btfss QB_FLAGS,FROM_USER bra L22 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 L23 ;IF_ DEBUG,SSTEP == 0 ;not a single step, btfsc DEBUG,SSTEP bra L24 ;IF_ PIR1,RCIF == 1 ;and a key was pressed btfss PIR1,RCIF bra L25 POINT sPrompt ;must be an interrupt key call StringDisplay ;show extra prompt for WatchLabels ;ENDIF_ L25 ;ENDIF_ L24 ;ENDIF_ L23 ;IF_ QB_FLAGS,SHOW_LABEL == 1 btfss QB_FLAGS,SHOW_LABEL bra L26 bcf QB_FLAGS,SHOW_LABEL call DisplayWatchLabels POINT sPrompt ;print prompt for WatchData call StringDisplay ;ENDIF_ L26 call DisplayWatchData ;always show WatchData if from user ;ENDIF_ L22 MainLoop ;LOOP_ L27 ; movf STKPTR,W ; call DisplayHexByte 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 L27 PL27 ;;;;;;; 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 bsf RCSTA,CREN ;enable reception bsf RCSTA,SPEN ;enable serial port return ;;;;;;; CheckResetCondition subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CheckResetCondition ;IF_ STKPTR,STKUNF == 1 ;detect stack underflow by checking STKFUL, purpose to show error only btfss STKPTR,STKUNF bra L28 bcf STKPTR,STKUNF POINT sSTKUNF call StringDisplay ;ENDIF_ L28 ;IF_ STKPTR,STKFUL == 1 ;detect stack overflow by checking STKUNF btfss STKPTR,STKFUL bra L29 bcf STKPTR,STKFUL POINT sSTKFUL call StringDisplay ;ENDIF_ L29 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 L30 ;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 L31 ;IF_ QB_FLAGS,INVALID_BP == 1 ;if not breakpoint, then check if it is a single step btfss QB_FLAGS,INVALID_BP bra L32 ;IF_ DEBUG,SSTEP == 0 ;if not SSTEP,then check if a key was pressed btfsc DEBUG,SSTEP bra L33 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 L34 bcf RCON,POR ;MCLR from user is equivalent to POR ;ENDIF_ L34 ;ENDIF_ L33 ;ENDIF_ L32 ;ELSE_ ;not from user, not a detectable reset; must be MCLR bra L35 L31 bcf RCON,POR ;MCLR from BDM is equivalent to POR ;ENDIF_ L35 ;ENDIF_ L30 ;IF_ RCON,POR == 0 ;Power-on Reset btfsc RCON,POR bra L36 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 L37 L36 bsf QB_FLAGS,POR ;ENDIF_ L37 ;IF_ RCON,BOR == 0 ;Brown-out Reset btfsc RCON,BOR bra L38 bcf QB_FLAGS,BOR bsf RCON,BOR POINT sBOR call StringDisplay ;ELSE_ bra L39 L38 bsf QB_FLAGS,BOR ;ENDIF_ L39 ;IF_ RCON,RI == 0 ;Reset Instruction btfsc RCON,RI bra L40 bcf QB_FLAGS,RI bsf RCON,RI ;IF_ QB_FLAGS2,LOAD_RESET == 0 btfsc QB_FLAGS2,LOAD_RESET bra L41 POINT sReset call StringDisplay ;ENDIF_ L41 ;ELSE_ bra L42 L40 bsf QB_FLAGS,RI ;ENDIF_ L42 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_ L43 rcall TXbyte ;Send byte via UART tblrd+* ;Increment pointer movf TABLAT,W ;Load next byte ;UNTIL_ .Z. bnz L43 RL43 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 ;REPEAT_ L44 ;UNTIL_ PIR1,TXIF == 1 ;Wait for previous transfer to be completed btfss PIR1,TXIF bra L44 RL44 movwf TXREG ;Transmit byte 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 L45 ;REPEAT_ L46 movf RCREG,W ;UNTIL_ PIR1,RCIF == 0 btfsc PIR1,RCIF bra L46 RL46 ;ENDIF_ L45 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_ L47 tblrd*+ movf TABLAT,F ;Check for zero ;IF_ .NZ. ;If not zero, check next byte for 0xef bz L48 tblrd*+ movf TABLAT,W sublw 0xef ;IF_ .Z. bnz L49 bcf MONITORSTATE,4 ;Found "goto" instruction ;ENDIF_ L49 ;BREAK_ ;Done in either case bra RL47 ;ELSE_ bra L50 L48 tblrd*+ movf TABLAT,W sublw 0xef ;IF_ .Z. bnz L51 bcf MONITORSTATE,4 ;Found "goto" instruction ;BREAK_ ;Exit in this case bra RL47 ;ELSE_ bra L52 L51 movf TABLAT,W ;IF_ .NZ. bz L53 ;BREAK_ ;Not a "nop", so exit in this case also bra RL47 ;ENDIF_ L53 ;ENDIF_ L52 ;ENDIF_ L50 decf COUNT,F ;UNTIL_ .Z. bnz L47 RL47 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_ L54 tblrd*+ movf TABLAT,W xorlw 0xFF ;IF_ .NZ. bz L55 ;BREAK_ bra PL54 ;ENDIF_ L55 decf COUNT_L,F ;IF_ .B. bc L56 decf COUNT_H,F ;ENDIF_ L56 movf COUNT_L,F ;IF_ .Z. bnz L57 movf COUNT_H,F ;IF_ .Z. bnz L58 ;BREAK_ bra PL54 ;ENDIF_ L58 ;ENDIF_ L57 ;ENDLOOP_ bra L54 PL54 movf COUNT_H,F ;IF_ .Z. bnz L59 movf COUNT_L,F ;IF_ .NZ. bz L60 bcf MONITORSTATE,4 ;ENDIF_ L60 ;ELSE_ bra L61 L59 bcf MONITORSTATE,4 ;ENDIF_ L61 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 L62 call UserMemoryErase ;Erase user memory block POINT sTError ;Display message call StringDisplay ;ELSE_ bra L63 L62 ;IF_ MONITORSTATE,7 == 1 ;User code outside of User addr space btfss MONITORSTATE,7 bra L64 call UserMemoryErase ;Erase user memory block POINT sLargeCode ;Display message call StringDisplay ;ELSE_ bra L65 L64 ;IF_ MONITORSTATE,4 == 1 ;No user reset vector is loaded btfss MONITORSTATE,4 bra L66 POINT sNoUserVector ;Display message call StringDisplay ;ELSE_ bra L67 L66 ;IF_ MONITORSTATE,1 == 1 ;Single step btfss MONITORSTATE,1 bra L68 call SingleStepCode ;ELSE_ bra L69 L68 ;IF_ MONITORSTATE,0 == 1 ;run user code btfss MONITORSTATE,0 bra L70 call RunUserCode ;ENDIF_ L70 ;ENDIF_ L69 ;ENDIF_ L67 ;ENDIF_ L65 ;ENDIF_ L63 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. ; movf STKPTR,W ;IF_ .NZ. ;must push stack after reset bz L71 incf STKPTR,F MOVLF H'00',TOSU MOVLF H'00',TOSH MOVLF H'00',TOSL ;ENDIF_ L71 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 L72 bsf T0CON,TMR0ON ;ENDIF_ L72 ;IF_ REGISTER_TEMP,TMR1_ON == 1 btfss REGISTER_TEMP,TMR1_ON bra L73 bsf T1CON,TMR1ON ;ENDIF_ L73 ;IF_ REGISTER_TEMP,TMR2_ON == 1 btfss REGISTER_TEMP,TMR2_ON bra L74 bsf T2CON,TMR2ON ;ENDIF_ L74 ;IF_ REGISTER_TEMP,TMR3_ON == 1 btfss REGISTER_TEMP,TMR3_ON bra L75 bsf T3CON,TMR3ON ;ENDIF_ L75 ;IF_ REGISTER_TEMP,GIEH == 1 btfss REGISTER_TEMP,GIEH bra L76 bsf INTCON,GIEH ;ENDIF_ L76 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 L77 POINT sRunning call StringDisplay ;ENDIF_ L77 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. ; movf STKPTR,W ;IF_ .NZ. ;must push stack after reset bz L78 incf STKPTR,F MOVLF H'00',TOSU MOVLF H'00',TOSH MOVLF H'00',TOSL ;ENDIF_ L78 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 L79 bsf T0CON,TMR0ON ;ENDIF_ L79 ;IF_ REGISTER_TEMP,TMR1_ON == 1 btfss REGISTER_TEMP,TMR1_ON bra L80 bsf T1CON,TMR1ON ;ENDIF_ L80 ;IF_ REGISTER_TEMP,TMR2_ON == 1 btfss REGISTER_TEMP,TMR2_ON bra L81 bsf T2CON,TMR2ON ;ENDIF_ L81 ;IF_ REGISTER_TEMP,TMR3_ON == 1 btfss REGISTER_TEMP,TMR3_ON bra L82 bsf T3CON,TMR3ON ;ENDIF_ L82 ;IF_ REGISTER_TEMP,GIEH == 1 btfss REGISTER_TEMP,GIEH bra L83 bsf INTCON,GIEH ;ENDIF_ L83 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 btfss PIR1,RCIF bra L84 ;REPEAT_ ;might be more than one char, remove all L85 movf RCREG,W ;UNTIL_ PIR1,RCIF == 0 btfsc PIR1,RCIF bra L85 RL85 ;ENDIF_ L84 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_ L86 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 L87 call CheckShortcut ;Check for shortcut keys movf FKEY,F ;Did we receive a function key (NZ=1)? ;IF_ .NZ. ;Yes; exit bz L88 ;BREAK_ bra RL86 ;ENDIF_ L88 ;ENDIF_ L87 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 L89 bsf QB_FLAGS2,FNKEYS ;BREAK_ bra RL86 ;ELSE_ bra L90 L89 bcf QB_FLAGS2,FNKEYS ;ENDIF_ L90 movf INDF0,W ;Is it the enter key (CR)? xorlw 0x0D ;IF_ .Z. ;Yes bnz L91 MOVLF 0,INDF0 ;Null terminate the string rcall CR_LF ;Send CR, LF and exit ;BREAK_ bra RL86 ;ENDIF_ L91 movf INDF0,W ;Is it a backspace? xorlw 0x08 ;IF_ .Z. ;Yes bnz L92 movlw low LINEBUF ;Look for empty LINEBUF xorwf FSR0L,W ;IF_ .NZ. ;Backspace if not at beginning of the line bz L93 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_ L93 ;ELSE_ bra L94 L92 movf POSTINC0,W ;Echo received character call TXbyte decf COUNT1,F ;Decrement space remaining in LINEBUF ;IF_ .Z. bnz L95 MOVLF 0,INDF0 ;Null terminate the string rcall CR_LF ;and send CR, LF ;ENDIF_ L95 ;ENDIF_ L94 movf COUNT1,F ;Repeat until line buffer is full ;UNTIL_ .Z. bnz L86 RL86 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 L96 MOVLF 10,FKEY ;ELSE_ bra L97 L96 xorlw 3 ;Original A'2' now sets Z=1 ;IF_ .Z. bnz L98 MOVLF 20,FKEY ;ENDIF_ L98 ;ENDIF_ L97 call RXbyte ;Get the next byte into WREG movf FKEY,F ;FKEY now contains 0, 10, or 20 ;IF_ .NZ. bz L99 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 L100 incf TBLPTRH,F ;ENDIF_ L100 tblrd* movff TABLAT,FKEY ;ENDIF_ L99 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_ L101 ;REPEAT_ ;Inner loop takes 256x3x0.4 = 303 microsec L102 decf WREG,F ;UNTIL_ .Z. bnz L102 RL102 decf COUNT,F ;UNTIL_ .Z. ;Outer loop takes 303x32 = 9830 microsec bnz L101 RL101 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 L103 bcf RCSTA, CREN ; then clear it bsf RCSTA, CREN ; and reenable receives ;ENDIF_ L103 ;REPEAT_ L104 ;UNTIL_ PIR1,RCIF == 1 ;Wait for byte to be received btfss PIR1,RCIF bra L104 RL104 movf RCREG,W ;Put received byte into W 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 ;;;;;;; CheckShortcut subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; CheckShortcut iorlw B'00100000' ;Convert to lower case movwf TEMP0 xorlw A'h' ;IF_ .Z. bnz L105 MOVLF 0x01,FKEY ;ENDIF_ L105 movf TEMP0,W xorlw A't' ;IF_ .Z. bnz L106 MOVLF 0x02,FKEY ;ENDIF_ L106 movf TEMP0,W xorlw A'l' ;IF_ .Z. bnz L107 MOVLF 0x03,FKEY ;ENDIF_ L107 movf TEMP0,W xorlw A'd' ;IF_ .Z. bnz L108 MOVLF 0x04,FKEY ;ENDIF_ L108 movf TEMP0,W xorlw A'b' ;IF_ .Z. bnz L109 MOVLF 0x05,FKEY ;ENDIF_ L109 movf TEMP0,W xorlw A'w' ;IF_ .Z. bnz L110 MOVLF 0x06,FKEY ;ENDIF_ L110 movf TEMP0,W xorlw A'r' ;IF_ .Z. bnz L111 MOVLF 0x07,FKEY ;ENDIF_ L111 movf TEMP0,W xorlw A's' ;IF_ .Z. bnz L112 MOVLF 0x08,FKEY ;ENDIF_ L112 movf TEMP0,W xorlw A'm' ;IF_ .Z. bnz L113 MOVLF 0x09,FKEY ;ENDIF_ L113 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 L114 call RXbyte ;Get Next byte movwf INDF0 ;write to current position in INDF xorlw A'[' ;XOR WREG with '[' ;IF_ .Z. bnz L115 call RXbyte ;Get next byte movwf INDF0 ; write to current position in INDF0 call FkeyDecode ; F1-F12 -> 0x00-0x0E in FKEY ;ENDIF_ L115 ;ENDIF_ L114 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 L116 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_ L116 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 L117 incf PCLATH,F ;ENDIF_ L117 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_ L118 call LoopTime decf TEMP1,F ;UNTIL_ .Z. bnz L118 RL118 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 sPrompt ;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 L119 bsf MONITORSTATE,0 ;Run User Code ;ENDIF_ L119 return KeyAct8 call CheckForUserCode ;Check if User Code is Loaded ;IF_ MONITORSTATE,4 == 0 btfsc MONITORSTATE,4 bra L120 bsf MONITORSTATE,1 ;Single Step User Code ;ENDIF_ L120 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_ L121 movlw D'16' xorwf COUNT1,W ;IF_ .Z. bnz L122 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_ L122 tblrd*+ ;movf TABLAT,W ;call DisplayHexByte ;IF_ TBLPTRL,0 == 1 btfss TBLPTRL,0 bra L123 movff TABLAT,TEMP1 ;ELSE_ bra L124 L123 movf TABLAT,W call DisplayHexByte movf TEMP1,W call DisplayHexByte ;ENDIF_ L124 ;IF_ TBLPTRL,0 == 0 btfsc TBLPTRL,0 bra L125 movlw A' ' call TXbyte ;ENDIF_ L125 decf COUNT1,F movlw 0x00 xorwf COUNT1,W ;IF_ .Z. bnz L126 movlw A'\r' call TXbyte movlw A'\n' call TXbyte MOVLF D'16',COUNT1 ;ENDIF_ L126 decf COUNT,F ;UNTIL_ .Z. bnz L121 RL121 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_ L127 ;REPEAT_ L128 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 L128 RL128 movf FSR2H,F ;UNTIL_ .Z. bnz L127 RL127 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 L129 ;IF_ .Z. ;ADDRH = QwikBug(high), check ADDRL bnz L130 movlw low QwikBug_Trap subwf ADDRL,W ;IF_ .NN. ;ADDRL >= QwikBug(low) bn L131 bsf MONITORSTATE,7 ;set flag for error ;ENDIF_ L131 ;ELSE_ ;positive result bra L132 L130 bsf MONITORSTATE,7 ;ADDRH > QwikBug(high) ;ENDIF_ L132 ;ENDIF_ L129 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 L133 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_ L133 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_ L134 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 L135 movf INDF0,F ;IF_ .NZ. ;if an enter key, do not show invalid command bz L136 INC0 movf INDF0,F ;IF_ .Z. ;if 2nd is 0x00 bnz L137 DEC0 WATCHCOMMAND 'r' ;IF_ .Z. bnz L138 call BreakPoint_Clear POINT sNoBreakpointSet call StringDisplay ;BREAK_ ;Quit loop if valid command bra PL134 ;ELSE_ bra L139 L138 WATCHCOMMAND 'd' ;IF_ .Z. bnz L140 call DisplayBreakPoint ;BREAK_ ;Quit loop if valid command bra PL134 ;ELSE_ ;neither 'r' or 'd' bra L141 L140 POINT sInvalidCommand call StringDisplay ;ENDIF_ L141 ;ENDIF_ L139 ;ELSE_ ;2nd not 0x00, see if valid bka bra L142 L137 DEC0 MOVLF 0x04,COUNT bsf WATCH_FLAG,VALID_HEX ;REPEAT_ L143 ;IF_ WATCH_FLAG,VALID_HEX == 1 btfss WATCH_FLAG,VALID_HEX bra L144 movf POSTINC0,W call CheckHexDigit ;ENDIF_ L144 decf COUNT,F ;UNTIL_ .Z. bnz L143 RL143 ;IF_ WATCH_FLAG,VALID_HEX == 1 btfss WATCH_FLAG,VALID_HEX bra L145 movf INDF0,F ;if valid_hex, INDF0 is at 5th position ;IF_ .Z. ;check for 0x00 bnz L146 call BreakPoint_Set call DisplayBreakPoint ;BREAK_ ;Quit loop if valid command bra PL134 ;ELSE_ bra L147 L146 POINT sInvalidCommand call StringDisplay ;ENDIF_ L147 ;ELSE_ bra L148 L145 POINT sInvalidCommand call StringDisplay ;ENDIF_ L148 ;ENDIF_ L142 ;ELSE_ ;else 0x00 at 1st position bra L149 L136 ;BREAK_ bra PL134 ;ENDIF_ ;end if not 0x00 at 1st position L149 ;ELSE_ bra L150 L135 call CR_LF POINT sInvalidCommand call StringDisplay ;ENDIF_ ;end if function key L150 ;ENDLOOP_ bra L134 PL134 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_ L151 call ASC2toBinary movff HEXBYTE,POSTDEC1 movf POSTINC0,W decf COUNT,F ;UNTIL_ .Z. bnz L151 RL151 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 L152 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 L153 L152 POINT sNoBreakpointSet call StringDisplay ;ENDIF_ L153 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_ L154 POINT sF6 call StringDisplay call GetCommand ;IF_ QB_FLAGS2,FNKEYS == 0 ;ParseCommand cannot detect function keys, must check flag! btfsc QB_FLAGS2,FNKEYS bra L155 lfsr 0,LINEBUF movf INDF0,F ;IF_ .NZ. ;if 0x00 at 1st position, do not show invalid command bz L156 call ParseCommand ;IF_ WATCH_FLAG,INVALID_COMMAND == 0 btfsc WATCH_FLAG,INVALID_COMMAND bra L157 call UpdateWatchArray ;BREAK_ bra PL154 ;ELSE_ bra L158 L157 POINT sInvalidCommand call StringDisplay ;ENDIF_ ;end if INVALID_COMMAND L158 ;ELSE_ ;else 0x00 at 1st position bra L159 L156 ;BREAK_ bra PL154 ;ENDIF_ ;end if not 0x00 at 1st position L159 ;ELSE_ bra L160 L155 call CR_LF POINT sInvalidCommand call StringDisplay ;ENDIF_ ;end if funtion keys L160 ;ENDLOOP_ bra L154 PL154 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_ L161 ;IF_ INDF1,INVALID_COMMAND == 1 ;add new watch variable btfss INDF1,INVALID_COMMAND bra L162 movff WATCH_TEMP2,POSTINC1 movff WATCH_TEMP1,POSTINC1 movff WATCH_TEMP0,INDF1 ;BREAK_ bra PL161 ;ENDIF_ L162 movf PREINC1,W ;point to second byte xorwf WATCH_TEMP1,W ;IF_ .Z. ;if zero, first part of address matches bnz L163 movf PREINC1,W xorwf WATCH_TEMP0,W ;IF_ .Z. ;if zero, second part of address matches, now check options bnz L164 movf WATCH_TEMP2,F ;IF_ .Z. ;if zero, default mode, delete this element bnz L165 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_ L166 decf COUNT,F ;IF_ .Z. bnz L167 bsf INDF1,INVALID_COMMAND ;BREAK_ bra PL166 ;ENDIF_ L167 ;IF_ INDF0,INVALID_COMMAND == 0 btfsc INDF0,INVALID_COMMAND bra L168 movff POSTINC0,POSTINC1 movff POSTINC0,POSTINC1 movff POSTINC0,POSTINC1 ;ELSE_ bra L169 L168 bsf INDF1,INVALID_COMMAND ;BREAK_ bra PL166 ;ENDIF_ L169 ;ENDLOOP_ bra L166 PL166 ;ELSE_ ;else, update the option byte bra L170 L165 DEC1 DEC1 movff WATCH_TEMP2,INDF1 ;ENDIF_ L170 ;BREAK_ ;quit loop in either case bra PL161 ;ENDIF_ L164 ;ELSE_ ;2nd byte doesn't match WATCH_TEMP1 bra L171 L163 INC1 ;point to third byte ;ENDIF_ L171 decf COUNT,F ;IF_ .Z. bnz L172 POINT sWatchArrayFull call StringDisplay ;BREAK_ bra PL161 ;ELSE_ bra L173 L172 INC1 ;adjust FSR1 to point to correct element. ;ENDIF_ L173 ;ENDLOOP_ bra L161 PL161 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_ L174 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 L175 movf POSTINC0,W call CheckHexDigit ;if one is invalid, VALID_HEX will be set to 0 ;ENDIF_ L175 decf COUNT,F ;UNTIL_ .Z. bnz L174 RL174 ;IF_ WATCH_FLAG,VALID_HEX == 1 btfss WATCH_FLAG,VALID_HEX bra L176 movlw Debug_Bank+1 subwf WATCH_TEMP1,W ;WATCH_TEMP1-W = W ;IF_ .NN. ;if not negative Debug_Bank <= TEMP1 bn L177 movf WATCH_TEMP1,W xorlw 0x0F ;IF_ .NZ. ;if not zero, not BANK F, and therefore invalid bz L178 bcf WATCH_FLAG,VALID_HEX ;ENDIF_ L178 ;ENDIF_ L177 ;ENDIF_ L176 ;IF_ WATCH_FLAG,VALID_HEX == 1 ;if valid hex, store in WTEMP1 and WTEMP0, then check for others. btfss WATCH_FLAG,VALID_HEX bra L179 ;REPEAT_ L180 movf INDF0,F ;if 0x00 , break ;IF_ .Z. bnz L181 ;BREAK_ bra RL180 ;ENDIF_ L181 WATCHCOMMAND ' ' ;IF_ .NZ. ;if not a space, keep checking bz L182 WATCHCOMMAND 'a' ;IF_ .Z. ;set ASCII mode bnz L183 movlw RADIX_BITS andwf WATCH_TEMP2,W ;IF_ .Z. ;if zero, radix=00,and can be set. bnz L184 movlw RADIX_BITS andlw ASCII_RADIX iorwf WATCH_TEMP2,F ;ELSE_ bra L185 L184 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L185 ;ELSE_ bra L186 L183 WATCHCOMMAND 'b' ;set BINARY mode ;IF_ .Z. bnz L187 movlw RADIX_BITS andwf WATCH_TEMP2,W ;IF_ .Z. ;if zero, radix=00,and can be set. bnz L188 movf WATCH_TEMP2,W andlw LENGTH_BITS ;IF_ .Z. ;if zero, length = 0, ok to set to BIN mode bnz L189 ;IF_ WATCH_TEMP2,REVERSE_BIT == 0 ;cannot do reverse in binary mode btfsc WATCH_TEMP2,REVERSE_BIT bra L190 movlw RADIX_BITS andlw BIN_RADIX iorwf WATCH_TEMP2,F ;ELSE_ bra L191 L190 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L191 ;ELSE_ bra L192 L189 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L192 ;ELSE_ bra L193 L188 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L193 ;ELSE_ bra L194 L187 WATCHCOMMAND 'h' ;set HEX mode ;IF_ .Z. bnz L195 movlw RADIX_BITS andwf WATCH_TEMP2,W ;IF_ .Z. ;if zero, radix=00,and can be set. bnz L196 movlw RADIX_BITS andlw HEX_RADIX iorwf WATCH_TEMP2,F ;ELSE_ bra L197 L196 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L197 ;ELSE_ bra L198 L195 WATCHCOMMAND '-' ;set reverse order mode ;IF_ .Z. bnz L199 ;IF_ WATCH_TEMP2,REVERSE_BIT == 0 btfsc WATCH_TEMP2,REVERSE_BIT bra L200 movf WATCH_TEMP2,W andlw RADIX_BITS xorlw BIN_RADIX ;IF_ .NZ. ;can't have both reverse and binary bz L201 bsf WATCH_TEMP2,REVERSE_BIT ;ELSE_ bra L202 L201 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L202 ;ELSE_ ;more then one '-' bra L203 L200 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L203 ;ELSE_ bra L204 L199 movf WATCH_TEMP2,W andlw LENGTH_BITS ;IF_ .Z. ;check if more than one digit of length, if yes->invalid bnz L205 call UpdateLengthValue ;if invalid value, invalid bit will be set ;ELSE_ bra L206 L205 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L206 ;ENDIF_ L204 ;ENDIF_ L198 ;ENDIF_ L194 ;ENDIF_ L186 ;ENDIF_ L182 ;IF_ WATCH_FLAG,INVALID_COMMAND == 1 btfss WATCH_FLAG,INVALID_COMMAND bra L207 ;BREAK_ ;exit repeat loop bra RL180 ;ENDIF_ L207 movf PREINC0,F ;LINEBUF always have a null 0x00 ;UNTIL_ .Z. ;if content is 0x00 (null) then quit bnz L180 RL180 ;ELSE_ bra L208 L179 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L208 ;IF_ WATCH_FLAG,INVALID_COMMAND == 0 btfsc WATCH_FLAG,INVALID_COMMAND bra L209 movf WATCH_TEMP2,W andlw LENGTH_BITS ;IF_ .Z. bnz L210 ;IF_ WATCH_TEMP2,REVERSE_BIT == 1 btfss WATCH_TEMP2,REVERSE_BIT bra L211 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L211 ;ENDIF_ L210 ;ENDIF_ L209 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 L212 addlw 0x09 ;W + 0x09 = W ;IF_ .NN. ;if not negative 0x32 <= W <= 0x39 bn L213 movf WATCH_TEMP2,W andlw RADIX_BITS xorlw BIN_RADIX ;if also binary mode, invalid command ;IF_ .Z. bnz L214 bsf WATCH_FLAG,INVALID_COMMAND ;ELSE_ ;else, the current value of length is 0 bra L215 L214 movf INDF0,W ;write length value andlw 0x0F ;mask out top nibble iorwf WATCH_TEMP2,F ;ENDIF_ L215 ;ELSE_ bra L216 L213 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L216 ;ELSE_ bra L217 L212 bsf WATCH_FLAG,INVALID_COMMAND ;ENDIF_ L217 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 L218 addlw 0x0A ;W + 0x0A = W ;IF_ .NN. ;if not negative 0x30 <= TEMP1 <= 0x39 bn L219 bsf WATCH_FLAG,VALID_HEX ;ELSE_ ;if negative TEMP1 > 0x39 bra L220 L219 movf TEMP1,W sublw A'a'-1 ;0x60-TEMP1 = W ;IF_ .N. ;if negative 0x61 <= TEMP1 bnn L221 addlw 0x06 ;W + 0x06 = W ;IF_ .NN. ;if not negative 0x61 <= TEMP1 <= 0x66 bn L222 bsf WATCH_FLAG,VALID_HEX ;ENDIF_ L222 ;ENDIF_ L221 ;ENDIF_ L220 ;ENDIF_ L218 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_ L223 movlw 0x80 ;MSB must be set (signify invalid) movwf POSTINC1 ;Clear three-byte element clrf POSTINC1 clrf POSTINC1 decf TEMP1,F ;UNTIL_ .Z. bnz L223 RL223 return ;;;;;;;;;;;;;;;;;;;;;;;;;; DISPLAY WATCH VARIABLES ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;; DisplayWatchLabels 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_ L224 ;IF_ INDF1,INVALID_COMMAND == 1 btfss INDF1,INVALID_COMMAND bra L225 ;BREAK_ bra PL224 ;ELSE_ bra L226 L225 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 L227 movlw A'-' ;ELSE_ bra L228 L227 movlw A' ' ;ENDIF_ L228 call TXbyte movf WATCH_TEMP2,W ;check radix mode andlw RADIX_BITS xorlw ASCII_RADIX ;IF_ .Z. ;ascii mode bnz L229 movf WATCH_TEMP2,W andlw LENGTH_BITS addlw -0x05 ;WREG = WREG - 5 ;IF_ .NN. bn L230 addlw 0x02 ;ELSE_ bra L231 L230 movlw 0x01 ;ENDIF_ L231 ;ELSE_ bra L232 L229 movf WATCH_TEMP2,W andlw RADIX_BITS xorlw BIN_RADIX ;IF_ .Z. ;bin mode bnz L233 movlw 0x05 ;ELSE_ ;else, must be hex mode bra L234 L233 movf WATCH_TEMP2,W andlw LENGTH_BITS addlw -0x03 ;IF_ .NN. bn L235 addlw 0x01 ;ELSE_ bra L236 L235 movlw 0x00 ;ENDIF_ L236 rlncf WREG,F addlw 0x01 ;ENDIF_ L234 ;ENDIF_ L232 call DisplaySpaces ;display the number of spaces in WREG ;ENDIF_ L226 decf COUNT,F ;IF_ .Z. bnz L237 ;BREAK_ bra PL224 ;ELSE_ bra L238 L237 INC1 ;set up pointer for next Watch Variable ;ENDIF_ L238 ;ENDLOOP_ bra L224 PL224 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 L239 movlw A'1' ;ELSE_ bra L240 L239 movlw A'0' ;ENDIF_ L240 call TXbyte movlw A' ' ;Space call TXbyte ;IF_ STATUS_TEMP,0 == 1 ;Display C btfss STATUS_TEMP,0 bra L241 movlw A'1' ;ELSE_ bra L242 L241 movlw A'0' ;ENDIF_ L242 call TXbyte movlw A' ' call TXbyte ;IF_ STATUS_TEMP,4 == 1 ;Display N btfss STATUS_TEMP,4 bra L243 movlw A'1' ;ELSE_ bra L244 L243 movlw A'0' ;ENDIF_ L244 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_ L245 ;IF_ INDF1,INVALID_COMMAND == 1 btfss INDF1,INVALID_COMMAND bra L246 ;BREAK_ bra PL245 ;ELSE_ ;else, display according to radix,length, and reverse option bra L247 L246 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 L248 incf WATCH_TEMP2,F ;should not affect the upper nibble, lower was 0, add 1, becomes 1. ;ENDIF_ L248 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 L249 ;REPEAT_ L250 call DisplayRadix INC0 decf WATCH_TEMP2,F movf WATCH_TEMP2,W andlw LENGTH_BITS ;UNTIL_ .Z. bnz L250 RL250 ;ELSE_ ;decrement mode bra L251 L249 movf WATCH_TEMP2,W andlw LENGTH_BITS addlw -0x01 ;decrement by 1 first. addwf FSR0L,F ;IF_ .C. bnc L252 incf FSR0H,F ;ENDIF_ L252 ;REPEAT_ L253 call DisplayRadix DEC0 decf WATCH_TEMP2,F movf WATCH_TEMP2,W andlw LENGTH_BITS ;UNTIL_ .Z. bnz L253 RL253 ;ENDIF_ ;end if reverse = 0 or =1 L251 movf TEMP1,W call DisplaySpaces ;ENDIF_ ;end if invalid command L247 decf COUNT,F ;IF_ .Z. bnz L254 ;BREAK_ bra PL245 ;ELSE_ bra L255 L254 INC1 ;set up pointer for next Watch Variable ;ENDIF_ L255 ;ENDLOOP_ bra L245 PL245 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 L256 movf INDF0,W call CheckValidAscii call TXbyte movf TEMP1,W addlw -0x01 ;IF_ .NZ. bz L257 decf TEMP1,F ;ENDIF_ L257 ;ELSE_ bra L258 L256 movf WATCH_TEMP2,W andlw RADIX_BITS xorlw BIN_RADIX ;IF_ .Z. ;bin mode bnz L259 movf INDF0,W call DisplayBinaryByte movlw 0x01 movwf TEMP1 ;ELSE_ ;else, must be hex mode bra L260 L259 movf INDF0,W call DisplayHexByte movf TEMP1,W xorlw 0x05 ;IF_ .Z. bnz L261 movlw 0x03 ;ELSE_ bra L262 L261 movlw 0x01 ;ENDIF_ L262 movwf TEMP1 ;ENDIF_ L260 ;ENDIF_ L258 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_ L263 rlcf TEMP0,F ;Get bits MSb first and send them one by one ;IF_ .C. bnc L264 movlw A'1' ;ELSE_ bra L265 L264 movlw A'0' ;ENDIF_ L265 call TXbyte decf COUNT1,F ;UNTIL_ .Z. bnz L263 RL263 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 L266 movlw A'*' ;ELSE_ bra L267 L266 movf TEMP0,W addlw -0x20 ;Carry will be set for WREG >= 0x20 ;IF_ .NC. ;Out of range (low) bc L268 movlw A'*' ;ELSE_ bra L269 L268 movf TEMP0,W ;ENDIF_ L269 ;ENDIF_ L267 return ;;;;;;; DisplaySpaces subroutine ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ; This subroutine sends WREG space characters to the PC. DisplaySpaces movwf TEMP0 movlw A' ' ;Get space character ;REPEAT_ L270 call TXbyte ;Send space character to PC decf TEMP0,F ;UNTIL_ .Z. bnz L270 RL270 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 L271 call DisplayRAMorRegister ;ELSE_ bra L272 L271 call ModifyRAMorRegister ;ENDIF_ L272 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 L273 addlw 10+A'0' ;ELSE_ ;Fix letters bra L274 L273 addlw A'A' ;ENDIF_ L274 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 L275 addlw 10 ;ELSE_ ;If '0' to '9' bra L276 L275 addlw 49 ;ENDIF_ L276 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 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; sQwikBug db "\r\n\t\t\tQwikBug for PIC18F452 v1.2.3\r\n\x00" 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" sPrompt db "QB>\x00" sDisMod db "Display/Modify contents of RAM or register address: \r\n to Display , to Modify\r\n\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\x00" 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. \n\r\x00" sF10 db "F10 \r\n\x00" sF11 db "F11 \r\n\x00" ;sF12 db "F12 \r\n\x00" sF12 db "F12:Enter High Address(2 hex digits)>\x00" sBOR db "Brown-out Reset Detected\n\r\x00" sSTKFUL db "Stack Overflow\n\r\x00" sSTKUNF db "Stack Underflow\n\r\x00" sInvalidCommand db " F6>Invalid Command\n\r\x00" sWatchArrayFull db " F6>Watch Array Full\n\r\x00" sHighAddress db "Highest Address Programmed: 0x\x00" ;sByteUsed db "User Memory Space Used: \x00" ;sByte db " bytes\n\r\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