由于采用手工汇编的机器码是相对于存储器的绝对地址进行定位的,因此在调试时,若要在程序中增加或删除一条指令,就会造成指令的绝对地址发生变化。这样除修改那条指令外,几乎所有转移、调用指令的操作数都要作相应的修改,稍有疏忽程序就会出错。
而采用机器汇编,在很大程度上可以避免上述麻烦。只要通过键盘输入源程序后,其余作工作都由计算机来进行,即查出相应的机器码,对地址进行定位,建立能被开发装置接收的机器码文件、建立能打印出清单的列表文件等等。机器码不需要再一个一个地从仿真器的键盘上键入,只要从PC机的通讯口直接传送到开发装置中去。因此使用机器汇编大大提高了单片机开发的速度。
源程序被输入到机器中后,以一个文件的形式保存起来,然后就可以对这个文件进行处理了,要对这个文件进行汇编,必须有相应的汇编程序。在PC机上进行汇编的程序有多种,以下介绍的MASM51软件就是其中的一种,它的主要特点有:
1.对用户编写的源程序格式要求不太严格,例如,英文字母的大、小写不受限制,变量名或标号名没有长度限制,对源程序中的空格数也没有严格的要求等等。这些给用户编写程序带来很大的方便。
2.具有较强的功能,例如支持宏语句,使同一程序中类似的程序结构,只要输入一次,在使用时可用宏语句调用。此外还允许用条件汇编语句以及完善的伪指令系统等。减少了编写、输入源程序的工作量。
3.不管用户的源程序有多少错误,它都能全部汇编完,并能形成列表文件及目标文件,以供用户修改错误。
4.可以接收汉字注释,并能完整的将汉字注释复制到列表文件,而其它一些汇编软件则不能处理汉字,或会使列表文件中的汉字注释混乱。
运行MASM51软件的条件很低,现在的PC机都能达到。
第二节 如何启动MASM51宏汇编程序
- 一、启动和退出
MASM51软件有两个文件:MASM51.EXE和MASM51.OVL,只要将这两个文件拷入硬盘的一个目录中(如MASM51)中,然后在DOS命令行中打入:
MASM51(回车)
即在显示器上显示如下信息:
The Cybernetic Micro Systems
CYS-8051 Software Package
Version 3.03`
for the IBM Personal Computer
(C)Copy right Cybrenetic MIcro Systems,Inc 1983,1984,1985,
1986
High Level Language Commands
ASM51[file] P8751 file P8744 file//
P27XX file COMP[file] READ[kbytes] DISP[start,num]
INITIALIZE CLEAR LIST EXIT
RUN[file] GOTO Label STOP CONT
DIR[spec] TYPE file DOS cmd HELP
PRINT arglist EQU = WARBLE
DELAY[secs] CLS HLSEARCH flag BEEP
OFFSET pc[,loc] PROM type SECURE SAVE file[,start,num]
LOAD file EDBYTE pc PVOLT value PSETUP num,value
>
信息的出现说明已进入MASM51宏汇编程序,在屏幕下方出现的符号‘〉’是宏汇编程序提示符,它的出现告诉用户,宏汇编程序处于接收命令状态。
虽然屏幕上提供了很多的命令,但大部份命令要在相应的硬件配合下才能实现。当用来汇编源程序时,使用的主要命令是:
ASM51 盘号:文件名 〈回车〉 进行汇编
DIR 盘号:文件名 〈回车〉 显示目录
EXIT 〈回车〉 退出宏汇编
二、进行汇编
用户在使用宏汇编程序之前,应该已使用编缉器(如EDIT、CCED等)编好了8051汇编语言的源程序,并已作为一个文件存盘,该文件一般以.ASM为扩展名。
进入MASM51后,键入
ASM51 [d:filename[.asm] [选项]
如果省略扩展名,则系统默认为.asm,如果不以.asm为扩展名,则应键入文件的全名。汇编后,将产生两个输出文件:一个是16进制代码文件,以.hex为扩展名,另一个是列表文件,以.lst为扩展名,文件名与输入文件的文件名相同。
在汇编时,可以加入一些选项进行控制,可加的选项如下:
1./L选项:只在屏幕上显示列表文件的内容,不生成列表文件。
2./H选项:汇编后不在磁盘上产生.hex文件。
3./S选项:产生的列表文件中将不包括符号表。
4./P选项:汇编程序序所生成的符号表将成为能被其他源程序引用的永久符号表的一部份,即如果不退出宏汇编,该符号表将保留在内存中,在汇编其它源程序时可以引用它们,以便连接几个源文件。
5./N选项:使生成的列表文件在打印(或显示)时不分页。不加'/N'则每隔55行源代码生成一个新页头。
一般情况下,我们不需要在命令后面加任何选项。
如下面所示:
ASM51 Filename[.asm] [回车]即可
下面列出DEMO51.ASM的源文件,及用MASM51汇编后产生的代码文件和列表文件,以供参考。
T0_flag bit 0
T1_flag bit 1
INIT_Timer0 equ 0FE80H
INIT_Timer1 equ 0FD00H
X0_vector equ 0003H
X1_vector equ 0013H
T0_vector equ 000BH
T1_vector equ 001BH
S0_vector equ 0023H
INIT: AJMP SETUP
;Interrept Jump Table
ORG X0_vector
INT_0: SETB P3.2
RETI ;int0 interrupt
ORG T0_vector
T0INT: ACALL TIM0 ;timer0 interrupt
RETI
ORG X1_vector
INT_1: SETB P3.3
RETI ;int1 interrupt
ORG T1_vector
T1INT: ACALL TIM1 ;timer1 interrupt
RETI
ORG S0_vector
AJMP SERIAL ;UART interrupt
DB 'This is demo program for MCS-51TM'
SETUP: MOV SP,#5FH ;set sp point
MOV TMOD,#11H ;set timer mode
MOV TH0,#HIGH(INIT_Timer0)
MOV TL0,#LOW(INIT_Timer0)
MOV TH1,#HIGH(INIT_Timer1)
MOV TL1,#LOW(INIT_Timer1)
SETB EX0
SETB PX0
SETB EX1
CLR PX1
SETB PT1
SETB ET0
SETB ET1
SETB TR0
SETB TR1
SETB ES
MOV SCON,#99H
SETB EA ;enable interrupt
WAIT: NOP
AJMP WAIT ;wait
SERIAL: NOP ;serial interrupt process
CLR RI
RETI
TIM0: CLR TR0 ;timer0 interrupt process
CPL T0_flag
MOV TL0,#LOW(INIT_Timer0)
MOV A,#HIGH(INIT_Timer0)
MOV TH0,A
SETB TR0
CLR P3.3
RET
TIM1: CLR TR1 ;timer1 interrupt process
CPL T1_flag
MOV TL1,#LOW(INIT_Timer1)
MOV A,#HIGH(INIT_Timer1)
MOV TH1,A
SETB TR1
CLR P3.2
RET
END
;********************************************************
;DEMO51.ASM FILE END
;********************************************************
以上是DEMO51.ASM源程序文件。
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 1
08-25-96
0000 = T0_flag bit 0
0001 = T1_flag bit 1
FE80 = INIT_Timer0 equ 0FE80H
FD00 = INIT_Timer1 equ 0FD00H
0003 = X0_vector equ 0003H
0013 = X1_vector equ 0013H
000B = T0_vector equ 000BH
001B = T1_vector equ 001BH
0023 = S0_vector equ 0023H
0000 0146 INIT: AJMP SETUP
;Interrept Jump Table
0003 ORG X0_vector
0003 D2B2 INT_0: SETB P3.2
0005 32 RETI ;int0 interrupt
000B ORG T0_vector
000B 1178 T0INT: ACALL TIM0 ;timer0 interrupt
000D 32 RETI
0013 ORG X1_vector
0013 D2B3 INT_1: SETB P3.3
0015 32 RETI ;int1 interrupt
001B ORG T1_vector
001B 1188 T1INT: ACALL TIM1 ;timer1 interrupt
001D 32 RETI
0023 ORG S0_vector
0023 0174 AJMP SERIAL ;UART interrupt
0025 54 68 69 DB 'This is demo program for MCS-51TM'
0028 73 20 69 73 20 64 65 6D 6F 20
0032 70 72 6F 67 72 61 6D 20 66 6F
003C 72 20 4D 43 53 2D 35 31 54 4D
0046 75815F SETUP: MOV SP,#5FH ;set sp point
0049 758911 MOV TMOD,#11H ;set timer mode
004C 758CFE MOV TH0,#HIGH(INIT_Timer0)
004F 758A80 MOV TL0,#LOW(INIT_Timer0)
0052 758DFD MOV TH1,#HIGH(INIT_Timer1)
0055 758B00 MOV TL1,#LOW(INIT_Timer1)
0058 D2A8 SETB EX0
005A D2B8 SETB PX0
005C D2AA SETB EX1
005E C2BA CLR PX1
0060 D2BB SETB PT1
0062 D2A9 SETB ET0
0064 D2AB SETB ET1
0066 D28C SETB TR0
0068 D28E SETB TR1
006A D2AC SETB ES
006C 759899 MOV SCON,#99H
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 2
08-25-96
006F D2AF SETB EA ;enable interrupt
0071 00 WAIT: NOP
0072 0171 AJMP WAIT ;wait
0074 00 SERIAL: NOP ;serial interrupt process
0075 C298 CLR RI
0077 32 RETI
0078 C28C TIM0: CLR TR0 ;timer0 interrupt process
007A B200 CPL T0_flag
007C 758A80 MOV TL0,#LOW(INIT_Timer0)
007F 74FE MOV A,#HIGH(INIT_Timer0)
0081 F58C MOV TH0,A
0083 D28C SETB TR0
0085 C2B3 CLR P3.3
0087 22 RET
0088 C28E TIM1: CLR TR1 ;timer1 interrupt process
008A B201 CPL T1_flag
008C 758B00 MOV TL1,#LOW(INIT_Timer1)
008F 74FD MOV A,#HIGH(INIT_Timer1)
0091 F58D MOV TH1,A
0093 D28E SETB TR1
0095 C2B2 CLR P3.2
0097 22 RET
0000 END
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 3
08-25-96
;%T Symbol Name Type Value
INIT. . . . . . . . . . . . . . L 0000
INIT_TIMER0 . . . . . . . . . . I FE80
INIT_TIMER1 . . . . . . . . . . I FD00
INT_0 . . . . . . . . . . . . . L 0003
INT_1 . . . . . . . . . . . . . L 0013
S0_VECTOR . . . . . . . . . . . I 0023
SERIAL. . . . . . . . . . . . . L 0074
SETUP . . . . . . . . . . . . . L 0046
T0INT . . . . . . . . . . . . . L 000B
T0_FLAG . . . . . . . . . . . . B 0000
T0_VECTOR . . . . . . . . . . . I 000B
T1INT . . . . . . . . . . . . . L 001B
T1_FLAG . . . . . . . . . . . . B 0001
T1_VECTOR . . . . . . . . . . . I 001B
TIM0. . . . . . . . . . . . . . L 0078
TIM1. . . . . . . . . . . . . . L 0088
WAIT. . . . . . . . . . . . . . L 0071
X0_VECTOR . . . . . . . . . . . I 0003
X1_VECTOR . . . . . . . . . . . I 0013
;%Z
00 Errors (0000)
以上是DEMO51.LST文件。
:020000000146B7
:03000300D2B23244
:03000B0011783237
:03001300D2B33233
:03001B0011883217
:0D0023000174546869732069732064656D71
:100030006F2070726F6772616D20666F72204D4322
:10004000532D3531544D75815F758911758CFE7551
:100050008A80758DFD758B00D2A8D2B8D2AAC2BA9B
:10006000D2BBD2A9D2ABD28CD28ED2AC759899D257
:10007000AF00017100C29832C28CB200758A8074E0
:10008000FEF58CD28CC2B322C28EB201758B007485
:08009000FDF58DD28EC2B222F3
以上是DEMO51.HEX文件。
第三节 汇编程序输入和输出文件的格式
一、源文件
源文件是由文字编缉器编写的由汇编指令和MASM51伪指令构成的文本文件。源文件一般应以.ASM为扩展名。
- 二、源文件的格式
以回车作为结束的一行称为语句行。每一语句行长度应少于80个字符(即40个汉字)。每一个语句行对于汇编程序来说都是一条单独的命令行,它可以是一条汇编语言指令,也可以是一条注释,或是空白(即什么也不写),还可以是系统允许的伪指令。所有行必须按照INTEL标准格式书写即:
标号: 命令 参数 ;注释
即一行由四部份组成,各部份的顺序不能搞错,但可以根据需要缺省其中的一部份或几部份,甚至全部省去,即空白行。标号后面必须有‘:’,而命令语句和参数之间必须用空格分开,如果命令有多个参数,则参数与参数之间必须用‘,’分开,注释前必须用‘;’,也即‘;’后面的语句将不参与汇编,不生成代码,所以可以在‘;’引导的后面写任何字符,包括汉字。
标号是标志程序中某一行的符号名,标号的数值就是标号所在行代码的地址。在宏汇编MASM51中标号的长度不受限制,但标号中不能包含‘:’或其它的一些特殊符号,也不以用汉字,可以用数字作标号,但必须用字母开头。当标号作参数用(如标号作转移地址),在命令后面出现时,必须舍去‘:’(如 LJMP START中的 START)。
每行只能有一个标号,一个标号只能用在一处,如果有两行用了同一个标号,则汇编时就会出错。由于标号的长度没有限制,可以用有意义的英文来说明行,使源程序读起来更方便。
命令及参数请参考有关单片机教材,其规定符合INTEL公司的51单片机汇编语言要求。这里必须注意:当采用十六进制数时,如果数值是以A,B,C,D,E,F开头的,则为了区分是数字还是字母,应当在这些数字前加'0',如FFH,应当写成0FFH,C0H应写成0C0H等。
注释用于对程序的说明,它以分号开始,以回车结束。源程序行可以只包含注释,注释只是被复制到列表文件中,不产生机器码。由于汇编程序不直观,所以在编写源程序时,应当养成多写注释的习惯,这样有助于源程序的阅读。否则,时间一长,自编的程序也会搞不清表达的含义。典型的注释有如下几种:1.在一行的后面写,以说明这一行语句的用途;2.在一段子程序的前面写,以说明这一子程序的用途,由什么程序调用,入口参数,出口值,占用资源等等情况;3.在整个源程序的最前面书写,以说明本程序的用途,资源的分配等情况。以下是一个例子:
;堆栈从5FH起
;01H单元,用作键盘扫描程序判断是否有键按下,有为'1',无为'0'
;38H单元,音量大小代码,初始为2
ORG 0000H
LJMP START
ORG 0003H ;外中断0处理程序
NOP
RET
START: ;初始化程序
MOV SP,5FH
.
.
;***********************************************
;以上初始化程序
;***********************************************
MAIN:
LCALL KEY
LJMP MAIN
;**********************************************
;以上主程序
;*******************************************
;***********************************************
;以下键盘程序,键值从0开始
;***************************************
KEY:
PUSH PSW ;将PSW推入堆栈
SETB RS0 ;选第一工作区
MOV SBUF,#00H
KEY_DISP:
.
.
.
POP PSW
RET
;*********************************************************
;以上键盘程序
;********************************************************
.
.
三、算术表达式和算术操作
在宏汇编中允许使用一个算术表达式的结果来作为一个参数使用,从而使编写源程序时更为灵活。算术表达式由参数与算术操作符组成,其中参数可以是数值类型,符号类型或字符串类型。而算术表达式的最后运算结果即为该算术表达式所代表的参数。它是一个十六位的二进制数。在可以用数值的任何地方,都可以使用表达式,例如直接值,转移和子程序调用的址址部分。汇编命令内表达式由汇编解释程序在汇编时计算,改变表达式值的唯一方法就是修改参数值。
算术表达式的最简单的形式就是只有一个参数,这个参数的值也就是它的结果。一般形式为参数与算术操作符的组合,它们之间用某些限定符予以隔离,例如空格、制表符等。
在宏汇编中,所有的参数值均为整数,以十六位二进制形式存放,因此其表示范围为0~65535。对于负数则以其补码来表示。下面给出一个例子介绍宏汇编中允许使用的各种算术操作符,请大家仔细阅读。
MOV A,#3+5 ;MOV A,#8,加法,前面加'#'号代表是立即数
MOV A,3+5 ;MOV A,8 ,加法,未加'#'号代表3+5是地址
MOV A,5-3 ;MOV A,2 ,减法
MOV A,3-5 ;MOV A,0FEH,减法
MOV A,#5-3 ;MOV A,#2,减法
MOV A,#5*3 ;MOV A,#15,乘法
MOV A,#6/3 ;MOV A,#2 ,除法
MOV A,#6 MOD 3 ;MOV A,#0 ,取模运算
MOV A,01010010 SHR 3 ;MOV A,E3H ,向右移位,01010010向右移3位
MOV A,#21 SHL 5 ;MOV A,#0A0H ,向左移位,21向左移5位
MOV A, # NOT 0A5H ;MOV A,#5AH ,取0A5H的反
MOV A,#10101111 OR 11110000 ;MOV A,#57H,求两数之或
MOV A,#10101010 AND 00001111 ;MOV A,#52H,求两数之与
MOV A,#4B2H XOR 3197 ;MOV A,#0CFH,求两数之异或
MOV A,#(HIGH 4567H) ;MOV A,#45H ,取4567H的高两位
MOV A,LOW 4567H ;MOV A,67H ,取4567H的低两位
END
四、机器码代码文件
机器码代码文件由宏汇编产生,为了与一般的HEX文件相区别,通常由宏汇编产生的机器码代码文件被称为INTEL文件。该文件是由能够在处理器上运行的机器指令码组成。它可被用来传送到仿真器或用户系统中进行调试或运行。
INTEL HEX文件由若干行组成,每行表示一个记录,它的每一行都是可以打印的ASCII字符,用两个字符,用两个字符来表示一个字节的值。每一行的格式如下:
:CC aaaa tt dd.......dd ss冒号":"为每一行的起始符,随后为一组数据在其中。CC 为由两个字节表示的十六进制数,它的范围为00H~10H,它表示该记录包含的机器代码的字节数。
aaaa为用ASCII码表示的四个十六进制数,它是该记录第一个数据字节所驻存的存储空间的绝对地址。
tt是表示该记录数据类型的一个代码,若该记录为数据,则tt为0,否则是其它值。dd表示代码的实际字节值,每一个字节值用两个字符表示。(将会有cc个dd数据)ss是一个和校验,它是从计数器到最后一个数据字节的所有字节值累加和的相所数。
当ss与记录中所有的字相加时,其结果应该为零,若为其它值则表明该记录有错。
五、列表文件
由汇编程序生成的第二个文件是列表文件,它以LST为扩展名,也是一个ASCII码字符文件,回而可以被打印显示,也可以作为程序的文档。
列表文件是分页的,每页的长度由缺省值决定。也可以通过伪指令&PAGE来决定。每页以一起始行开始,用来指出汇编的类型类型和版本,以及页号。
当用户使用&TITLE伪指令时,该行后面紧接着一个标题行,当用户使用SUBTITLE指令时,后再接一个子标题行。例如:The Cybernetic Micro Sysystems 8051 FamilyAssembler,Version 3.03 Page 109-10-83。
每页的其它部分由用户程序、汇编所产生的绝对地址和机器代码组成。通常一个行的第一个字符是一个空格,若出现"I"则指出这一行是个外部来的文件。而"+"则表明是一个宏扩展行。后面接着的四个字符通常是当前语句的程序地址,以十六进制形式给出,END指令前的四个字符表示该指令的起始地址参数值。后面有一个空格。
程序计数器后是10个字符的空间,它通常包含该行汇编出的目标代码的十六进制值。显示出的字节值的数目依赖于每条语句所要求的字节数目,这里,每个字节值用两字符表示。对等价指令,这个域还包含一个等号(=)。不生成目标代码的源程序行中该域全为空格。
每行的前16个字符后面的剩余部分包含着台的源程序文件。因而列表文件的每一行长度为源程序的长度再加上16列字符。如果是用80列宽的打印纸则源程序行的长度应小于64列,否则打印设备在打印时反长行切断,或则分成两行。
第四节 伪 指 令
伪指令是对汇编起某种控制作用的特殊命令,其格式与通常的操作指令一样,并可加在汇编程序的任何地方,但它们并不产生机器指令。
许多伪指令要求带参数,这在定义伪指令时由“表达式”域指出,任何数值与表达式匀可以作为参数。
不同汇编程序允许的伪指令并不相同,以下所述的伪指令仅适用于MASM51系统,但一些基本的伪指令在大部份汇编程序中都能使用,当使用其它的汇编程序版本时,只要注意一下它们之间的区别就可以了。MASM51中可用的伪指令有:
ORG 设置程序起始地址
END 标志源代码结束
EQU 定义常数
SET 定义整型数
DATA 给字节类型符号定值
BYTE 给字节类型符号定值
WROD 给字类型符号定值
BIT 给位地址取名
ALTNAME 用自定义名取代保留字
DB 给一块连续的存储区装载字节型数据
DW 给一块连续的存储区装载字型数据
DS 预留一个连续的存储区或装入指定字节。
INCLUDE 将一个源文件插入程序中
TITLE 列表文件中加入标题行
NOLIST 汇编时不产生列表文件
NOCODE 条件汇编时,条件为假的不产生清单
- 一、ORG
伪指令ORG用于为在它之后的程序设置地址值,它有一个参数,其格式为:
ORG 表达式
表达式可以是一个具体的数值,也可以包含变量名,如果包含变量名,则必须保证,当第一次遇到这条伪指令时,其中的变量必须已有定义(已有具体的数值),否则,无定义的值将由0替换,这将会造成错误。在列表文件中,由ORG定义的指令地址会被打印出来。
ORG指令有什么用途呢?指令被翻译成机器码后,将被存入系统的ROM中,一般情况下,机器码总是一个接一个地放在存储器中,但有一些代码,其位置有特殊要求,典型的是五个中断入口,它们必须被放在0003H,000BH,0013H,001BH和0023H的位置,否则就会出错,如果我们编程时不作特殊处理,让机器代码一个接一个地生成,不能保证这些代码正好处于这些规定的位置,执行就会出错,这时就要用到ORG伪指令了。看如下例子:
例:
INT_0 EQU 1000H
TIME_0 EQU 1010H
INT_1 EQU 1020H
TIME_1 EQU 1030H
SERIAL EQU 1040H
AJMP START ;跳转到主程序起始点
LJMP INT_0 ;外中断0处理程序
LJMP TIME_0 ;定时中断0处理程序
LJMP INT_1 ;外中断1处理程序
LJMP TIME_1 ;定时中断1处理程序
LJMP SERIAL ;串行口中断程序
START:
NOP
END
上面的程序经汇编后列表文件如下:
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 1
08-26-96
1000 = INT_0 EQU 1000H
1010 = TIME_0 EQU 1010H
1020 = INT_1 EQU 1020H
1030 = TIME_1 EQU 1030H
1040 = SERIAL EQU 1040H
0000 0111 AJMP START ;跳转到主程序起始点
0002 021000 LJMP INT_0 ;外中断0处理程序
0005 021010 LJMP TIME_0 ;定时中断0处理程序
0008 021020 LJMP INT_1 ;外中断1处理程序
000B 021030 LJMP TIME_1 ;定时中断1处理程序
000E 021040 LJMP SERIAL ;串行口中断程序
START:
0011 00 NOP
0000 END
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 2
08-26-96
;%T Symbol Name Type Value
INT_0 . . . . . . . . . . . . . I 1000
INT_1 . . . . . . . . . . . . . I 1020
SERIAL. . . . . . . . . . . . . I 1040
START . . . . . . . . . . . . . L 0011
TIME_0. . . . . . . . . . . . . I 1010
TIME_1. . . . . . . . . . . . . I 1030
;%Z
00 Errors (0000)
由列表文件,可以绘出代码在ROM中的映象图如下:
代码
01H11H02H10H00H02H10H10H02H10H20H地址
00H01H02H03H04H05H06H07H08H09H0AH代码
02H10H30H02H10H40H00H地址
0BH0CH0DH0EH0FH10H11H12H13H14H15由上面的映象图可知,在03H处的代码为10H,而不是我们要的02H,所以外断程序INT_0不能被正确执行,其它各中断程序的情况同样如此,如在0BH处,本来存放的应当是定时器0中断程序,但按上述的映象图,0BH处开始的3个代码是:02H,10H,30H,这是定时器1的入口地址,所以,如果定时器0发生中断,所执行的其实是定时器1的中断程序,这当然不对。
例2:
INT_0 EQU 1000H
TIME_0 EQU 1010H
INT_1 EQU 1020H
TIME_1 EQU 1030H
SERIAL EQU 1040H
AJMP START ;跳转到主程序起始点
ORG 0003H
LJMP INT_0 ;外中断0处理程序
ORG 000BH
LJMP TIME_0 ;定时中断0处理程序
ORG 0013H
LJMP INT_1 ;外中断1处理程序
ORG 001BH
LJMP TIME_1 ;定时中断1处理程序
ORG 0023H
LJMP SERIAL ;串行口中断程序
START:
NOP
END
上面的程序经过汇编后列表文件如下:
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 1
08-26-96
1000 = INT_0 EQU 1000H
1010 = TIME_0 EQU 1010H
1020 = INT_1 EQU 1020H
1030 = TIME_1 EQU 1030H
1040 = SERIAL EQU 1040H
0000 0126 AJMP START ;跳转到主程序起始点
0003 ORG 0003H
0003 021000 LJMP INT_0 ;外中断0处理程序
000B ORG 000BH
000B 021010 LJMP TIME_0 ;定时中断0处理程序
0013 ORG 0013H
0013 021020 LJMP INT_1 ;外中断1处理程序
001B ORG 001BH
001B 021030 LJMP TIME_1 ;定时中断1处理程序
0023 ORG 0023H
0023 021040 LJMP SERIAL ;串行口中断程序
START:
0026 00 NOP
0000 END
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 2
08-26-96
;%T Symbol Name Type Value
INT_0 . . . . . . . . . . . . . I 1000
INT_1 . . . . . . . . . . . . . I 1020
SERIAL. . . . . . . . . . . . . I 1040
START . . . . . . . . . . . . . L 0026
TIME_0. . . . . . . . . . . . . I 1010
TIME_1. . . . . . . . . . . . . I 1030
;%Z
00 Errors (0000)
由列表文件,可以绘出代码在ROM中的映象图如下:
代码
01H11H 02H10H00H地址
00H01H02H03H04H05H06H07H08H09H0AH代码
02H10H10H 02H01H20H地址
0BH0CH0DH0EH0FH10H11H12H13H14H15H代码
02H10H30H地址
16H17H18H19H1AH1BH1CH1DH1EH1FH20H代码
02H10H40H00H地址
21H22H23H24H25H26H27H28H29H2AH2BH由映象图可知,各中断程序的代码都在其规定地址处,一旦产生中断即可执行相应的程序。至于图中未填的部分(如02H),根据各编程器不同而不同,一般为FFH或00H。
二、END
END语句标志源代码的结束,汇编程序遇到END语句即停止运行。若没有END语句,汇编将报错。END语句有一个参数,可以是数值0,也可以是表达式,其格式是:
标号: END 表达式
它的值就是程序的地址并且作为一个特殊的记录写入HEX文件。若这个表达式省略,HEX文件中其值就是0。
三、EQU
EQU以及其它一些符号定义伪指令用来给程序中出现的一些符号赋值。对这些符号名的要求与其它符号相同,即长度不限,大小写字母可互换并且必须以字母开头。
由等值指令定义的符号是汇编符号表的一部分。等值伪指令有两种形式。一种用EQU,另一种用字符“=”即
符号名 EQU 表达式
符号名 = 表达式
两种形式的效果是一样的。符号名在左边,其对应的值在右边。值可以是变元,其它的符号名或表达式。只要在两遍扫描中求出表达式的值就行,否则引用该符号名时将报错。当表达式的值是字符串时,只取后两个字符。若串长为1,高位字节被置0,符号名的值被打印在程序清单中。由等值伪指令定义的符号名不允许重名。如果经定义的符号名被重定义,则汇编将报出错,并且这个符号名按新定义的处理,最好不要在程序中出现重名。
例:0469= ABC EQU 469H
0464= XY EQU ABC-5
02F0= JK = 752
0754 XYJK = XY+JK
在列表文件中最左边的数字不是这些伪指令所在的地址而是通过汇编后赋给符号名的值。第一条符号名ABC被起来469H,第二条XY被赋于ABC-5,因此XY的值为469H-5=464H,JK的值为752(即2F0H),XYJK的值XY+JK=464H+2F0H=754H
四、SET
SET伪指令有些类似于等值伪指令,它定义了一个整数类型的符号名,它的格式为
符号名 SET 表达式
SET伪指令与等值伪指令的唯一区别在于SET伪指令所定义的符号名右以在程序中多次定义,而不报错。
例:002D= K57 SET 101101B
8707= K57 SET 34567
五、DATA与BYTE
DATA与BYTE都是用来定义字节类型的存储单元,赋予字节类型的存储单元一个符号名,以便在程序中通过符号名来访问这个存储单元,以帮助对程序的理解。
BYTE与DATE之间的区别类似于EQU和SET,BYTE伪指令不能定义重名。
六、WORD
WORD伪指令类似于DATE伪指令,只是WORD伪指令定义了一个字类型的符号名,其格式为:
符号名 WORD 表达式
0027= VAL31 WORD 39
0021= PAR7 WORD 21H
一个字由2个字节组成。当然,因为8051汇编语言集没有字操作,所以程序执行时,只处理字节。WROD伪指令仅仅允许用户定义一个认为是字的存储位置。
七、BIT
BIT伪指令定义了一个字位类型的符号名,其格式为:
符号名 BIT 表达式
这里表达式的值是一个位地址,这个伪指令有助于位的地址符号化。
例:
002F= LOG3 BIT 47
0014= Y731 BIT 14H
八、ALTNAME
替换名(ALTNAME)伪指令提供用户一种手段,以定义一个符号名来替换一个保留字,此后这个答名与被替换的保留字均可等效地用于程序中。任何保留类型的答名均可被替换。替换名伪指令格式为:
ALTNAME 保留字,新名
例:
0002= ALTNAME R2 COUNT
013A EA MOV A,R2
013B E502 MOV A,COUNT
九、DB
DB伪指令用于定义一个连续的存储区,给该存储区的存储单元赋值。该伪指令的参数即为存储单元的值,在表达式中对变元个数没有限制,只要此条伪指令能容纳在源程序的一行内,其格式为:
标号: DB 表达式
只要表达式不是字符串,每一表达式值都被赋给一个字节。计算表达式值时按16位处理,但其结果只取低8位,若多个表达式出现在一个DB伪指令中,它们必须以逗号分开。
表达式中有字符串时,以单引号“'”作分隔符,每个字符占一个字节,字符串不加改变地被存在各字节中,并不将小写字母转换成大写字母。
例如:
DB 00H 01H 03H 46H
DB 'This is a demo!'
十、DW
DW为以字节为单元(十六位二进制)来给一个的存储区赋值,其格式为:
标号: DW 表达式
例如:
0000 3035 D46B DW 12341,54379,10110100101110B
0004 2D2E
0006 4344 4243 DW 'ABCD','BC','A'
000A 0041
000C 2868 02E8 DW 456*375h,83+295h,'YZ',72h-456
0010 595A FEAA
十一、DS
DS为定义存储内容的伪指令,用它定义一个存储区,并用指定的参数填满该存储区。
DS伪指令包含两个变元,第一个变元定义了存储区的长度的字节数,在汇编时,汇编程序将跳过这些单元把其它指令汇编在这些字节之后,因此在使用DS伪指令时第一个变元不可活力第二个变元表示在这些单元中真入什么值,第二个变元可以活力活力时这些字节将不处理。下例中0173处有一条DS 9,则空出9个字节,下一第指令被汇编到017C处;在017C处空出1BH个单元,在这些字节中被27H所填充。
DS指令的格式如下:
标号: DS 表达式1,表达式2
表达式1定义了存储区的长度(以字节为单位)。这个变元不能省略。表达式2是可选择的,它的值低8位用以填入所定义的存储区。
若省略则这部分存储单元不处理。
例:
0000 04 INC A
0001 DS 9
000A 04 INC A
000B DS 1BH,27H
0026 04 INC A
十二、INCLUDE
INCLUDE伪指令用于链接源文件,即将一个源文件插入到另一个源文件中。它有一个参数,指出将要插入的文件名,该文件名中可包括驱动器名和路径名。若文件没有扩展名,则默认为是ASM。但待插入的文件必须是可以打开的。若文件打开操作失败,则产生致命错误,汇编将停止运行。反之,汇编程序将文件内容读入并按源代码处理。当遇到文件结速符时,汇编程序返回到INCLUDE伪指令处继续身下处理源程序。被插入的文件在程序清单中以“I”开头。
本宏汇编版本支持级嵌套,可在程序中用INCLUDE伪指令插入任意多个文件,但是,在一般情况下DOS允许打开的文件数量是有限的,如果用户需要打开较多的文件,则必须在CONFIG.SYS文件中加入FILES=40或更多的值,若超过8级嵌套或打开的文件太多,则产生致命错误,汇编中止运行。
INCLUDE伪指令提供了模块化程序设计手段,在汇编程序处理主程序时,模块被插入,尽管这不等价于链接和装配可重定位的目标模块,但它具有类似的功能,被插入的源文件中不应该包含END伪指令,否则,汇编就会提前停止运行,END伪指令只能出现在主程序中。此外,在主程序进行汇编前所有附加的源文件必须通过汇编,产生相应的HEX及LST文件,由于附加的文件没有END伪指令,因此,附加文件汇编时,汇编程序将显示:“没有结束语句”的错误,但并不影响与主程序的链接。
下面是一个使用INCLUDE伪指令的例子,其主程序的源文件MAIN.ASM为:
;MAIN.ASM
ORG 27H
START:
CLR A
MOV R3,A
INCLUDE MOD1
INC R5
INCLUDE MOD2.ASM
DEC R3
END START
主程序为带有END伪指令的完整的源文件。程序中有两INCLUDE伪指令,分别将两附加的文件MOD1.ASM及MOD2.ASM链接到主程序中。以下是这两个文件。
;MOD1.ASM
MOV R2,#31H
MOV R5,#18H
;MOD2.ASM
MOV R6,#47H
ANL A,#07H
MOV R1,A
注意MOD1.ASM及MOD2.ASM均没有END指令。
在进行汇编时必须先对MOD1.ASM和MOD2.ASM进行汇编,然后在汇编MAIN.ASM,由于上两个文件没有END伪指令,所以在汇编时会出现错误提示,不用管它,继续下面的工作,就可以得到正确的结果。
以下是形成的列表文件:
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 1
;MOD2.ASM
MOV R6,#47H
ANL A,#07H
MOV R1,A
08-27-96
;MAIN.ASM
0027 ORG 27H
START:
0027 E4 CLR A
0028 FB MOV R3,A
I INCLUDE MOD1
I ;MOD1.ASM
I0029 7A31 MOV R2,#31H
I002B 7D18 MOV R5,#18H
I
002D 0D INC R5
I INCLUDE MOD2.ASM
I ;MOD2.ASM
I002E 7E47 MOV R6,#47H
I0030 5407 ANL A,#07H
I0032 F9 MOV R1,A
0033 1B DEC R3
0027 END START
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 2
08-27-96
;%T Symbol Name Type Value
START . . . . . . . . . . . . . L 0027
;%Z
00 Errors (0000)
十三、TITL
TITLE伪指令用于在列表文件页头建立一个标题,其格式为:
$TITLE 标题行
这里标量行就是将出现在页头的标量与通常的字符串定义不同。这里标量行不加引号。汇编从$TITLE之后的第一个可打印字符开始,到回车符之间的字符串作为标量标量的最大长度是60个字符,基标量行省略,则标题行为空行。若TITLE伪指令在一页,它说明的标量行包含在本页,否则,标题将出现在下页页头。
十四、PAGE
PAGE伪指令用于形成新的一中定义一面的行数。其格式为:
$PAGE 表达式
若表达 式缺省则开始新的一页,若有表达式,则每页行数重新定义。汇编开始时页长为66行。一页中除出页外,剩余55行用于打印源程序,这一格式适用于标准打印纸。
如果变元值小于66,页内可打印的源代码行将相应减少。页长最小值为12。若小于12时,每页内除页上只打印一行源程序。
页长变元是16位字节,因而每页最长可定义到65535行,这时分页打印变为连续打印,在屏幕显示程序清单或在卷筒纸上打印程序清单时,常常使用连续打印,如果在启动汇编时用/N选项,页长就是65535。
十五、LIST与NOLIST
它们的格式为:
$LIST
$NOLIST
LIST伪指令使汇编时主生程序清单,但即使不用该指令,汇编也会自动产生清单。但如果使用了NOLIST伪指令后需要继续主生清单则必须使用LIST伪指令。
NOLIST伪指令使汇编时不产生清单,所有包含此伪指令及在这条伪指令之后的语句都不进入列表文件。当不需要任何列表文件,并且不需要显示程序清单时,可以在启动汇编时不加.L附加项,且在源代码的第一行加上NOLIST指令。
使用NOLIST伪指令与附加项/L不同之处是NOLIST伪指令可加在源程序中,与LIST伪指令配合使用,使源程序中某些部分不产生清单。而不加附加项/L则不产生任何程序清单。不过,不管有无$NOLIST伪指令,程序在汇编时检查到的错误都将在屏幕上显示出错的源代码行及错误信息
十六、NOCODE
其格式为 $NOCODE
NOCODE伪指令使得在汇编时,条件汇编程序结构中那些真值为假的条件不产生清单。有关条件汇编结构在下面介绍。如果没有这条伪指令,汇编将主生所有条件下的清单,不论其真值是否为真。但是假的条件,不产生目标码。而NOCODE伪指令使汇编清单中只列出那些由汇编程序用到的部分,因此,当使用NOCODE伪指令时,程序清单与源程序并非逐行对应。
- 第六节 宏 指 令
宏指令是汇编功能的另一种扩充。在我们编写程序时,常常会遇到这种情况:一些程序段它们的结构相同,但在不同情况下使用的参数不同,这时可以把那变化的参数定义为形参,采用宏指令的方法来解决。在汇编时,汇编程序将填入相应的实参,把它们逐条汇编并生成到相应的程序中去。
一个宏指令是汇编语句的一个代码段,其中可以包含有形参,所谓形参是指它的值由引用宏指令时定义,在编写宏指令时它仅仅是一个符号而已。每个宏指令都有一个宏指令名,在程序中可以通过引用它的名字以及给定所需要的参数使用它。宏指令通在汇编语句的命令中使用,参数出现在参数部分中,宏指令每次在程序中引用时,通过实参对形参的替换,使程序中宏指令中的形参获得实际值。一个宏指令必须在它的第一次使用之前被定义。通常,所有的宏指令的定义都集中在程序的首部,宏指令的定义格式如下:
宏指令名 MACRO 形参表
(宏指令体)
ENDM
MACRO和ENDM语句是宏指令定义的标志,它们指出了宏指令的首部和尾部。每个MACRO语句必须有一个匹配的ENDM语句。这些语句不使用标号。
宏指令名按照汇编程序中的其它符号名约定,宏指令名被加到汇编程序符号表中,并赋给一个宏指令类型。因而它必须有唯一的名字。其名字将包括在汇编程序列表输出的汇编符号清单中。
形参表是在宏指令定义中使用的形参集合。这些参数用符号表示,且仅仅在宏指令定义中使用,而不被加到汇编符号表中。它们只能在宏指令中的代码块中使用。各个形参在参数表中应该用逗号隔开。宏汇编中,每个宏指令最多可支持40个参数。但是,参数表必须与MACFO指令在同一行上,由于每一代码行最大长度为80个字符,因形参的数量也受这个条件限制,参数表是任选的,所以一个宏指令也可以是无参的。
宏指令是程序块,它可以是任何汇编语言或伪指令,但由于汇编程序不支持嵌套的宏指令,因此,在宏指令体内不能再使用宏指令或对其他宏指令进行定义。形参可以在代码段语句的任何域中使用,包括标号、命令、参数和注解。形参经常被用作为参数,但是也有可以在其它域中使用。
一个宏指令的主要特点是形参的使用。当宏指令在程序段中被调用时,调用处的实际参数替换定义中的形参。因此,宏指令每次被调用时,通过使用时提供的一个唯一的实际参数集,就生成一个唯一的代码段。一个简单的宏指令定义如下所示:
ABC MACRO P1,P2,P3
MOV A,P1
MOV R4,P2
P3
DEC R5
ENDM
JK MACRO P1,X5,OPC1,ARG1,ARG2
INC X5
MOV A,P1
OPC1 ARG1,ARG2
ENDM
CLR A
MOV R3,A
ABC R2,#25H,INC R3
NOP
JK @R0,R2,ADD,A,R6
NOP
ABC #4FH,#39H,DEC A
NOP
END
该程序中前部是两个宏指令定义,前一个宏指令为ABC,第二个为JK。后部为主程序,在主程序中ABC、JK分别被引用。汇编后的列表文件如下:
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 1
08-27-96
ABC MACRO P1,P2,P3
MOV A,P1
MOV R4,P2
P3
DEC R5
ENDM
JK MACRO P1,X5,OPC1,ARG1,ARG2
INC X5
MOV A,P1
OPC1 ARG1,ARG2
ENDM
0000 E4 CLR A
0001 FB MOV R3,A
0002 ABC R2,#25H,INC R3
+0002 EA MOV A,R2
+0003 7C25 MOV R4,#25H
+0005 0B INC R3
+0006 1D DEC R5
0007 00 NOP
0008 JK @R0,R2,ADD,A,R6
+0008 0A INC R2
+0009 E6 MOV A,@R0
+000A 2E ADD A,R6
000B 00 NOP
000C ABC #4FH,#39H,DEC A
+000C 744F MOV A,#4FH
+000E 7C39 MOV R4,#39H
+0010 14 DEC A
+0011 1D DEC R5
0012 00 NOP
0000 END
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 2
08-27-96
;%T Symbol Name Type Value
ABC . . . . . . . . . . . . . . M 0000
JK. . . . . . . . . . . . . . . M 0000
;%Z
00 Errors (0000)
在列表文件中,在调用宏指令处,实参数被充填到宏指令的形参处,并把宏指令体扩充到程序中形成代码。在这个例子中实参不仅代替了指令中的参数部分,而且还能代替整个指令。文件中最前列带“+”的语句为引用宏指令扩展的部分。宏指令被引用时,参数表上实参表应与所替代的形参相对应。在表中出现的参数必须用逗号隔开,汇编程序取逗号之间或与第一个逗号之间,或最后一个逗号与行尾之间的任何字符作为实际参数。这样,可以使用多个符号,甚至一个整条指令作为一个参数,只要在数中没有逗号就行。一个逗号不能作为一个参数的一部分被传递。汇编程序将滤去每个参数的前面和后面的空格,仅仅保留第一个到最后一个可打印字符。
此外,宏指令的实际参数表可以不与形参表完全匹配,即实际参数的数量可以多于或少于形参表中的形参个数。当参数空缺时,相应的形参在宏扩展时被取消,如果实际参数比形参多,则多余的实际参数将被忽略。最后一个在表中间的参数可以通过在参数表中间放两个逗号来赋空值。参见下面的例子。
ABC MACRO P1,P2,P3
MOV A,P1
MOV R4,P2
P3
DEC R5
ENDM
JK MACRO P1,X5,OPC1,ARG1,ARG2
INC X5
MOV A,P1
OPC1 ARG1,ARG2
ENDM
NOP
JK #33H,R3,NOP
SWAP A
NOP
JK R4,R2,ADDC,A,@R1,#66H
NOP
JK #40H,R4,,DEC R2
NOP
END
宏指令的另一个特点是形参可以被用作一个标号,如果一个形参被用作一个标号,当引用宏指令时,实参将成为标号。这就要求每次宏指令调用时,该实参不能相同。以避免标号重复定义错误。例:
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 1
08-27-96
ABC MACRO P1,P2,P3
MOV A,P1
MOV R4,P2
P3
DEC R5
ENDM
JK MACRO P1,X5,OPC1,ARG1,ARG2
INC X5
MOV A,P1
OPC1 ARG1,ARG2
ENDM
0000 00 NOP
0001 JK #33H,R3,NOP
+0001 0B INC R3
+0002 7433 MOV A,#33H
+0004 00 NOP ,
0005 C4 SWAP A
0006 00 NOP
0007 JK R4,R2,ADDC,A,@R1,#66H
+0007 0A INC R2
+0008 EC MOV A,R4
+0009 37 ADDC A,@R1
000A 00 NOP
000B JK #40H,R4,,DEC R2
+000B 0C INC R4
+000C 7440 MOV A,#40H
+000E 1A DEC R2,
000F 00 NOP
0000 END
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 2
08-27-96
;%T Symbol Name Type Value
ABC . . . . . . . . . . . . . . M 0B0D
JK. . . . . . . . . . . . . . . M 0000
;%Z
00 Errors (0000)
若在宏指信中使用标号,由于这些标号不是形参,所以不会被实参所替代。这样,在程序中多次引用是否会出现标号重复呢?在作宏汇编时,汇编程序会在所有标号尾部增加一个唯 一的四位数,以使多次引用时标号不会重复。例:
LMAC MACRO P1,P2,L1
ANL A,P1
JZ L1
ORL A,P2
L1: MOV @R1,A
INC R1
ENDM
MOV R1,#2AH
MOV A,#65H
LMAC R3,#0F0H,R3ANDA0
MOV A,#23H
LMAC R5,#07H,R5ANDA0
NOP
END
程序中有二个标号MATCH1及NOMATCH,宏指令被二次引用,经汇编后其列表文件如下:
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 1
08-27-96
LMAC MACRO P1,P2,L1
ANL A,P1
JZ L1
ORL A,P2
L1: MOV @R1,A
INC R1
ENDM
0000 792A MOV R1,#2AH
0002 7465 MOV A,#65H
0004 LMAC R3,#0F0H,R3ANDA0
+0004 5B ANL A,R3
+0005 6002 JZ R3ANDA0
+0007 44F0 ORL A,#0F0H
+0009 F7 R3ANDA0: MOV @R1,A
+000A 09 INC R1
000B 7423 MOV A,#23H
000D LMAC R5,#07H,R5ANDA0
+000D 5D ANL A,R5
+000E 6002 JZ R5ANDA0
+0010 4407 ORL A,#07H
+0012 F7 R5ANDA0: MOV @R1,A
+0013 09 INC R1
0014 00 NOP
0000 END
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 2
08-27-96
;%T Symbol Name Type Value
LMAC. . . . . . . . . . . . . . M 0000
R3ANDA0 . . . . . . . . . . . . L 0009
R5ANDA0 . . . . . . . . . . . . L 0012
;%Z
00 Errors (0000)
若在宏指令中使用标号,由于这些标号不是形参,所以不会被实参所替代。这样,在程序中多次引用是否会出现标号重复呢,这一点宏汇编程序已作了考虑,在作宏汇编时,汇编程序会在所有标号尾部增加一个唯一的四位数,以使多次引用时标号不会重复。下面的例子说明了多次引用宏指令时内部标号的处理。
例:
CHK MACRO N1,N2
XRL A,N1
JZ MATCH1
XRL A,N2
JNZ NOMATCH
ADD A,#15H
MATCH1:
ADD A,#7
MOV R4,A
NOMATCH:
MOV R5,A
ENDM
MOV A,#23H
CHK #65H,R3
MOV A,@R0
CHK R2,#23H
NOP
END
程序中有二个标号MATCH1及NOMATCH。宏指令被二次引用。经汇编后其列表文件如下:
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 1
08-27-96
CHK MACRO N1,N2
XRL A,N1
JZ MATCH1
XRL A,N2
JNZ NOMATCH
ADD A,#15H
MATCH1:
ADD A,#7
MOV R4,A
NOMATCH:
MOV R5,A
ENDM
0000 7423 MOV A,#23H
0002 CHK #65H,R3
+0002 6465 XRL A,#65H
+0004 6005 JZ MATCH10001
+0006 6B XRL A,R3
+0007 7005 JNZ NOMATCH0001
+0009 2415 ADD A,#15H
+ MATCH10001:
+000B 2407 ADD A,#7
+000D FC MOV R4,A
+ NOMATCH0001:
+000E FD MOV R5,A
000F E6 MOV A,@R0
0010 CHK R2,#23H
+0010 6A XRL A,R2
+0011 6006 JZ MATCH10002
+0013 6423 XRL A,#23H
+0015 7005 JNZ NOMATCH0002
+0017 2415 ADD A,#15H
+ MATCH10002:
+0019 2407 ADD A,#7
+001B FC MOV R4,A
+ NOMATCH0002:
+001C FD MOV R5,A
001D 00 NOP
0000 END
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 2
08-27-96
;%T Symbol Name Type Value
CHK . . . . . . . . . . . . . . M 0000
MATCH10001. . . . . . . . . . . L 000B
MATCH10002. . . . . . . . . . . L 0019
NOMATCH0001 . . . . . . . . . . L 000E
NOMATCH0002 . . . . . . . . . . L 001C
;%Z
00 Errors (0000)
从列表文件中我们可以看到,汇编程序已对宏指令中的标号作了处理,在第一次引用时,宏指令中的标号MATCH1及NOMATCH分别衩扩展为MATCH1001及NOMATCH0001,而第二次被引用时相应的标号被扩展为MATCH1002及NOMATCH0002,从而避免在同一程序中标号的重复。
“&”在宏指令中作为连接操作符。“&”通常出现在带有形参的字符串中。为了区分字符串中哪些字符是形参,需要在形参与固定字符间加入“&”例如:
CON MACRO X1,X2,SI,L1
MOV R2,#X1
MOV R2,#X1
ADD A,#X1+5-X2
DB 'STRING&SI&'
JNZ LAB&L1
LAB&L1:
XRL A,R3
ENDM
CLR A
CON 23H,65H,SPECIAL,57
END
列表文件如下:
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 1
08-27-96
CON MACRO X1,X2,SI,L1
MOV R2,#X1
MOV R2,#X1
ADD A,#X1+5-X2
DB 'STRING&SI&'
JNZ LAB&L1
LAB&L1:
XRL A,R3
ENDM
0000 E4 CLR A
0001 CON 23H,65H,SPECIAL,57
+0001 7A23 MOV R2,#23H
+0003 7A23 MOV R2,#23H
+0005 24C3 ADD A,#23H+5-65H
+0007 53 54 52 DB 'STRINGSPECIAL'
+000A 49 4E 47 53 50 45 43 49 41 4C
+0014 7000 JNZ LAB57
+ LAB57:
+0016 6B XRL A,R3
0000 END
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 2
08-27-96
;%T Symbol Name Type Value
CON . . . . . . . . . . . . . . M 0000
LAB57 . . . . . . . . . . . . . L 0016
;%Z
00 Errors (0000)
在算术表达式中所有的加数否需要连接操作符,这是因为算术操作符本身具有符号与数之间的自然分隔符作用。立即操作数(由#符号引导)可以用一个空格把它与一个形参分开,在这种情况下也不需要字符“&”。
最后,所有的宏指令所支持的功能可以合并起来使用,从而组成一个相当复杂的宏指令,例如宏指令可以包括条件结构,或者宏指令可以是一个条件结构的一部分,但是宏指令不可以嵌套,条件结构不可以超过宏指令的边界。即宏指令必须完全在一个条件结构中,或者条件结构必须在一条宏指令中。可以通过形参来决定条件表达式的值。当宏指令衩调用时,传递的实际参数值将决定每一条指令的生成代码。如:
CON MACRO C1,C2,P1,P2,P3
MOV A,P1
IF C1
INC R3
MOV R4,A
ENDIF
IF C2
MOV R5,P2
ADDC A,P3
ELSE
MOV R1,P2
XRL A,R3
ENDIF
MOV @R1,A
ENDM
CLR A
MOV R3,#2AH
CON 0,0,#23H,#65H,R6
END
列表文件如下:
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 1
08-27-96
CON MACRO C1,C2,P1,P2,P3
MOV A,P1
IF C1
INC R3
MOV R4,A
ENDIF
IF C2
MOV R5,P2
ADDC A,P3
ELSE
MOV R1,P2
XRL A,R3
ENDIF
MOV @R1,A
ENDM
0000 E4 CLR A
0001 7B2A MOV R3,#2AH
0003 CON 0,0,#23H,#65H,R6
+0003 7423 MOV A,#23H
+ IF 0
+ INC R3
+ MOV R4,A
+ ENDIF
+ IF 0
+ MOV R5,#65H
+ ADDC A,R6
+ ELSE
+0005 7965 MOV R1,#65H
+0007 6B XRL A,R3
+ ENDIF
+0008 F7 MOV @R1,A
0000 END
The Cybernetic Micro Systems 8051 Family Assembler, Version 3.03 Page 2
08-27-96
;%T Symbol Name Type Value
CON . . . . . . . . . . . . . . M 0000
;%Z
00 Errors (0000)
程序中宏指令带有条件结构,并可以通过形参C1确定判别条件。
第七节 汇编程序的出错信息
一个汇编程序往往不可能一次编写就完全正确,总会有一些错误存在,MASM51在汇编时可以对源程序中存在的一些语法错误判别,并给出出错信息,出错信息将出现在屏幕上,并被写入到列表文件中去,我们可以根据出错信息对照源程序进行修改。
一般出错信息的格式为:
描述性信息(aaaa)
描述性信息向用户提供错误的类型,以提供用户分析与纠正错误。aaaa是四位十六进制数字,它指出的是上一个错误出现的地址,以便 用户在较长的列表文件中找到上一个错误的所在。
错误数量的报告记录在列表文件的最后,其格式为:
XX Errors(0000)
其中XX为十六进制数表示的错误数量。
宏汇编能提供的错误信息有:
(1)Undefined symbol
表示在源程序的语句中使用的符号未被定义,实际上可能是符号名拼错或在源程序中缺少一个该符号的等价指令。
(2)Missing Argumentin Expression
表示表达式中算术运算符后面没有操作数。
(3)Unblaanced Parentheses
每一个左括号必须与一个右括号配套,如果表达式中多余或缺少括号,将出现上述提示信息。
(4)ILLEGAL EQUATE
表示把两个不同类型的量用等价伪指令连在一起,例如保留字的值1寄存器名等,它们不允许用等值伪指令来改变。
(5)BADLY ROFMED ARGUMENT
表示一个数字参数使用了非法的数字。例如二进制中出现除0与1以外的其他数字。
(6)MISSING END STATEMENT
源文件结束处没有END语句。源文件的结束处必须有一个END语句。
(7)LABEL NAME CONFLICTS WITH SYMBOL NAME
标号名与符号名相同。
(8)MULTIPLY DEFINED LABEL
相同标号多次出现。
(9)LABEL ADDRESS CHANGED ON PASS 2
标号的值在两次扫描中改变。这个错误往往是在源程序的不同处用了同一个标号名,只要用字处理软件查找这个标号,就可以改正。
(10)UNRECOGNIZED STATEMENT OR UNDEFINED ARGUMENT
表示有未定义的指令、或源代码。出现一个语法类型的错误。该类错误可能由拼错命令名产生或由于记错命令的用法产生,改正起来较为简单。
(11)ADDRESS OUT OF RANGE
转移地址超出该语句的范围,例如SJMP转移地址超过+127~-128的范围,另外JB、JNB、JZ、JNZ等指令也容易发生类错误。如果是SJMP、AJMP、ACALL等指令发生错误,则改正较为容易,只要将指令改为范围较在的即可,如将SJMP改为AJMP,将AJMP改为LJMP,将ACLL改为LCALL等,如果是JZ、JB、JNZ、JNB等指令发生此类错误,则要使用一些技工技巧,如下面程序1所示,NEXT标号已超出了JZ指令作用的范围,则可以改成程序2,使用一个中间标号NEXT1,然后从NEXT1跳转到NEXT处。
例:
.
.
JB NEXT1
.
.
.
NEXT1:
AJMP NEXT
.
.
(12)VALUE OUT OF RANGE
用一个非法的值来说明一个语句。