;***************************************************************; ;***************************************************************; ;** Monitor for the 8051 Microcontroller Family **; ;** UW ECE 453 Computer Labs **; ;** (c) 1997 University of Wisconsin **; ;** **; ;** History: **; ;** 2.0 First public release Kevin Hugo May 97 **; ;***************************************************************; ;***************************************************************; ;##### BEGIN OLD CODE #####; ;**01234567890123456789012345678987654321098765432109876543210**; ;*** Internal memory map ***; ; Register usage ; Bank 0 -- Monitor ; Bank 1 -- Interrupts ; Bank 2 -- Unused ; Bank 3 -- User programs ; Bit addressable area ; Entirely reserved for user for bit and byte variables. ; Stack ; Initiaized and reserved for 0x30 to 0xff (beyond bit-addressable area). ;*** External and code memory map ***; ; Note: External memory is only necessary if you are going to ; download programs. In addition, you may choose to add ; memory-mapped parts. In order for the download routine to work, ; the external memory map and code memory map must be the same. ; You should functionally OR (that is, physically AND) together /RD ; and /PSEN. ; ; 0x0000 to 0x0fff Interrupt vectors and monitor code. ; 0x1000 to 0x1fff Utility routines. ; 0x2000 to 0x3fff Typically external EPROM. ; 0x2000 User interrupt and menu routines. ; 0x4000 to 0x7fff Typically external RAM. ;##### END OLD CODE #####; ;***************************************************************; ;** Constants, Variables, and Stack **; ;***************************************************************; MONR0: equ 0x00 ; Monitor's R0 MONR1: equ 0x01 ; Monitor's R1 MONR2: equ 0x02 ; Monitor's R2 MONR3: equ 0x03 ; Monitor's R3 MONR4: equ 0x04 ; Monitor's R4 MONR5: equ 0x05 ; Monitor's R5 MONR6: equ 0x06 ; Monitor's R6 MONR7: equ 0x07 ; Monitor's R7 ;***************************************************************; ;** Exception Vectors **; ;***************************************************************; org 0x0000 ; Reset vector ljmp reset org 0x0003 ; External interrupt 0 vector ljmp USREI0 org 0x000b ; Timer 0 overflow vector ljmp USRTF0 org 0x0013 ; External interrupt 1 vector ljmp USREI1 org 0x001b ; Timer 1 overflow vector reti ; (Used by serial port) org 0x0023 ; Serial port interrupt vector push PSW ; Save the flags lcall uartintr ; Service the interrupt ; If we got a break, ; Stop the program pop PSW ; Restore the flags reti ; Return from interrupt ;***************************************************************; ;** Jump Table **; ;***************************************************************; org inchar ; Input a character ljmp _inchar org inhex ; Input a hexadecimal value ljmp _inhex org outchar ; Output a character ljmp _outchar org outnib ; Output 4-bit hexadecimal value ljmp _outnib org outbyte ; Output an 8-bit hexadecimal value ljmp _outbyte org outword ; Output a 16-bit hexadecimal value ljmp _outword org outstr ; Output a null-terminated string ljmp _outstr ;***************************************************************; ;** Reset Code **; ;***************************************************************; reset: mov PSW, #0 ; Clear flags, use register set 0 mov SP, #STACK ; Initialize stack ;##### BEGIN OLD CODE #####; mov R0, #0x00 ; Initialize the default address mov R1, #0x40 ;##### END OLD CODE #####; lcall uartinit ; Initialize the UART mov DPTR, #sbanner ; Print banner lcall _outstr ; Drop into monitor ;***************************************************************; ;** Monitor Main Loop **; ;***************************************************************; monitor: mov DPTR, #sprompt ; Output the monitor prompt lcall _outstr mon1: lcall _inchar ; Get next character IF 0 mov B, A ; Save the character clr C subb A, #" " ; If it is a space or less, jc mon1 ; ignore it jz mon1 mov A, B ; Else restore it ELSE cjne A, #" ", mon2 ; If it is a space or less, sjmp mon1 ; Ignore it mon2: jc mon1 ENDI ;***************************************************************; ;** Download Intel Hex File **; ;***************************************************************; ; This is the Intel hex file format. ; Data lines are of the form ; :nn aaaa tt dd...dd cc ; :nn aaaa 00 dd...dd cc ; End records are of the form ; :00 ; :00 aaaa 01 cc ; Segement definition lines (invalid on the 8051) are of the form ; :nn 0000 tt aaaa cc ; :02 0000 02 aaaa cc ; Where ; nn is the number of data bytes (stored in R0) ; aaaa is the address (high byte in R1, low byte in R2) ; tt is the record type (stored in R3) ; dd...dd is the data bytes ; cc is the checksum (accumulated in R4) cjne A, #":", nintdown ; Is the command a colon? intdown: lcall inrbyte ; Then get the download byte count mov R0, A ; Store it in R0 mov R4, A ; Initialize the checksum lcall inrbyte ; Get the high address byte mov R1, A ; Store it in R1 add A, R4 ; Add it to the checksum mov R4, A lcall inrbyte ; Get the low address byte mov R2, A ; Store it in R2 add A, R4 ; Add it to the checksum mov R4, A lcall inrbyte ; Get the record type mov R3, A ; Save it in R3 add A, R4 ; Add it to the checksum mov R4, A cjne R3, #00, down2 ; If it is 0, mov DPL, R0 ; Set DPTR mov DPH, R1 down1: lcall inrbyte ; Get a data byte movx @DPTR, A ; Save it inc DPTR ; Bump DPTR add A, R4 ; Add the byte to checksum mov R4, A djnz R0, down1 ; Repeat for all the bytes lcall inrbyte ; Get the checksum add A, R4 ; Add it to our count jnz down3 ; And report any errors ljmp mon1 ; Return to monitor (silently) down2: cjne R3, #01, down3 ; Else if it is 1, lcall inrbyte ; Get the checksum add A, R4 ; Add it to our count jnz down3 ; And report any errors mov DPTR, #sdok ; Else report success lcall _outstr ljmp monitor ; Return to monitor down3: mov DPTR, #sderr ; Else print download error lcall _outstr down4: lcall _inchar ; Loop until Q pressed cjne A, #"Q", down4 ljmp monitor ; Return to monitor ;***************************************************************; ;** Download Motorola Hex File **; ;***************************************************************; nintdown: cjne A, #"S", nmotdown ; Is the command an "S"? motdown: ljmp monitor ; Return to monitor ;***************************************************************; ;** Add Breakpoint **; ;***************************************************************; nmotdown: lcall _outchar ; Echo the command cjne A, #"b", naddbrk ; Is the command a "b"? addbrk: ljmp monitor ; Return to monitor ;***************************************************************; ;** Edit Breakpoints **; ;***************************************************************; naddbrk: cjne A, #"B", ndelbrk ; Is the command a "B"? delbrk: ljmp monitor ; Return to monitor ;***************************************************************; ;** View Code Memory **; ;***************************************************************; ndelbrk: cjne A, #"c", nviewcode ; Is the command a "c"? viewcode: ljmp monitor ; Return to monitor ;***************************************************************; ;** View External Memory **; ;***************************************************************; nviewcode: cjne A, #"m", nviewmem ; Is the command an "m"? viewmem: ;##### BEGIN OLD CODE #####; lcall inaddr ; Get the start address mov DPL, R0 ; Save it in DPTR mov DPH, R1 lcall inbyte ; Get the count mov R2, A ; Save it in R2 rdmem1: movx A, @DPTR ; Fetch the next byte lcall outbyte ; Output it mov A, #" " ; Output two spaces lcall outchar lcall outchar inc DPTR ; Bump DPTR djnz R2, rdmem1 ; Lather, rinse, and repeat ljmp monitor ; Else return to monitor ;***************************************************************; ;** Edit External Memory **; ;***************************************************************; nviewmem: cjne A, #"M", neditmem ; Is the command an "M"? editmem: lcall inaddr ; Get the start address mov DPL, R0 ; Save it in DPTR mov DPH, R1 lcall inbyte ; Get the count mov R2, A ; Save it in R2 wrmem1: lcall inbyte ; Input the next byte movx @DPTR, A ; Save it mov A, #" " ; Output two spaces lcall outchar lcall outchar inc DPTR ; Bump DPTR djnz R2, wrmem1 ; Lather, rinse, and repeat ljmp monitor ; Else return to monitor ;***************************************************************; ;** View Registers **; ;***************************************************************; neditmem: cjne A, #"r", nviewreg ; Is the command an "r"? viewreg: lcall inbyte ; If so, input the address mov R0, A ; Save it in R0 mov A, @R0 ; Fetch the value lcall outbyte ; Output it ljmp monitor ; Return to monitor ;***************************************************************; ;** Edit Registers **; ;***************************************************************; nviewreg: cjne A, #"R", neditreg ; Is the command an "R"? editreg: lcall inbyte ; If so, input the address mov R0, A ; Save it in R0 lcall inbyte ; Input the value mov @R0, A ; Store it ljmp monitor ; Return to monitor ;***************************************************************; ;** Continue Execution **; ;***************************************************************; neditreg: cjne A, #"x", ncont ; Is the command an "x"? cont: lcall inaddr ; Get starting address jb F0, error ; Check for errors mov SP, #STACK ; Reinitialize the stack pointer mov A, #{monitor&0xff} ; Push the return address low byte push ACC mov A, #{monitor>>8} ; Push the return address high byte push ACC push MONR0 ; Push the execution address push MONR1 ; (R0 and R1) setb RS0 ; Choose register bank 3 setb RS1 ret ; Jump to user code ;***************************************************************; ;** Single-Step Execution **; ;***************************************************************; ncont: cjne A, #"X", nstep ; Is the command an "X"? step: ljmp error ; Not valid op for the 8751 ;##### END OLD CODE #####; ;***************************************************************; ;** Reboot Command **; ;***************************************************************; nstep: cjne A, #"z", nreboot ; Is the command a "z"? reboot: mov DPTR, #ssure ; If so, ask for confirmation lcall _outstr lcall _inchar cjne A, #"y", reboot1 ; If okay, reboot ljmp reset reboot1: cjne A, #"Y", reboot2 ljmp reset reboot2: ljmp monitor ; Otherwise, continue ;***************************************************************; ;** Halt Command ** ;***************************************************************; nreboot: cjne A, #"Z", nhalt ; Is the command a "Z"? halt: mov DPTR, #ssure ; If so, ask for confirmation lcall _outstr lcall _inchar cjne A, #"y", halt1 ; If okay, halt sjmp halt2 halt1: cjne A, #"Y", halt3 mov PCON, #0x02 ; Power down halt2: sjmp halt2 ; Failsafe loop halt3: ljmp monitor ; Otherwise, continue ;***************************************************************; ;** Help Command **; ;***************************************************************; nhalt: cjne A, #"?", nhelp ; Is the command a "?" help: mov DPTR, #sbanner ; If so, output the banner lcall _outstr mov DPTR, #shelp ; And the help message lcall _outstr ljmp monitor ; Go back to prompt ;***************************************************************; ;** Illegal Command **; ;***************************************************************; nhelp: error: mov DPTR, #serror ; Otherwise, invalid command lcall _outstr ; Print error message ljmp monitor ; And go back to prompt ;***************************************************************; ;** Process Control **; ;***************************************************************; clr RS0 ; Choose register bank 0 clr RS1 ;***************************************************************; ;** I/O routines **; ;***************************************************************; ;##### BEGIN OLD CODE #####; IF 1 ;*** innib ***; ; Input, convert, and echo a nibble from the serial port. ; Inputs: ; None. ; Outputs: ; ACC = nibble that was input. ; Calls: ; inchar, outchar. ; Error handling: ; Sets F0 on error and returns key in ACC, clears F0 otherwise. ; Side effects: ; Blocks until character is input. innib: clr F0 ; Clear errors lcall inchar ; Get a character cjne A, #"0", in2 ; If "0", in1: lcall outchar ; Echo it add A, #{-"0"} ; Subtract "0" ret ; Done! in2: jc inerr ; If < "0", error cjne A, #"9", in2a ; If <= "9", sjmp in1 in2a: jc in1 ; Same as "0" cjne A, #"A", in4 ; If "A", in3: lcall outchar ; Echo it add A, #{10-"A"} ; Adjust for "A" ret ; Done! in4: jc inerr ; If < "A", error cjne A, #"F", in4a ; If <= "F", sjmp in3 in4a: jc in3 ; Same as "A" cjne A, #"a", in6 ; If "a", in5: lcall outchar ; Echo it add A, #{10-"a"} ; Adjust for "a" ret ; Done! in6: jc inerr ; If < "a", error cjne A, #"f", in7 ; If <= "f", sjmp in5 in7: jc in5 ; Same as "a" inerr: setb F0 ; Else error -- set F0 ret ; Done! ;*** inbyte ***; ; Input, convert, and echo a byte from the serial port. ; Inputs: ; None. ; Outputs: ; ACC = byte that was input. ; Calls: ; innib. ; Error handling: ; Sets F0 on error and returns key in ACC, clears F0 otherwise. ; Side effects: ; Flags, R6 destroyed. ; Blocks until character is input. inbyte: lcall innib ; Input the high nibble jb F0, ib1 ; Check for error swap A ; Swap it into the high nibble mov R6, A ; Save it in R6 lcall innib ; Input the low nibble jb F0, ib1 ; Check for error orl A, R6 ; Combine it with the high nibble ib1: ret ; Done! ;*** inaddr ***; ; Input a 16-bit address. ; Inputs: ; R0 = default low byte. ; R1 = default high byte. ; Outputs: ; R0 = low byte of address. ; R1 = high byte of address. ; Calls: ; Error handling: ; Sets F0 on error and returns key in ACC, clears F0 otherwise. ; Side-effects: ; Flags, R6 destroyed. ; Blocks until characters received. inaddr: lcall inbyte ; Input the high byte jb F0, ja1 ; Check for error mov R1, A ; Save it in R1 lcall inbyte ; Input the low byte jb F0, ja1 ; Check for error mov R0, A ; Save it in R0 ja1: ret ; Done! ENDI ;##### END OLD CODE #####; ;*** inhex ***; ; Input a hexadecimal value from the serial port. Full user interface. ; Inputs: ; R0 = number of nibbles. ; R1 = default value high byte. ; R2 = default value low byte. ; Outputs: ; R1 = new value high byte. ; R2 = new value low byte. ; A = last character typed. ; Calls: ; inchar, outchar. ; Error handling: ; Number of nibbles (R0) must be in range 1 to 4. ; Illegal key causes return. ; Side effects: ; Flags destroyed. ; Blocks until character is input. _inhex: push B ; Save B sjmp ih3 ; Jump ahead to print value ; Backspace over existing value ih1: mov A, #BS ; Back up one digit lcall _outchar ih2: djnz B, ih1 ; Repeat for each digit ; Print new value ih3: cjne R0, #4, ih4 ; If 4 digits, mov A, R1 ; Output highest byte lcall _outbyte sjmp ih6 ih4: cjne R0, #3, ih5 ; If 3 digits, mov A, R1 ; Output highest nibble lcall _outnib sjmp ih6 ih5: cjne R0, #2, ih7 ; If 2 digits, ih6: mov A, R2 ; Output lowest byte lcall _outbyte sjmp ih8 ih7: mov A, R2 ; Otherwise, output lowest nibble lcall _outnib ih8: mov A, #BS ; Back up to last digit lcall _outchar ; Get the next key lcall _inchar ; Get the next key cjne A, #"'", ih11 ; If quote, mov A, R2 ; Shift value one byte mov R1, A lcall _inchar ; Get another key mov R2, A ; Put it in low byte ih10: mov B, R0 ; Repeat with new value sjmp ih2 ih11: cjne A, #"+", ih12 ; If "+", mov A, #1 ; Add 1 add A, R2 ; To low byte, producing carry mov R2, A clr A ; Add carry to high byte addc A, R1 mov R1, A sjmp ih10 ; Repeat with new value ih12: cjne A, #"-", ih13 ; If "-", setb C ; Subtract 1 mov A, R2 ; From low byte subb A, #0 ; Producing borrow mov R2, A mov A, R1 ; Subtract borrow from high byte subb A, #0 mov R1, A sjmp ih10 ; Repeat with new value ih13: cjne A, #BS, ih14 ; If backspace, mov A, R1 ; Shift the high byte right one nibble swap A mov B, A ; (Save the formerly low nibble) anl B, #0xf0 anl A, #0x0f ; Mask off new nibble mov R1, A ; Store new high byte mov A, R2 ; Shift the low byte right one nibble swap A anl A, #0x0f ; Put in new high nibble orl A, B mov R2, A ; Store the new low byte sjmp ih10 ; Repeat with new value ih14: cjne A, #"0", ih17 ; If "0", ih15: add A, #{-"0"} ; Subtract "0" ih16: mov B, R2 ; Save the old low byte high nibble anl B, #0xf0 swap A ; Combine the new nibble anl MONR2, #0x0f ; With the old low byte (in R2) orl A, R2 swap A mov R2, A ; Save the new low byte mov A, R1 ; Mask off the high byte anl A, #0x0f orl A, B ; Combine with its new nibble swap A mov R1, A ; Save the new high byte sjmp ih10 ; Repeat with new value ih17: jc iherr ; If < "0", error cjne A, #"9", ih18 ; If <= "9", sjmp ih15 ih18: jc ih15 ; Same as "0" cjne A, #"A", ih20 ; If "A", ih19: add A, #{10-"A"} ; Adjust for "A" sjmp ih16 ; Combine in new nibble ih20: jc iherr ; If < "A", error cjne A, #"F", ih21 ; If <= "F", sjmp ih19 ih21: jc ih19 ; Same as "A" cjne A, #"a", ih23 ; If "a", ih22: add A, #{10-"a"} ; Adjust for "a" sjmp ih16 ; Combine in new nibble ih23: jc iherr ; If < "a", error cjne A, #"f", ih24 ; If <= "f", sjmp ih22 ih24: jc ih22 ; Same as "a" iherr: push ACC ; Otherwise, illegal character mov A, R2 ; Move cursor forward lcall _outnib ; By emitting lowest nibble pop ACC ; Restore A pop B ; Restore B ret ; Done! ;*** inrnib ***; ; Input a raw (no echoing or error checking) nibble from serial port. ; Inputs: ; None. ; Outputs: ; A = nibble that was input. ; Calls: ; inchar. ; Error handling: ; None. ; Side-effects: ; Blocks until character received. ; Flags destroyed. inrnib: lcall _inchar ; Get a character cjne A, #"A", irn2 ; Check for 'A' through 'F' irn1: add A, #{10-"A"} ; Convert from ASCII ret irn2: jnc irn1 ; Else convert '0' through '9' add A, #{-"0"} ; from ASCII ret ; Done! ;*** inrbyte ***; ; Input a raw (no echoing or error checking) byte from serial port. ; Inputs: ; None. ; Outputs: ; A = byte that was input. ; Calls: ; inrnib, inchar. ; Error handling: ; None. ; Side-effects: ; Blocks until two characters received. ; Flags, B destroyed. inrbyte: lcall inrnib ; Input the high nibble swap A ; Swap it into the high nibble mov B, A ; Save it in B lcall inrnib ; Input the low nibble orl A, B ; Combine it with the high nibble ret ; Done! ;*** outnib, outbyte, outword ***; ; Output a hex-formatted 4, 8, or 16-bit value to the serial port. ; Inputs: ; A = nibble, byte, or low byte of word to output. ; B = high byte of word to output. ; Outputs: ; None. ; Calls: ; outchar. ; Error handling: ; None. ; Side effects: ; Destroys A and flags. ; Blocks until character(s) are output. _outword: push ACC ; Save the low byte mov A, B ; Output the high byte lcall _outbyte pop ACC ; Restore the low byte ; and fall through to output it _outbyte: push ACC ; Save the low nibble swap A ; Output the high nibble first lcall _outnib pop ACC ; Restore the low nibble ; and fall through to output it _outnib: anl A, #0x0f ; Mask off the bottom nibble add A, #0x90 ; Use the ol' BCD trick to daa ; convert to ASCII addc A, #"@" daa lcall _outchar ; Output the character ret ; Done! ;*** outstr ***; ; Output a null-terminated string to the serial port. ; Inputs: ; DPTR = code address of start of string. ; Outputs: ; DPTR = code address after terminating null. ; Calls: ; outchar. ; Error handling: ; None. ; Side-effects: ; Blocks until string is transmitted. _outstr: push ACC ; Save A os1: clr A ; Get the next character movc A, @A+DPTR ; Note: We use MOVC instead of ; MOVX because strings are in code. inc DPTR ; Bump pointer jz os2 ; If not NUL, lcall _outchar ; Output it sjmp os1 ; And repeat os2: pop ACC ; Otherwise, restore A ret ; and return ;***************************************************************; ;** Strings and lookup tables **; ;***************************************************************; ; General-purpose strings sprompt: ; Monitor prompt dfb CR, LF, "> ", NUL serror: ; Monitor error string dfb CR, LF, "Stupid human! Type ? for help.", NUL sabort: ; Command aborted dfb CR, LF, "Command aborted", NUL ssure: ; Are you sure? prompt dfb CR, LF, "Are you sure? ", NUL ; Monitor messages sbanner: ; Banner and copyright dfb CR, LF, "Monitor 2.0 (c) 1997 University of Wisconsin", NUL sdok: ; Download successful dfb CR, LF, "Download successful.", NUL sderr: ; Download error dfb CR, LF, "Download error. Press Q to resume. ", NUL scode: ; Prompt for code address dfb CR, LF, "Code address: ", NUL smem: ; Prompt for memory address dfb CR, LF, "Memory address: ", NUL sreg: dfb CR, LF, "Register address: ", NUL sto: ; Prompt for to address dfb CR, LF, "To address: ", NUL shelp: ; Monitor help dfb CR, LF, " ^B Break program" dfb CR, LF, " b Add breakpoint" dfb CR, LF, " B Edit breakpoints" dfb CR, LF, " c View code memory" dfb CR, LF, " m View external memory" dfb CR, LF, " M Edit external memory" dfb CR, LF, " r View registers" dfb CR, LF, " R Edit registers" dfb CR, LF, " x Continue execution" dfb CR, LF, " X Single-step execution" dfb CR, LF, " z Reboot" dfb CR, LF, " Z Halt" dfb CR, LF, " ? Show this help screen" dfb CR, LF, "To download a hex file, just send it." dfb NUL sexit: ; Program exited dfb CR, LF, "Program exited", NUL ;*** Old stuff below ***; smhelp: ; Memory routine help string dfb CR,LF,LF dfb "Memory routine 2.0 (c) 1997 University of Wisconsin", NUL dfb " 0-9A-F Hexadecimal value", CR, LF dfb " ' Enter ASCII text", CR, LF dfb " + Increment byte", CR, LF dfb " - Decrement byte", CR, LF dfb " Space Skip to next byte", CR, LF dfb " Enter Skip to next line", CR, LF dfb " r Redisplay line", CR, LF dfb " m Set new memory address", CR, LF dfb " ? Display this help message", CR, LF dfb " q Quit; return to monitor", CR, LF dfb NUL ;***************************************************************; ;** Return to your main file **; ;** **; ;** You need to define the following procedures: **; ;** uartinit Initialize the UART. **; ;** uartintr UART interrupt service routine. **; ;** _inchar Input a character from the serial port. **; ;** _outchar Output a character to the serial port. **; ;***************************************************************;