⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 paulmon2.asm

📁 MCS51系列单片机的汇编器
💻 ASM
📖 第 1 页 / 共 4 页
字号:
; 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 + -