; ; 8051 monitor ; ; Completed Fri Dec 13, 1996 -- J Zbiciak ; Main System Equates .equ CR , 0x0dh .equ LF , 0x0ah .equ FF , 0x0ch .equ NULL , 0x00h .equ MAXCHAR , 0x40h ; Maximum line input length=64 chars. .equ BS , 0x08h .equ BELL , 0x07h .equ SPC , 0x20h .equ ESC , 0x1bh .equ EOT , 0xffh .equ BKPT , 0x3fh ; my preference ;.equ TERMESC , 0x14h ; Terminal Mode escape (^T) ;.equ TERMDL , 0x12h ; Terminal Mode download request (^R) ; standard .equ TERMESC , 0x03h ; Terminal Mode escape (^C) .equ TERMDL , 0x01h ; Terminal Mode download request (^A) .equ TSTAT , 0xff25h .equ TDATA , 0xff24h .equ USTAT , 0xff27h .equ UDATA , 0xff26h .equ BAUDS , 0xff1fh .equ BAUD , 0x3fh .equ IE0V , 0x1003h .equ TF0V , 0x100bh .equ IE1V , 0x1013h .equ TF1V , 0x101bh .equ RIVTIV , 0x1023h .equ TF2V , 0x102bh .equ T2MOD , 0xC9h .equ SADEN , 0xB9h .equ SADDR , 0xA9h ; initialize program variables to upper 256 bytes of RAM onboard .equ MSTART , 0x0000h ; Where to start monitor in the address spc. .equ MONSTK , 0xE0h ; where to start monitor's internal stack .equ SVSTART , 0xE0h ; start of memory area to save .equ SVEND , 0x20h ; byte after last location to save .equ MPG , MSTART+0x0f00h ; page holding the monitor variables .equ SVDBLK , MPG+0 ; internal-RAM save area .equ SVDCNT , (SVEND-SVSTART)&0xFF ; count of addresses to save .equ SVDREG , SVDBLK+SVDCNT ; register value save area .equ REGSTK , 0x10h ; where to "stack" registers being saved .equ ASAVE , MPG+0xF0h ; A-register save location .equ BSAVE , ASAVE+1 ; B-register " " .equ R0SAVE , BSAVE+1 ; B-register " " .equ PCVAL , R0SAVE+1 ; PC " " .equ BPAD , PCVAL+2 ; Breakpoint address .equ ISBKP , BPAD+2 ; Is Breakpoint? flag .equ DEBUG , ISBKP+1 ; Excerpted code for breakpoint handling .equ DBGE , (DEBUG+3)&0xFFh ; LSB of end-of-excerpted code+1 .equ DBGL , DEBUG&0xFFh ; LSB of DEBUG address .equ DBGH , DEBUG/256 ; MSB of DEBUG address .equ QUIET , DEBUG+3 ; Quiet-mode flag .equ ARGV1L , 0x0004h ; R4 .equ ARGV1H , 0x0005h ; R5 .equ ARGV2L , 0x0006h ; R6 .equ ARGV2H , 0x0007h ; R7 .equ CKSUM , 0x0006h ; R6 .equ CKERR , 0x0007h ; R7 .equ COUNT , 0x0002h ; R2 .equ TEMP , 0x0003h ; R3 .equ BUFF , 0x80h ; 0x80h - 0xDBh char buffer .equ SLFMOD , 0x1000h ; location where self-modifying code is kept .equ P2SAVE , 0x30h ; location user should save P2 in .org MSTART ljmp main ; special function register addresses .org MSTART+0x0030h enter: push DPL ; save DPTR push DPH push PSW ; save PSW clr 0xD5h ; clear "check for bkpt" flag sjmp cont benter: push DPL ; entry point for breakpoints push DPH push PSW setb 0xD5h ; set "check for bkpt" flag cont: clr 0xD3h ; set PSW for Bank 0 clr 0xD4h ; Bank 0 --> R0-R7 are in 00h-07h mov DPTR,#ASAVE movx @DPTR,A ; save A inc DPTR mov A,B movx @DPTR,A ; save B inc DPTR mov A,R0 movx @DPTR,A ; save R0 mov DPTR,#SVDBLK ; point to memory-save arena mov R0,#SVSTART ; Point R0 to start of save area ent0: mov A,@R0 ; grab a byte from internal RAM into A movx @DPTR,A ; write byte to save arena inc DPTR inc R0 cjne R0,#SVEND,ent0 ; loop until all are saved ; move the 6 SFRs that the monitor modifies into internal RAM mov R0,PSW pop PSW ; restore saved PSW pop DPH ; restore saved DPTR pop DPL pop 0xf0h ; get PC value from 'lcall enter' pop 0xe0h ; if this is a breakpoint, into B:A mov REGSTK,SP ; save SP mov SP,#REGSTK ; point stack to lower RAM push PSW push DPL push DPH push IE push IP ; Note: a race condition exists here! ; I should probably clear this much earlier. mov IE,#0x00h ; clear interrupt-enable mov PSW,R0 push ACC ; save 'A' for the moment mov DPTR,#SVDREG mov R0,#REGSTK ; initialize R0 to point to start of 'stack' entCP: mov A,@R0 movx @DPTR,A ; save register @R0 inc R0 inc DPTR cjne R0,#REGSTK+7,entCP ; saves all 6 SFRs pop ACC ; restore 'A' jb 0xD5h,bk1 ; if clear, no breakpt, else maybe breakpt mov A,#0x03 ; If not bkpt, put 1000 in for PCVAL mov B,#0x10 ; (1003-3=1000) ; now, we've managed to save almost everything ; still to save is the PC value. The PC is in B:A bk1: mov DPTR,#PCVAL add A,#-3 ; adjust PC value down by 3 mov R2,A ; save adjusted LSB in R2 movx @DPTR,A ; write adjusted LSB of PC mov A,B addc A,#-1 ; finish adjustment mov R3,A ; save adjusted MSB in R3 inc DPTR movx @DPTR,A ; write adjusted MSB of PC ; start normal routine here mov B,#0 ; B is used for "Is Breakpoint" flag. jnb 0xD5h,entrNB ; Don't even compare if not a bkpt entry mov DPTR,#BPAD ; check PC for breakpoint match movx A,@DPTR ; load LSB of breakpoint cjne A,002h,entrNB ; No match, not a breakpoint inc DPTR movx A,@DPTR ; load MSB of breakpoint cjne A,003h,entrNB ; No match, not a breakpoint acall clrbkp ; clear the breakpoint mov DPTR,#BRSTOP ; let user know we've stoped at the bkpt acall printc ; acall cC ; clear bkpoint address mov B,#1 sjmp entrFN ; finish up entry code entrNB: mov DPTR,#SVDREG ; Wasn't a breakpoint, fix saved stackptr movx A,@DPTR add A,#2 movx @DPTR,A clr A ; Point PCVAL to 0x1000h mov DPTR,#PCVAL movx @DPTR,A inc DPTR mov A,#0x10h movx @DPTR,A entrFN: mov DPTR,#ISBKP ; point to "Is Breakpoint" flag mov A,B movx @DPTR,A mov SP,#0xe0h ; initialize stack pointer to top of saved RAM parser: mov DPTR,#parser ; push parser's address push DPL ; so that internal routines call here push DPH parse: mov DPTR,#QUIET movx A,@DPTR mov 0x10h,A mov DPTR,#PROMPT acall printc ; print the prompt mov P2,#0x0Fh ; point P2 to monitor scratchpad page acall readln ; get an input line mov R1,#BUFF ; point R1 to buffer mov R2,#-4 ; clear command count movx A,@R1 ; grab input byte into R3 acall upcase ; make command uppercase mov R3,A inc R1 movx A,@R1 acall upcase ; make command uppercase mov R4,A parse0: mov DPTR,#clt ; point DPTR to command lookup tbl parse1: mov A,R2 add A,#4 mov R2,A movc A,@DPTR+A ; grab byte from lookup tbl jz parseE ; end of table cjne A,3,parse1 ; not equal, keep looking ; ok, I've found a match if I'm here. Offset into table is in R2 ; time to check for second letter: inc DPTR mov A,R2 movc A,@DPTR+A ; get second byte from lookup tbl jz parse2 ; no second letter, done parsing cjne A,4,parse0 ; doesn't match, keep looking ; found a double-letter command inc R1 ; found a single-letter command parse2: inc R2 mov A,R2 movc A,@DPTR+A push ACC inc R2 mov A,R2 movc A,@DPTR+A push ACC acall skipsp ; skip blanks acall getPC ; set default ARGV1 to be PCVAL acall atoh ; convert ARGV1 to hex mov ARGV1H,A ; store ARGV1 mov ARGV1L,B ; acall skipsp ; get to next arguement mov A,#0x00h ; mov B,#0x10h ; load a default ARGV2 parse3: acall atoh ; mov ARGV2H,A mov ARGV2L,B ; store argv2 mov A,ARGV1H mov A,ARGV1L mov A,ARGV2H mov A,ARGV2L ret ; this should jump to the selected routine parseE: mov DPTR,#ERROR acall printc sjmp parse ; readln ; ; uses R7 as free counter ; does not call routt until done with R7 readln: push DPH push DPL push 0 mov R1,#BUFF ; load buffer start location mov R0,#0xFFh mov DPTR,#0xFFFFh rd3: acall gett ; wait for input from term jc got djnz DPL,rd3 djnz DPH,rd3 ljmp lrd4 rd4: djnz R0,rd3 ss: lcall scrsave mov R1,#BUFF mov A,#CR got: cjne A,#BS,rd2 ; backspace hit? cjne R1,#BUFF,rd0 sjmp rd3 rd0: dec R1 ; backtrack pointer acall bsoutt ; backspace onscreen acall soutt acall bsoutt sjmp rd3 ; get next character rd2: cjne A,#CR,rd1 ; check for CR movx @R1,A ; store CR inc R1 acall routt ; print CR and LF clr A movx @R1,A ; append NULL pop 0 pop DPL pop DPH ret rd1: add A,#-0x20h jnc rd3 ; ignore control characters add A,#0x20h cjne R1,#(BUFF+MAXCHAR),rd5 ; check for end of buffer acall beloutt sjmp rd3 ; go back and wait for CR or BS rd5: acall outt movx @R1,A ; store in buffer inc R1 sjmp rd3 ; go back for more ; ; atoh converts a hex value in the input buffer into a value in A,B ; If there isn't a valid hex string at the current input buffer position, ; A and B are left untouched. This allows presetting a default in A,B. ; atoh: push 0x4h ; push R4 push 0x5h ; push R5 push ACC clr A mov R4,A ; mov R5,A ; intialize hex value movx A,@R1 ; get first character acall hexc ; convert to hex jc atoh3 ; valid character, continue pop ACC ; invalid char, restore A and abort sjmp atoh2 atoh3: mov A,R5 ; shift MSB value left 4 bits swap A mov R5,A ; store R5 mov A,R4 swap A ; swap nibbles of LSB value mov R4,A ; make a copy anl A,#0xFh ; this code relies on Bank 0 registers! :-/ orl 5,A ; complete the MSB value anl 4,#0xf0h ; clear out lsnibble of LSB value mov A,B ; get saved char orl 4,A ; complete the LSB value inc R1 ; increment pointer movx A,@R1 acall hexc ; convert to hex again jc atoh3 ; only two byte value atoh4: mov A,R5 mov B,R4 setb C pop 0x5h ; pop & throw away old A value atoh2: pop 0x5h pop 0x4h ret ; return with value loaded into A,B getPC: push DPL push DPH mov DPTR,#PCVAL movx A,@DPTR ; get LSB of PCVAL mov B,A inc DPTR movx A,@DPTR ; get MSB of PCVAL pop DPH pop DPL ret ;; ;; [H]elp command. Prints help text. ;; cH: mov DPTR,#HELPTX acall printc ret ;; ;; [T]erminal command. Enters into terminal mode ;; cT: mov DPTR,#TERM acall printc ; print mode cT2: acall getu ; get unix character jnc cT3 ; jump if unix char not available acall outt ; display character cT3: acall gett ; get from terminal keyboard jnc cT2 ; try again if nothing cjne A,#TERMESC,cTT ; ^T --> exit Terminal mode sjmp cT4 cTT: cjne A,#TERMDL,cT5 ; ^R --> download file acall cdl mov DPTR,#TERM sjmp cT2 ; begin again... cT5: acall outu ; send it to unix sjmp cT2 ; begin again cT4: mov DPTR,#MON ; print "Monitor mode" ajmp printc ; and return to commandline parser ; General subroutines for monitor ; ; ; ; Download routine. ; ; This routine download a user specified Intel-HEX format file. cdl: mov DPTR,#DLP acall printc ; print "Filename: " string mov P2,#0x0Fh acall readln ; get input mov R1,#BUFF acall skipsp ; skip spaces movx A,@R1 ; get first character cjne A,#CR,dl9 ; check for return sjmp dlexit ; exit, go back to unix prompt dl9: mov DPTR,#DL ; load download message dl3: clr A movc A,@A+DPTR ; send 'cat' message jz dl4 acall outu ; print a letter of 'cat' inc DPTR sjmp dl3 mov P2,#0x0Fh dl4: movx A,@R1 ; load filename jz dl5 acall outu ; print filename to monitor inc R1 sjmp dl4 dl5: acall gett cjne A,#ESC,dl10 ; escape hit? sjmp dlexit dl10: mov CKSUM,#0xffh ; set checksum dl6: acall getu jnc dl5 ; wait for a character from Unix or term cjne A,#':',dl5 ; see if start of line dl7: acall getbu ; get byte mov COUNT,A ; save count acall cks ; update checksum acall getbu ; get MSB of address mov ARGV1H,A ; save MSB mov CKERR,#0 ; check for a code conflict error cjne A,#0x0fh,noerr ; disallow writing to 0f00h page sjmp dlerr2 ; report the error noerr: acall cks ; update cheksum acall getbu ; get LSB of address mov ARGV1L,A ; store it acall cks ; update checksum acall getbu ; get packet-type code (0=data 1=end of HEX) cjne A,#0x00h,dlfnis ; check for end-of-HEX code acall cks ; update checksum mov DPL,ARGV1L mov DPH,ARGV1H ; load address of code into DPTR dl8: acall getbu ; get byte movx @DPTR,A ; store the byte acall cks ; update checksum inc DPTR djnz COUNT,dl8 ; if count not zero, get next byte acall getbu inc CKSUM ; add one to checksum cjne A,CKSUM,dlerr1 ; if not equal then print error mov A,#CR acall outt acall avoutt ; show user where we're loading sjmp dl5 dlfnis: acall cks ; update checksum acall getbu ; get checksum byte inc CKSUM ; add one cjne A,CKSUM,dlerr1 ; check for bad checksum mov DPTR,#DLDONE ; print successful message acall printc dlexit: ret dlerr1: mov DPTR,#DLERR acall printc sjmp dlexit dlerr2: mov DPTR,#DLROM acall printc sjmp dlexit ; subroutine clrbkp ; ; move the program bytes stored in DEBUG back to the ; the program bytes stored in the user's program ; ; the MSB of BPAD is in R3 and the LSB of BPAD is in R2 ; clrbkp: push 0 ; Save R0 mov P2,#DBGH ; Point P2 to page w/ debug info mov R0,#DBGL ; Point P2:R0 to excerpted code mov DPL,R2 mov DPH,R3 ; Point DPTR to code address cbkl: movx A,@R0 ; Grab excerpted code byte from @P2:R0 movx @DPTR,A ; Copy back to code to @DPTR inc DPTR ; move along inc R0 cjne R0,#DBGE,cbkl ; Break out when stepped past end of line pop 0 ret ; subroutine setbkp ; ; copy code bytes in program to DEBUG, and move the program bytes ; for 'lcall enter' into location pointed to DEBUG by BPAD. ; setbkp: mov R1,#0x12h ; opcode for 'lcall' mov R2,#benter/256 ; MSB of monitor breakpoint entry point mov R3,#benter&0FFh ; LSB of monitor breakpoint entry point mov P2,#BPAD/256 mov R0,#BPAD&0FFh movx A,@R0 mov DPL,A inc R0 movx A,@R0 mov DPH,A docpy: mov P2,#DBGH mov R0,#DBGL mov B,1 acall st mov B,2 acall st mov B,3 st: movx A,@DPTR ; copy code byte from RAM movx @R0,A ; save code byte @ DEBUG mov A,B ; prepare trap code byte movx @DPTR,A ; write trap code to RAM inc DPTR inc R0 ret ; subroutines for variably-addressed direct addressing. ; ; these routines rely on writing a 3-byte piece of self-modifying code ; to the location SLFMOD for reading directly-addressed memory. The ; set/clr breakpoint routines are {re|ab}used cleverly here. (Maybe too ; clever--you decide.) Because breakpoint code only needs to be saved ; in the breakpoint-save area *during external code execution*, the ; locations may be reused for other purposes when inside the monitor. ; ; This code uses these locations to save the memory overwritten by the ; self-modifying code. Routines which need to use getD should call ; startD first, and stopD when done. The cDB routine does this by ; calling startD at the top-of-loop, and stopD just before returning. ; ; startD: sets up the self-modifying code startD: push 0 pop 2 ; Move R0 into R2 mov R1,#0xE5h ; opcode for 'mov A,direct' mov R3,#0x22h ; opcode for 'ret' mov DPTR,#SLFMOD ; put self-modifying code at SLFMOD (01000h) sjmp docpy ; {re|ab}use the breakpoint save routine ; getB: gets a bit of bit-addressed memory using the self-modifying code ; written by startD. This code hijacks getD, so you cannot safely ; mix getB and getD calls. The bit is returned in C. getB: mov A,#0xA2h ; 'mov C,bit' sjmp putB1 ; putB: puts a bit of bit-addressed memory using the self-modifying code ; written by startD. This code hijacks getD, so you cannot safely ; mix getB and getD calls. The bit value is passed in C. putB: mov A,#0x92h ; 'mov bit,C' putB1: mov DPTR,#SLFMOD movx @DPTR,A ; getD: gets a byte of directly-addressed memory using the self-modifying ; code written by startD. getD: mov DPTR,#SLFMOD+1 ; point to "direct" byte mov A,R0 movx @DPTR,A ljmp SLFMOD ; call self-modifying code ; stopD: restores code overwritten by startD stopD: mov R2,#SLFMOD&0FFh ; setup pointer to area to be restored mov R3,#SLFMOD/256 sjmp clrbkp ; {re|ab}use the breakpoint clear routine ; cks - send a value in register A and it will subtract it ; from the CKSUM ; info in register B is destroyed cks: clr C ; clear borrow mov B,A ; save A mov A,CKSUM subb A,B ; subtract A from LSB mov CKSUM,A ; and save mov A,B ; get A back ret ; coutt coutt: push 0xe0h ; push A add A,#0xE0h ; compare A jb 0xe7h,nota ; check for legal ascii value add A,#0xA2h ; subtract 7eh jnb 0xe7h,nota pop 0xe0h acall outt ret nota: mov A,#0x2eh acall outt pop 0xe0h ret ; Note: dpoutt & avoutt trash A ; Write 4-nibble hex value in ARGV1 avoutt: mov A,ARGV1H acall aoutt mov A,ARGV1L sjmp aoutt ; Write 4-nibble hex value in DPTR dpoutt: mov A,DPH acall aoutt mov A,DPL sjmp aoutt a1outt: push ACC mov A,R4 anl A,#0xF0h jnz ao1 acall soutt pop ACC sjmp hxoutt ao1: pop ACC ; Write 2-nibble hex value in A aoutt: swap A ; Put MS-nibble in lower half acall hxoutt ; Output MS-nibble swap A ; Output LS-nibble of A hxoutt: push ACC anl A,#0x0fh ; Output LS-nibble of accumulator push DPL push DPH mov DPTR,#hexl movc A,@DPTR+A acall coutt pop DPH pop DPL pop ACC ret ; A and B are used as free registers ; getbu returns the next byte in the hex file ; in A ; all information in A and B is destroyed getbu: acall gett ; check for char from terminal jc buesc ; check for escape bu1: acall getu ; get char from unix jnc getbu ; go back if nothing there acall hexc ; convert to hex jnc buabrt ; get out if error mov A,B ; destroy A info swap A ; shift up a nibble mov TEMP,A ; save MSnibble bu4: acall getu ; wait for next next char jnc bu4 acall hexc ; convert to hex jnc buabrt ; leave if error mov A,TEMP orl A,B ; form full value setb C bu3: ret buesc: cjne A,#ESC,bu1 clr C buabrt: pop ACC pop ACC ajmp dlexit ; abort download in an unfriendly manner getu: push DPL push DPH mov DPTR,#USTAT ; get port status movx A,@DPTR ; mov C,0xe1h ; use carry as flag jnb 0xe1h,getu2 ; char ready? dec DPL ; DPTR = UDATA movx A,@DPTR ; clr 0xE7h ; clear parity bit getu2: pop DPH pop DPL ret ; subroutine gett ; ; this subroutine looks ONCE to see if a character is ready gett: push DPL push DPH mov DPTR,#TSTAT ; get port status movx A,@DPTR ; read port status mov C,0xe1h ; C=0 means no char, C=1 means char jnb 0xe1h,gett2 ; leave if char not ready dec DPL movx A,@DPTR ; clr 0xe7h ; mask high order bit gett2: pop DPH pop DPL ret ; ; subroutine waitt ; ; this subroutine will wait until a character is hit ; waitt: push DPL push DPH mov DPTR,#TSTAT wt2: movx A,@DPTR ; get port address jnb 0xe1h,wt2 ; is A != 0x2h (character ready?) dec DPL ; movx A,@DPTR ; get char when ready pop DPH pop DPL ret ; ride, Sally. ride. outu: push DPL push DPH push 0xe0h mov DPTR,#USTAT outu2: movx A,@DPTR jnb 0xe0h,outu2 ; jmp if unix not ready pop 0xe0h dec DPL movx @DPTR,A ; send character to unix pop DPH pop DPL ret outt: cjne A,0x10h,outt0 ret outt0: push DPL ; 0x83h ; push DPH push DPH ; 0x82h ; push DPL push 0xe0h ; push A mov DPTR,#TSTAT outt2: movx A,@DPTR jnb 0xe0h,outt2 pop 0xe0h mov DPTR,#TDATA movx @DPTR,A pop DPH ; 0x82h pop DPL ; 0x83h ret ; space maker msoutt: acall soutt ; output 4 spaces acall soutt acall soutt soutt: mov A,#SPC sjmp outt ; output space beloutt: mov A,#BELL sjmp outt ; output 2 backspaces bs2outt: acall bsoutt ; output 1 backspace bsoutt: mov A,#8 sjmp outt routt: push 0xe0h ; push A mov A,#CR ; A is scrambled acall outt ; load return and output it mov A,#LF acall outt ; load linefeed and output it acall dly ; delay for viewpoint pop 0xe0h ret printc: clr A movc A,@A+DPTR ; get character jz pt1 acall outt ; print character inc DPTR sjmp printc pt1: ret ; leave ; delay ; dly: mov 0x17h,#0x06h dly2: mov 0x16h,#0 dly1: djnz 0x16h,dly1 djnz 0x17h,dly1 ret ; ; skip spaces in input ; skipsp: movx A,@R1 ; load character in buffer to a cjne A,#SPC,sk1 inc R1 sjmp skipsp sk1: ret ; ; convert characters to uppercase ; upcase: push 0xe0h ; push A add A,#-97 ; check for less than 'a' jb 0xe7h,up2 ; add A,#-25 jnb 0xe7h,up2 add A,#90 dec SP sjmp up3 up2: pop 0xe0h ; push A up3: ret ; hexc: receives an ascii character in the accumulator (A) and ; returns the converted number in the B register (B) ; ; if carry is set, the number is valid ; if carry is clear, the number is not valid hexc: acall upcase ; add A,#-48 ; bring down number jb 0xe7h,hexc3 ; invalid data mov B,A ; add A,#-10 ; is it a number? jb 0xe7h,hexc2 ; if true, A is a number from 0-9 mov A,B add A,#-7 ; bring down to a letter mov B,A ; add A,#-10 ; is it too low again jb 0xe7h,hexc3 ; not a number add A,#-6 ; jb 0xe7h,hexc2 ; a valid number from A-F hexc3: clr C clr A ret ; done 4/17/95 hexc2: setb C mov A,B ret ; ; set Breakpoint ; cB: ; 1) Check for illegal bkpt ; 2) Remove any existing bkpt ; 3) Set bkpt location mov A,ARGV1H ; 1) check for illegal bkpt add A,#-0x10h ; illegal bkpt = bkpt less that 0x1000h jc cB1 mov DPTR,#BRROM ; print error acall printc sjmp cB2 cB1: mov DPTR,#BRSET acall printc ; let user know what's going on. acall avoutt mov DPTR,#BPAD ; 3) Set bkpt location mov A,ARGV1L ; get starting address LSB acall a2dptr mov A,ARGV1H ; get starting address MSB acall a2dptr mov A,ARGV1L acall routt cB2: ret ; ; Clear breakpoint ; cC: clr A mov DPTR,#BRCLR ; Tell user "Breakpoint cleared" acall printc mov DPTR,#BPAD acall a2dptr ; Clear breakpoint address acall a2dptr ;fall through to a2dptr ; Clear 'Is Breakpoint?' flag ; Store A @DPTR, inc DPTR a2dptr: movx @DPTR,A inc DPTR ret ; memory display routines ;; ;; Display eXternal memory ;; ; Display format: ; ; 3210: 30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 46 0123456789ABCDEF cDarg: anl ARGV1L,#0xf0h ; start on an even address mov A,ARGV2L anl A,#0xfh ; see if count is XXX0xh jz cDarg2 mov A,ARGV2L ; add A,#0x10h ; make count XXX0xh jnc cDarg3 inc ARGV2H sjmp cDarg3 cDarg2: mov A,ARGV2L ; load the LSB of count into A cDarg3: swap A ; swap nibbles anl A,#0xfh ; (shift right four places) mov ARGV2L,A ; save the shift mov A,ARGV2H ; swap A ; mov B,A ; anl A,#0xf0h ; shift ARGV2 (count) left four bits orl ARGV2L,A ; mov A,B ; anl A,#0xfh ; mov ARGV2H,A ; setb C ; so far, so good jnz cDarg0 ; mov A,ARGV2L jnz cDarg0 ; clr C cDarg0: inc ARGV2H ; adjust ARGV2H (since all "cD" rtns use djnz) ret cDX: acall cDarg jc cDskip ret cDskip: acall gett ; check for esc hit cjne A,#ESC,cDX8 ret ; esc hit, so leave cDX8: cjne A,#CR,cDX9 ; acall waitt cDX9: acall routt cDX7: mov DPL,ARGV1L mov DPH,ARGV1H acall dispX mov B,#0x10h cDX5: movx A,@DPTR ; print a line of memory values acall aoutt acall soutt mov A,B cjne A,#0x09h,cDX10 acall soutt cDX10: inc DPTR djnz B,cDX5 acall msoutt ; four spaces mov DPL,ARGV1L ; reload beginning address mov DPH,ARGV1H mov B,#0x10h ; reload counter cDX6: movx A,@DPTR ; print the ascii of the memory values acall coutt inc DPTR djnz B,cDX6 acall routt ; carriage return clr C mov A,ARGV1L ; addc A,#0x10h ; mov ARGV1L,A ; mov A,ARGV1H ; addc A,#0x0h ; mov ARGV1H,A ; add 16 to the address clr C djnz ARGV2L,cDX7 ; djnz ARGV2H,cDX7 ; acall routt ; finish cDX4: ret ; displays external address as "XXXX: " dispX: acall dpoutt mov A,#':' acall coutt ; put a colon after address ajmp soutt ; put space after colon ;; ;; Display Internal memory ;; ; Display format: ; ; I 10: 30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 46 0123456789ABCDEF cDI: push 0 mov ARGV1H,#0 mov ARGV2H,#0 acall cDarg jc cDIskp mov ARGV2L,#0x10 ; if ARGV2 (count) is zero, dump 0x100 bytes cDIskp: acall gett ; check for esc hit jnc cDI7 cjne A,#ESC,cDI8 sjmp cDI4 ; esc hit, so leave cDI8: cjne A,#CR,cDI9 ; acall waitt cDI9: acall routt cDI7: mov R0,ARGV1L acall dispI ; display "I xx: " mov B,#0x10h cDI5: acall grabI ;mov A,@R0 ; print a line of memory values acall aoutt acall soutt mov A,B cjne A,#0x09h,cDI10 acall soutt cDI10: inc R0 djnz B,cDI5 acall msoutt ; four spaces mov R0,ARGV1L ; reload beginning address mov B,#0x10h ; reload counter cDI6: acall grabI ;mov A,@R0 ; print the ascii of the memory values acall coutt inc R0 djnz B,cDI6 acall routt ; carriage return mov A,ARGV1L ; add A,#0x10h ; mov ARGV1L,A ; djnz ARGV2L,cDIskp ; go back if not done acall routt ; finish cDI4: pop 0 ret dispB: mov A,#'B' sjmp dispI0 ; display Internal address in R0 ; (trashes A) dispI: mov A,#'I' ; dispI0: acall outt ; Put "I " before addres. acall soutt ; mov A,R0 acall aoutt mov A,#':' acall coutt ; put a colon after address ajmp soutt ; put space after colon ;; ;; Display Bit-addressable memory ;; ; Display format: ; > DB 0 20 ; B 00: 0 0 1 1 0 1 1 0 0 0 1 1 0 0 0 1 ; B 10: 0 1 0 0 0 0 0 1 0 1 1 0 0 0 0 1 cDB: push 0 push 1 acall startD mov ARGV1H,#0 mov ARGV2H,#0 acall cDarg jc cDBskp mov ARGV2L,#0x10h ; default argv2 == 0x100 cDBskp: acall gett ; check for esc hit jnc cDB7 cjne A,#ESC,cDB8 sjmp cDB4 ; esc hit, so leave cDB8: cjne A,#CR,cDB9 ; acall waitt cDB9: acall routt cDB7: mov A,ARGV1L ; get bit address into A mov R1,A ; save bit address into R1 mov R0,A acall dispB mov A,R1 acall bt2d mov R0,A ; put direct address of bit mem into R0 mov B,#0x10h ;mov A,@R0 ; load a byte of bit-addressable RAM acall getD ; load a byte of bit-addressable RAM cDB5: rlc A ; move topmost bit of byte into A push 0xE0h ; save rotated A acall soutt ; output a space clr A ; "image" a 0 or 1 into A addc A,#'0' acall coutt ; display the 0 or 1 in A acall soutt ; output a space inc R1 mov A,B cjne A,#0x09h,cDB10 ; See if we've output 8 bits yet. acall soutt ; y: out another space and get another byte pop 0xE0h ; throw away current saved A mov A,R1 ; get current bit position acall bt2d ; convert to valid byte address mov R0,A ; save new indirect address mov A,@R0 ; load a byte of bit-addressable RAM push 0xE0h ; push new A onto stack cDB10: pop 0xE0h ; pop current save A for next iter djnz B,cDB5 acall routt ; carriage return mov A,ARGV1L ; add A,#0x10h ; mov ARGV1L,A ; djnz ARGV2L,cDBskp ; go back if not done acall routt ; finish cDB4: acall stopD ; clean up after self-modifying code pop 1 pop 0 ret ; convert a bit address into an direct address: bt2d: jb 0xE7h,b2sfr ; uhoh, trying to disp SFR anl A,#0x78 ; mask off the bits we don't want rr A rr A rr A ; divide bit addr by 8 to get byte addr orl A,#0x20 ; add offset ret b2sfr: anl A,#0xF1 ; This turns SFR bit addresses into *direct* ; addresses. ret ;; User interface format: ; ; Location: Value (as in XXXX: VV) ; ; As user types new value, old value is left-shifted and new LSnibble comes ; in. [ENTER] moves to the next memory location. Values are committed ; "automatically." [ESC] or [SPACE] ends the memory examining operation. ;; ;; Examine/modify eXternal memory ;; cEX: mov DPL,ARGV1L mov DPH,ARGV1H mov R4,#0xFFh cEX1: acall dispX ; Display DPTR movx A,@DPTR ; Grab byte of RAM acall cEE ; Let user edit value, or proceed movx @DPTR,A inc DPTR jc cEX1 ret ;; ;; Examine/modify Internal memory ;; cEI: mov R0,ARGV1L ; get internal address into R0 cEI0: mov R4,#0xFFh ; set mask to 0FFFh cEI1: cEI2: acall dispI acall grabI ;mov A,@R0 ; Grab byte of RAM acall cEE ; Let user edit value, or proceed acall putI ;mov @R0,A inc R0 jc cEI1 ret ; ; grabI: grab byte of Internal RAM, transparently redirecting accesses ; to locations we've saved externally. ; ; Call with R0=location, returns value in A grabI: push PSW push DPL push DPH push 0 acall cvtI ; convert Internal jc gI1 ; if C=0: internal, C=1: external & DPTR=address mov A,@R0 sjmp gIdone gI1: movx A,@DPTR gIdone: pop 0 pop DPH pop DPL pop PSW ret ; ; putI: put byte of Internal RAM, transparently redirecting accesses ; to locations we've saved externally. ; ; Call with R0=location, writes A to that location putI: push PSW push DPL push DPH push 0 acall cvtI ; convert Internal jc pI1 ; if C=0, internal, C=1, external mov @R0,A sjmp pIdone pI1: movx @DPTR,A pIdone: pop 0 pop DPH pop DPL pop PSW ret ; cvtI: converts an Internal address, transparently redirecting accesses ; to saved internal RAM to the save-area in external RAM ; ; Locations 0xE0h-0xFFh are in 0x0F00h-0x0F1Fh ; Locations 0x01h-0x1fh are in 0x0F21h-0x0F3Fh ; Location 0 is in R0SAVE ; ; Call with address in R0. ; Returns C=0 if new address is internal. R0 points to correct location. ; Returns C=1 if new address is external. DPTR points to correct location. cvtI: push ACC push 1 mov A,R0 jz cvt0 ; is address==0? Redirect to R0SAVE add A,#0x20h ; shift internal address a little mov R1,A anl A,#0x3Fh cjne A,1,cvtII cvtX1: mov DPH,#SVDBLK/256 mov DPL,A sjmp cvtX ; return w/ carry set cvtII: clr C sjmp cvtD cvt0: mov DPTR,#R0SAVE cvtX: setb C cvtD: pop 1 pop ACC ret ;; ;; Examine/modify Bit-addressible memory ;; cEB: acall startD mov R0,ARGV1L ; get internal address into R0 mov R4,#0x01h ; set mask to 01h cEB1: mov A,R0 jb 0xE7,cEB2 ; don't let user edit SFRs acall dispB acall getB clr A mov 0xE0,C acall cEE ; Let user edit value, or proceed push PSW mov C,0xE0 acall putB pop PSW inc R0 jc cEB1 ajmp stopD cEB2: acall stopD mov DPTR,#EBERR ; "Cannot edit ... from 0x80-0xFF" ajmp printc ;; ;; Let user edit byte of RAM. (common to all 3 Examine routines) ;; ; inputs: ; A == value to edit ; R4 == mask value (edited value is ANDed against this) ; outputs ; A == edited value ; R2,R3 trashed cEE: mov R3,A ; Copy byte to R3 for safe keeping acall a1outt ; Show byte of RAM cE1a: acall waitt ; Wait for a character cjne A,#0x1bh,cE0a ; Is character an ESC, Space, or Enter? sjmp cE0c cE0a: cjne A,#0x20h,cE0b ; No, continue sjmp cE0c cE0b: cjne A,#0x0Dh,cE2 cE0c: push ACC ; save keypress acall routt ; print a newline pop ACC ; restore keypress clr C ; clear carry cjne A,#0x0Dh,cE0 ; If it wasn't Enter, leave. setb C cE0: mov A,R3 ret cE2: acall hexc ; Convert input from hex jnc cE3 ; ring bell on error mov R2,A ; save input mov A,R3 ; get "current value" swap A ; shift left by 4 anl A,#0xF0h ; bringing in 0's in lower nibble orl A,R2 ; logical-OR with input anl A,R4 ; mask against input mask (usu 0FFh or 001h) mov R3,A ; save new "current value" acall bs2outt mov A,R3 acall a1outt sjmp cE1a cE3: acall beloutt sjmp cE1a ;; ;; Examine/modify registers ;; cR: ; step through registers one at a time: ; SP, PSW, DPL, DPH first ; A, and B next ; then R0 through R7 in the "current" bank mov R4,#0xFFh ; Set edit mask to 0xFFh mov R1,#0 ; start counter @ 0 mov DPTR,#SVDREG ; point to the saved-registers area cR1: acall dispR ; display register name movx A,@DPTR ; grab byte from saved-register area acall cEE ; let user edit byte movx @DPTR,A ; write back edited byte jnc cRdn inc DPTR inc R1 cjne R1,#0x06,cR2a sjmp cR2b cR2a: cjne R1,#0x04,cR1 mov DPTR,#ASAVE sjmp cR1 cR2b: mov DPTR,#SVDREG+1 movx A,@DPTR anl A,#0x18 mov R1,#0 mov R0,A cR3: acall dispRn acall grabI acall cEE acall putI jnc cRdn inc R0 inc R1 cjne R1,#0x08h,cR3 acall routt cRdn: ret ; Displays register name enumerated in R1 ; ; 0x00-0x05: SP PSW DPL DPH A B ; ; format: "[REG]: " dispR: acall dr mov A,R1 rl A rl A push DPH push DPL mov DPH,#REGS/256 add A,#REGS&255 mov DPL,A acall printc pop DPL pop DPH sjmp dispRd dr: mov A,#'[' ajmp outt ; Displays register names R0-R7, as enumerated in R1 ; ; dispRn: acall dr acall soutt mov A,#'R' acall outt mov A,#'0' add A,R1 acall outt dispRd: mov A,#']' acall outt mov A,#':' acall outt ajmp soutt ;; ;; Toggle "Quiet Mode" ;; cQchk: setb C sjmp cQ0 cQ: clr C cQ0: mov DPTR,#QUIET movx A,@DPTR cjne A,#0xAAh,cQnxt sjmp cQtog cQnxt: cjne A,#0x07h,cQerr cQtog: jc cQdone xrl A,#0xADh movx @DPTR,A push ACC mov DPTR,#QMSG acall printc pop 0 mov A,#'n' cjne R0,#0xAA,cQon mov A,#'f' acall coutt cQon: acall coutt ajmp routt cQerr: mov A,#0xAAh movx @DPTR,A cQdone: ret ;; ;; Go to executable code ;; cG: acall setbkp ; restore all saved registers clr 0xD3h ; set PSW for Bank 0 clr 0xD4h ; Bank 0 --> R0-R7 are in 00h-07h mov DPTR,#ISBKP movx A,@DPTR mov B,A jnz gRSM mov DPTR,#GJUMP sjmp gMSG gRSM: mov DPTR,#GRSM gMSG: acall printc acall avoutt acall routt ; restore first half of saved memory (0xE0h-0xFFh) ; ( the user program's stack may be in that area ) mov DPTR,#SVDBLK ; pull the saved internal RAM mov R0,#SVSTART ; point to start of saved area g1: movx A,@DPTR ; get saved value mov @R0,A ; write it to internal RAM inc DPTR inc R0 cjne R0,#0,g1 ; loop until it's all copied back ; restore the saved registers mov DPTR,#SVDREG mov R0,#REGSTK ; initialize R0 to point to "register stack" movx A,@DPTR mov R1,A g2: mov @R0,A movx A,@DPTR ; restore register @R0 inc R0 inc DPTR cjne R0,#REGSTK+7,g2 ; restore 6 internal registers mov SP,R1 ; get saved SP mov A,B jnz gBK mov DPTR,#enter ; push entry point of monitor push DPL push DPH gBK: push ARGV1L ; push address of user's routine push ARGV1H mov A,SP ; save the new SP to A mov SP,#REGSTK+6 mov P2,#P2SAVE ; restore P2 from P2SAVE pop IP pop IE pop DPH pop DPL pop PSW ; this may change our register bank mov SP,A ; get saved, updated SP from A push DPL ; save the DPTR we just restored push DPH push PSW ; and PSW clr 0xD3h ; set PSW for Bank 0 clr 0xD4h ; Bank 0 --> R0-R7 are in 00h-07h ; restore the second half of saved memory (0x01-0x1Fh) mov DPTR,#SVDBLK+0x21h mov R0,#1 g3: movx A,@DPTR ; grab a byte mov @R0,A ; save it inc DPTR inc R0 cjne R0,#SVEND,g3 mov DPTR,#R0SAVE ; get the saved value for R0 movx A,@DPTR ; retrieve R0 mov R0,A dec DPL movx A,@DPTR ; retrieve B mov B,A ; store B dec DPL movx A,@DPTR ; store A pop PSW pop DPH pop DPL ret ; jump to the user's routine main: mov IE,#0x00h mov SP,#0x80h ; initialize stack pointer to ; upper 128 bytes of RAM acall initp ; initialize UARTs lcall cC ; clear breakpoint lcall cQchk ; init "quiet-mode" toggle mov DPTR,#OPEN lcall printc ; print opening message mov A,#0xffh ; The "entry point" routine expects a PC value on the stack. ; This PC value is compared against the break point value ; currently set up. The breakpoint shouldn't ever point to ; us here in ROM. :-) Knowing this address could make for ; some fun, undocumented behavior, I guess. Anyway, if an ; attempt is made to "resume execution at breakpoint," with ; this address as the breakpoint, then nothing profound will ; happen--business as usual should commence. ie. the user ; should get his prompt back. :-) BTW, setting this address ; as the breakpoint address would require external code, or ; editing memory manually anyway. :-) ; At the very least, this lcall properly pre-massages the stack. eloop: lcall enter ; cute way to get into 'entry point' sjmp eloop initp: ; delay to wait for UARTs to come out of reset. ; total delay == ((256*24)+24)*160 + 24 == 986904 cycles ; or approx 0.16 second at 6MHz. mov 0x7h,#0xA0h ; loop 0A000h times initd: lcall dly2 mov P2,#0xFFh ; should be 0FFh for all I/O stuff clr A mov R0,A ; clear keypad acall outp1 mov R0,#BAUDS&0FFh mov A,#BAUD ; set baud to 9600 acall outp1 mov R0,#TSTAT&0FFh mov R1,#USTAT&0FFh acall outp2 mov A,#0x40h ; reset ports acall outp2 mov A,#0x6eh ; write the mode acall outp2 mov A,#0x15h ; write control word outp2: movx @R1,A outp1: movx @R0,A ret ;; ;; text and ASCII assignments ;; PROMPT: .byte CR,LF,"> ",NULL OPEN: .byte FF,CR,LF .byte "805x Monitor (Fri 13th)" .byte CR,LF,CR,LF .byte "Hardware: E. Johnson, J. Siegle 1994-95" .byte CR,LF .byte "Software: J. Siegle, J. Zbiciak 1995-96" .byte CR,LF,LF .byte "H for HELP" .byte CR,LF,NULL TERM: .byte CR,LF .byte "Terminal Mode" .byte CR,LF .byte "(^T exits)" .byte CR,LF,LF,NULL MON: .byte CR,LF .byte "Monitor Mode" .byte CR,LF,NULL ERROR: .byte CR,LF .byte "H for HELP" .byte BELL,NULL HELPTX: .byte FF .byte "Help:" .byte CR,LF,LF .byte "D[XIB] a l Display locations starting at ",CR,LF .byte " (X -- eXternal I -- Internal B -- Bit-addressed)",CR,LF .byte "E[XIB] a Examine/modify memory starting at ",CR,LF .byte "R Examine/modify registers",CR,LF .byte "B addr Set breakpoint",CR,LF .byte "C Clear breakpoint",CR,LF .byte "G addr Execute code at ('G'oto) ",CR,LF .byte "Q Toggle 'Q'uiet mode",CR,LF .byte "T Enter terminal mode",CR,LF .byte " ^",TERMESC+64," Return to monitor mode",CR,LF .byte " ^",TERMDL+64, " Download a .HEX file",CR,LF,LF .byte "Note: All params are in hex. Interrupt vectors occupy 0x1003 to 0x1032",CR,LF .byte NULL REGS: .byte " SP", NULL .byte "PSW", NULL .byte "DPL", NULL .byte "DPH", NULL .byte " A", NULL .byte " B", NULL hexl: .byte "0123456789ABCDEF" DL: .byte "cat " .byte NULL DLERR: .byte CR,LF .byte "Bad checksum, or other error.",BELL .byte CR,LF,NULL DLROM: .byte CR,LF .byte "Ext. RAM 0F00h-0FFFh is used by the monitor." .byte CR,LF,NULL DLDONE: .byte CR,LF .byte "Done." .byte CR,LF,NULL DLP: .byte CR,LF .byte "Filename: " .byte NULL GJUMP: .byte "Executing at ", NULL GRSM: .byte "Resuming at ", NULL BRSTOP: .byte "Breakpoint reached. Next 'G' command will resume code.",CR,LF .byte NULL BRCLR: .byte "Cleared breakpoint.",CR,LF .byte NULL BRSET: .byte "Set breakpoint at " .byte NULL BRROM: .byte "Error: Code space 0000h-0FFFh is in ROM." .byte CR,LF,BELL,NULL EBERR: .byte "Error: Cannot edit bit-addressed memory between 0x80-0xFF" .byte CR,LF,BELL,NULL QMSG: .byte "Quiet mode is now o",NULL ; Command Lookup Table clt: .byte "B",0 , cB&0FFh, cB/0100h .byte "C",0 , cC&0FFh, cC/0100h .byte "DX" , cDX&0FFh, cDX/0100h .byte "DI" , cDI&0FFh, cDI/0100h .byte "DB" , cDB&0FFh, cDB/0100h .byte "D",0 , cDX&0FFh, cDX/0100h .byte "EX" , cEX&0FFh, cEX/0100h .byte "EI" , cEI&0FFh, cEI/0100h .byte "EB" , cEB&0FFh, cEB/0100h .byte "E",0 , cEX&0FFh, cEX/0100h .byte "G",0 , cG&0FFh, cG/0100h .byte "H",0 , cH&0FFh, cH/0100h .byte "R",0 , cR&0FFh, cR/0100h .byte "T",0 , cT&0FFh, cT/0100h .byte "?",0 , cH&0FFh, cH/0100h .byte "Q",0 , cQ&0FFh, cQ/0100h .byte "Z",0 , 0xFDh, MSTART/256+0x0Fh .byte CR,0 , parse&0FFh, parse/0FFh .byte 0,0,0,0 .org MSTART+00003h ; IE0 interrupt vector ljmp IE0V .org MSTART+00006h ; Alternate monitor entry point ljmp enter .org MSTART+0000bh ; TF0 interrupt vector ljmp TF0V .org MSTART+00013h ; IE1 interrupt vector ljmp IE1V .org MSTART+0001bh ; TF1 interrupt vector ljmp TF1V .org MSTART+00023h ; RI+TI interrupt vector ljmp RIVTIV .org MSTART+0002bh ; TF2 interrupt vector ljmp TF2V ; ; Screen saver easter egg. If the computer idles for more than 13.4261 ; minutes at the prompt, bring this up. ; ; .org MSTART+0x0e00h MSG1: .byte " Hello World! Joe Zbiciak says 'Hi!' ",0 MSG2: .byte " Well, do something already! ",0 MSG3: .byte " Yawn. ",0 MSG4: .byte " I'm watching you.... ",0 MLST: .byte MSG1&255,MSG2&255,MSG3&255,MSG4&255 lrd3: lcall gett ; wait for input from term jc lgot djnz DPL,lrd3 djnz DPH,lrd3 ljmp rd4 lrd4: djnz R0,lrd3 ljmp ss lgot: ljmp got curpos: mov A,#0x1Bh acall outtv mov A,#0x59h acall outtv mov A,#0x20h add A,R2 acall outtv mov A,#0x20h add A,R3 ajmp outtv scrsave: setb 0xD4h setb 0xD3h mov R4,A anl A,#0x17h mov R2,A mov A,#FF acall outtv scr1: mov R1,#0 scr2: mov R5,#79 mov DPTR,#MLST mov A,R1 movc A,@DPTR+A mov DPH,#MSG1/256 mov DPL,A mov R3,#77 scrlp: acall curpos acall prints acall gettv jc scrdone dec R5 djnz R3,scrlp scrlp2: mov R3,#0 acall curpos acall prints acall gettv jc scrdone dec R5 cjne R5,#-0x30,scrlp2 mov A,R4 add A,#0xA5h mov R4,A anl A,#0x17h mov R2,A inc R1 cjne R1,#4,scr2 sjmp scr1 scrdone: clr 0xD4h clr 0xD3h mov A,#FF ajmp outtv prints: push 0x1Bh push 0x1Dh mov R6,#0 mov A,R5 jnb 0xE7,printp printnl: mov A,R6 movc A,@DPTR+A jz printD inc R6 inc R5 cjne R5,#0,printnl printp: mov A,R6 movc A,@DPTR+A jz printD acall outtv inc R6 inc R5 inc R3 cjne R3,#79,printp printD: mov R3,#0x30 mov R5,#0 prdly: djnz R5,prdly djnz R3,prdly pop 0x1Dh pop 0x1Bh ret ; ; Jump table, to allow user routines access ; .org MSTART+0x0FE0h outt0v: ljmp outt0 outtv: ljmp outt gettv: ljmp gett outuv: ljmp outu getuv: ljmp getu .org MSTART+0x0FFDh ljmp scrsave