📄 paulmon2.asm
字号:
; PAULMON2, a user-friendly 8051 monitor, by Paul Stoffregen
; Please email comments, suggestions, bugs to paul@pjrc.com
; It's free. PAULMON2 is in the public domain. You may copy
; sections of code from PAULMON2 into your own programs, even
; for commercial purposes. PAULMON2 should only be distributed
; free of charge, but may be bundled as 'value-added' with other
; products, such as development boards, CDROMs, etc. Please
; distribute the PAULMON2.DOC file and other files, not just
; the object code!
; The PAULMON2.EQU and PAULMON2.HDR files contain valuable
; information that could help you to write programs for use
; with PAULMON2.
; PAULMON2 is in the public domain. PAULMON2 is distributed in
; the hope that it will be useful, but without any warranty;
; without even the implied warranty of merchantability or fitness
; for a particular purpose.
; You are probably reading this code to see what it looks like
; and possibly learn something, or to modify it for some reason.
; Either is ok, but please remember that this code uses a number
; of tricks to cram all the functionality into just 4k. As a
; result, the code can be difficult to read, and adding new
; features can be very difficult without growing beyond 4k. To
; add or modify commands in PAULMON2, please consider using the
; "external command" functionality. It is easier to develop
; new commands this way, and you can distribute them to other
; users. Email paul@pjrc.com if you have new PAULMON2
; commands to contribute to others. Details about adding new
; commands to PAULMON2 (with examples) can be found at:
; http://www.pjrc.com/tech/8051/pm2_docs/addons.html
;---------------------------------------------------------;
; ;
; PAULMON2's default configuration ;
; ;
;---------------------------------------------------------;
; PAULMON2 should be assembled using the modified AS31 assembler,
; originally written by Ken Stauffer, many small changes by Paul
; Stoffregen. This free assembler is available on the web at
; http://www.pjrc.com/tech/8051/index.html
; As well, these web pages have a fill-out form which makes it
; very easy to custom configure PAULMON2. Using this form will
; edit the code for you, run the AS31 assmebler, and send you the
; object code to program into your chip.
; These two parameters control where PAULMON2 will be assembled,
; and where it will attempt to LJMP at the interrupt vector locations.
.equ base, 0x0000 ;location for PAULMON2
.equ vector, 0x2000 ;location to LJMP interrupt vectors
; These three parameters tell PAULMON2 where the user's memory is
; installed. "bmem" and "emem" define the space that will be searched
; for program headers, user installed commands, start-up programs, etc.
; "bmem" and "emem" should be use so they exclude memory areas where
; perphreal devices may be mapped, as reading memory from an io chip
; may reconfigure it unexpectedly. If flash rom is used, "bmem" and "emem"
; should also include the space where the flash rom is mapped.
.equ pgm, 0x2000 ;default location for the user program
.equ bmem, 0x1000 ;where is the beginning of memory
.equ emem, 0xFFFF ;end of the memory
; Flash ROM parameters. If "has_flash" is set to zero, all flash rom
; features are turned off, otherwise "bflash" and "eflash" should specify
; the memory range which is flash rom. Though AMD doesn't suggest it,
; you may be able to map only a part of the flash rom with your address
; decoder logic (and not use the rest), but you should be careful that
; "bflash" and "eflash" don't include and memory which is NOT flash rom
; so that the erase algorithm won't keep applying erase pulses until it
; finally gives up (which will stress the thin oxide and degrade the
; flash rom's life and reliability). "erase_pin" allows you to specify
; the bit address for a pin which (if held low) will tell PAULMON2 to
; erase the flash rom chip when it starts up. This is useful if you
; download programs with the "start-up" headers on them and the code you've
; put in the flash rom crashes!
.equ has_flash, 0 ;set to non-zero value if flash installed
.equ bflash, 0x8000 ;first memory location of Flash ROM
.equ eflash, 0xFFFF ;last memory location of Flash ROM
.equ erase_pin, 0 ;00 = disable erase pin feature
;.equ erase_pin, 0xB5 ;B5 = pin 15, P3.5 (T1)
; Please note... much of the memory management code only looks at the
; upper 8 bits of an address, so it's not a good idea to somehow map
; your memory chips (with complex address decoding logic) into chunks
; less than 256 bytes. In other words, only using a piece of a flash
; rom chip and mapping it between C43A to F91B would confuse PAULMON2
; (as well as require quit a bit of address decoding logic circuitry)
; To set the baud rate, use this formula or set to 0 for auto detection
; baud_const = 256 - (crystal / (12 * 16 * baud))
.equ baud_const, 0 ;automatic baud rate detection
;.equ baud_const, 255 ;57600 baud w/ 11.0592 MHz
;.equ baud_const, 253 ;19200 baud w/ 11.0592 MHz
;.equ baud_const, 252 ;19200 baud w/ 14.7456 MHz
;.equ baud_const, 243 ;4808 baud w/ 12 MHz
.equ line_delay, 6 ;num of char times to pause during uploads
; About download speed: when writing to ram, PAULMON2 can accept data
; at the maximum baud rate (baud_const=255 or 57600 baud w/ 11.0592 MHz).
; Most terminal emulation programs introduce intentional delays when
; sending ascii data, which you would want to turn off for downloading
; larger programs into ram. For Flash ROM, the maximum speed is set by
; the time it takes to program each location... 9600 baud seems to work
; nicely for the AMD 28F256 chip. The "character pacing" delay in a
; terminal emulation program should be sufficient to download to flash
; rom and any baud rate. Some flash rom chips can write very quickly,
; allowing high speed baud rates, but other chips can not. You milage
; will vary...
; Several people didn't like the key definations in PAULMON1.
; Actually, I didn't like 'em either, but I never took the time
; to change it. Eventually I got used to them, but now it's
; really easy to change which keys do what in PAULMON2. You
; can guess what to do below, but don't use lowercase.
.equ help_key, '?' ;help screen
.equ dir_key, 'M' ;directory
.equ run_key, 'R' ;run program
.equ dnld_key, 'D' ;download
.equ upld_key, 'U' ;upload
.equ nloc_key, 'N' ;new memory location
.equ jump_key, 'J' ;jump to memory location
.equ dump_key, 'H' ;hex dump memory
.equ intm_key, 'I' ;hex dump internal memory
.equ edit_key, 'E' ;edit memory
.equ clrm_key, 'C' ;clear memory
.equ erfr_key, 'Z' ;erase flash rom
; timing parameters for AMD Flash ROM 28F256. These parameters
; and pretty conservative and they seem to work with crystals
; between 6 MHz to 24 MHz... (tested with AMD 28F256 chips only)
; unless you know this is a problem, it is probably not a good
; idea to fiddle with these.
;.equ pgmwait, 10 ;22.1184 MHz crystal assumed
.equ pgmwait, 19 ;11.0592 MHz
.equ verwait, 5
;.equ erwait1, 40 ;fourty delays @22.1184
.equ erwait1, 20 ;twenty delays for 11.0592 MHz
.equ erwait2, 229 ;each delay .5 ms @22.1184MHz
; These symbols configure paulmon2's internal memory usage.
; It is usually not a good idea to change these unless you
; know that you really have to.
.equ psw_init, 0 ;value for psw (which reg bank to use)
.equ dnld_parm, 0x10 ;block of 16 bytes for download
.equ stack, 0x30 ;location of the stack
.equ baud_save, 0x78 ;save baud for warm boot, 4 bytes
;---------------------------------------------------------;
; ;
; Interrupt Vectors ;
; (and little bits of code crammed in the empty spaces) ;
; ;
;---------------------------------------------------------;
.org base
ljmp poweron ;reset vector
.org base+3
ljmp vector+3 ;ext int0 vector
r6r7todptr:
mov dpl, r6
mov dph, r7
ret
.org base+11
ljmp vector+11 ;timer0 vector
dptrtor6r7:
mov r6, dpl
mov r7, dph
ret
.org base+19
ljmp vector+19 ;ext int1 vector
dash: mov a, #'-' ;seems kinda trivial, but each time
ajmp cout ;this appears in code, it takes 4
nop ;bytes, but an acall takes only 2
.org base+27
ljmp vector+27 ;timer1 vector
cout_sp:acall cout
ajmp space
nop
.org base+35
ljmp vector+35 ;uart vector
dash_sp:acall dash
ajmp space
nop
.org base+43
ljmp vector+43 ;timer2 vector (8052)
;---------------------------------------------------------;
; ;
; The jump table for user programs to call ;
; subroutines within PAULMON ;
; ;
;---------------------------------------------------------;
.org base+46 ;never change this line!! Other
;programs depend on these locations
;to access paulmon2 functions
ajmp phex1 ;2E
ajmp cout ;30
ajmp cin ;32
ajmp phex ;34
ajmp phex16 ;36
ajmp pstr ;38
ajmp ghex ;3A
ajmp ghex16 ;3C
ajmp esc ;4E
ajmp upper ;40
ljmp autobaud ;42
pcstr_h:ljmp pcstr ;45
ajmp newline ;48
ljmp lenstr ;4A
ljmp pint8u ;4D
ljmp pint8 ;50
ljmp pint16u ;53
ljmp smart_wr ;56
ljmp prgm ;59
ljmp erall ;5C
ljmp find ;5F
cin_filter_h:
ljmp cin_filter ;62
ajmp asc2hex ;64
;---------------------------------------------------------;
; ;
; Subroutines for serial I/O ;
; ;
;---------------------------------------------------------;
cin: jnb ri, cin
clr ri
mov a, sbuf
ret
dspace: acall space
space: mov a, #' '
cout: jnb ti, cout
clr ti ;clr ti before the mov to sbuf!
mov sbuf, a
ret
;clearing ti before reading sbuf takes care of the case where
;interrupts may be enabled... if an interrupt were to happen
;between those two instructions, the serial port will just
;wait a while, but in the other order and the character could
;finish transmitting (during the interrupt routine) and then
;ti would be cleared and never set again by the hardware, causing
;the next call to cout to hang forever!
newline2: ;print two newlines
acall newline
newline:push acc ;print one newline
mov a, #13
acall cout
mov a, #10
acall cout
pop acc
ret
;get 2 digit hex number from serial port
; c = set if ESC pressed, clear otherwise
; psw.5 = set if return w/ no input, clear otherwise
ghex:
ghex8: clr psw.5
ghex8c:
acall cin_filter_h ;get first digit
acall upper
cjne a, #27, ghex8f
ghex8d: setb c
clr a
ret
ghex8f: cjne a, #13, ghex8h
setb psw.5
clr c
clr a
ret
ghex8h: mov r2, a
acall asc2hex
jc ghex8c
xch a, r2 ;r2 will hold hex value of 1st digit
acall cout
ghex8j:
acall cin_filter_h ;get second digit
acall upper
cjne a, #27, ghex8k
sjmp ghex8d
ghex8k: cjne a, #13, ghex8m
mov a, r2
clr c
ret
ghex8m: cjne a, #8, ghex8p
ghex8n: acall cout
sjmp ghex8c
ghex8p: cjne a, #21, ghex8q
sjmp ghex8n
ghex8q: mov r3, a
acall asc2hex
jc ghex8j
xch a, r3
acall cout
mov a, r2
swap a
orl a, r3
clr c
ret
;carry set if esc pressed
;psw.5 set if return pressed w/ no input
ghex16:
mov r2, #0 ;start out with 0
mov r3, #0
mov r4, #4 ;number of digits left
clr psw.5
ghex16c:
acall cin_filter_h
acall upper
cjne a, #27, ghex16d
setb c ;handle esc key
clr a
mov dph, a
mov dpl, a
ret
ghex16d:cjne a, #8, ghex16f
sjmp ghex16k
ghex16f:cjne a, #127, ghex16g ;handle backspace
ghex16k:cjne r4, #4, ghex16e ;have they entered anything yet?
sjmp ghex16c
ghex16e:acall cout
acall ghex16y
inc r4
sjmp ghex16c
ghex16g:cjne a, #13, ghex16i ;return key
mov dph, r3
mov dpl, r2
cjne r4, #4, ghex16h
clr a
mov dph, a
mov dpl, a
setb psw.5
ghex16h:clr c
ret
ghex16i:mov r5, a ;keep copy of original keystroke
acall asc2hex
jc ghex16c
xch a, r5
lcall cout
mov a, r5
push acc
acall ghex16x
pop acc
add a, r2
mov r2, a
clr a
addc a, r3
mov r3, a
djnz r4, ghex16c
clr c
mov dpl, r2
mov dph, r3
ret
ghex16x: ;multiply r3-r2 by 16 (shift left by 4)
mov a, r3
swap a
anl a, #11110000b
mov r3, a
mov a, r2
swap a
anl a, #00001111b
orl a, r3
mov r3, a
mov a, r2
swap a
anl a, #11110000b
mov r2, a
ret
ghex16y: ;divide r3-r2 by 16 (shift right by 4)
mov a, r2
swap a
anl a, #00001111b
mov r2, a
mov a, r3
swap a
anl a, #11110000b
orl a, r2
mov r2, a
mov a, r3
swap a
anl a, #00001111b
mov r3, a
ret
;carry set if invalid input
asc2hex:
clr c
add a, #208
jnc hex_not
add a, #246
jc hex_maybe
add a, #10
clr c
ret
hex_maybe:
add a, #249
jnc hex_not
add a, #250
jc hex_not
add a, #16
clr c
ret
hex_not:setb c
ret
phex:
phex8:
push acc
swap a
anl a, #15
add a, #246
jnc phex_b
add a, #7
phex_b: add a, #58
acall cout
pop acc
phex1: push acc
anl a, #15
add a, #246
jnc phex_c
add a, #7
phex_c: add a, #58
acall cout
pop acc
ret
phex16:
push acc
mov a, dph
acall phex
mov a, dpl
acall phex
pop acc
ret
;a not so well documented feature of pstr is that you can print
;multiple consecutive strings without needing to reload dptr
;(which takes 3 bytes of code!)... this is useful for inserting
;numbers or spaces between strings.
pstr: push acc
pstr1: clr a
movc a, @a+dptr
inc dptr
jz pstr2
mov c, acc.7
anl a, #0x7F
acall cout
jc pstr2
sjmp pstr1
pstr2: pop acc
ret
upper: ;converts the ascii code in Acc to uppercase, if it is lowercase
push acc
clr c
subb a, #97
jc upper2 ;is it a lowercase character
subb a, #26
jnc upper2
pop acc
add a, #224 ;convert to uppercase
ret
upper2: pop acc ;don't change anything
ret
lenstr: mov r0, #0 ;returns length of a string in r0
push acc
lenstr1:clr a
movc a,@a+dptr
jz lenstr2
mov c,acc.7
inc r0
Jc lenstr2
inc dptr
sjmp lenstr1
lenstr2:pop acc
ret
esc: ;checks to see if <ESC> is waiting on serial port
;C=clear if no <ESC>, C=set if <ESC> pressed
;buffer is flushed
push acc
clr c
jnb ri,esc2
mov a,sbuf
cjne a,#27,esc1
setb c
esc1: clr ri
esc2: pop acc
ret
;---------------------------------------------------------;
; ;
; The 'high-level' stuff to interact with the user ;
; ;
;---------------------------------------------------------;
menu: ;first we print out the prompt, which isn't as simple
;as it may seem, since external code can add to the
;prompt, so we've got to find and execute all of 'em.
mov dptr, #prompt1 ;give 'em the first part of prompt
acall pcstr_h
mov a, r7
acall phex
mov a, r6
acall phex
;mov dptr, #prompt2
acall pstr
;now we're finally past the prompt, so let's get some input
acall cin_filter_h ;get the input, finally
acall upper
;push return address onto stack so we can just jump to the program
mov b, #(menu & 255) ;we push the return address now,
push b ;to save code later...
mov b, #(menu >> 8) ;if bogus input, just ret for
push b ;another prompt.
;first we'll look through memory for a program header that says
;it's a user installed command which matches what the user pressed
;user installed commands need to avoid changing R6/R7, which holds
;the memory pointer. The stack pointer can't be changed obviously.
;all the other general purpose registers should be available for
;user commands to alter as they wish.
menux: mov b, a ;now search for external commands...
mov dptr, #bmem
menux1: acall find
jnc menuxend ;searched all the commands?
mov dpl, #4
clr a
movc a,@a+dptr
cjne a, #254, menux2 ;only FE is an ext command
inc dpl
clr a
movc a,@a+dptr
cjne a, b, menux2 ;only run if they want it
acall space
mov dpl, #32
acall pstr ;print command name
acall newline
mov dpl, #64
clr a
jmp @a+dptr ;take a leap of faith and jump to it!
menux2: inc dph
mov a, dph
cjne a, #((emem+1) >> 8) & 255, menux1
menuxend:
mov a, b
;since we didn't find a user installed command, use the builtin ones
menu1a: cjne a, #help_key, menu1b
mov dptr, #help_cmd2
acall pcstr_h
ajmp help
menu1b: cjne a, #dir_key, menu1c
mov dptr, #dir_cmd
acall pcstr_h
ajmp dir
menu1c: cjne a, #run_key, menu1d
mov dptr, #run_cmd
acall pcstr_h
ajmp run
menu1d: cjne a, #dnld_key, menu1e
mov dptr, #dnld_cmd
acall pcstr_h
ajmp dnld
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -