C51 汇编伪指令
- 格式:doc
- 大小:75.50 KB
- 文档页数:6
第五章 MCS-51宏汇编伪指令伪指令是对汇编起某种控制作用的特殊命令,但自身并不产生机器码,不属于指令系统,而仅仅为汇编服务的一些指令,因此称为伪指令。
其格式与通常的操作指令一样,并可加在汇编程序的任何地方,但它们并不产生机器指令。
许多伪指令要求带参数,这在定义伪指令时由“表达式”域指出,任何数值与表达式均可以作为参数。
不同汇编程序允许的伪指令并不相同,以下所述的伪指令适用于Intel公司的MASM51系统,但一些基本的伪指令在大部份汇编程序中都能使用,当使用其它的汇编程序版本时,只要注意一下它们之间的区别就可以了。
MASM51中常用的伪指令共分为五大类:1. 程序计数与结束伪指令:ORG、END;2. 符号定义伪指令:EQU、SET、DATA、BYTE、WORD、BIT、ALTNAME、DB、DW、DS;3. 附加文件伪指令:INCLUDE;4. 程序清单格式化伪指令:TITLE、PAGE;5. 一般控制伪指令:LIST、NOLIST、NOCODE。
下面简要介绍一下各条伪指令的功能:5.1 ORG功能:程序计数伪指令,用于设置由表达式决定的初始地址,ORG也称为起始伪指令。
表达式缺省为0。
格式:ORG 16位地址例如:ORG 0100HSTART:MOV A,#05HADD A,#08HMOV 20H,AORG 0100H表示该伪指令下面第一条指令的起始地址是0100H,即“MOV A,#05H”指令的首字节地址为0100H,或标号START代表的地址为0100H。
5.2 END功能:是汇编语言源程序的结束标志。
在END以后所写的指令,汇编程序不再处理。
一个源程序只有一个END指令,放在所有指令的最后。
源程序中若没有END语句,汇编将报出错。
5.3 EQU功能:将一个数值或寄存器名赋给一个指定的符号名。
格式:符号名 EQU 表达式或符号名EQU 寄存器名符号名=表达式例如:DELY EQU 3344HDELY1 EQU 30HPP EQU R0ORG 0000HJMP MAINORG 0050HMAIN:MOV DPTR,#DELY ;(DPTR)=3344HMOV A,#DELY ;(A)=44HMOV A,#DELY1 (A)=30HMOV PP,#10 ;(PP)=10MOV A,PP ;(A)=10NOPEND5.4 SET功能:SET指令的功能与EQU指令类似,不同的是,用SET指令定义过的符号名可被重新定义。
第一个闪烁的灯:(P2口接LED,P2输出低时LED点亮)/**************************************************** /ORG 0000H ;ORG伪指令,定义程序起始地址AJMP START ;跳转到真正的程序起始地址ORG 0040H ;定义START地址START: SETB P2.0 ;设置P2口第一位为高,则LED灭(本开发板LED接低电平亮) LCALL DELAY;调用延时程序CLR P2.0 ;清除P20端口,则LED亮LCALL DELAY ;调用延时程序AJMP START ;返回开始处继续执行之前的步骤DELAY: MOV R7,#250 ;延时的外循环数放在R7D1: MOV R6,#250 ;内循环数放在R6内D2: DJNZ R6,D2 ;R6内数据自减,直至为0则继续执行下一行代码DJNZ R7,D1 ;R7内数据自减,直至为0则继续执行下一行代码RET ;返回指令END ;伪代码,告诉编译器程序到此结束/**********************************************************/#include <reg51.h> //包含51系列单片机标准头文件sbit P20=P2^0; //定义P20为一个bit寄存器(sbit定义一个位寄存器的关键字)void delay(int k); //声明delay函数,有形参int kvoid main() //主函数开始{while (1) //死循环,即程序一直运行这里面的命令{P20=1; //P20为高,LED灭=SETB P20delay(300); //延时P20=0; //P20为低,LED亮=CLR P20delay(300); //延时}}void delay(int k) //延时函数{unsigned char i; //定义i为无符号字符型,最大255for( ;k>0;k--) //外循环{for(i=250;i>0;i--); //内循环}}第一个简单的LED流水灯:(P2口接LED,P2输出低时LED点亮)************************************************************************** ORG 0000H ;ORG伪指令,定义程序起始地址AJMP START ;跳转到真正的程序起始地址ORG 0040H ;定义START地址START: MOV A,#0FEH ;将寄存器A赋值FEH,即1111 1110MOV P2,A ;将A赋值给P2端口,即P2=1111 1110,最低位LED亮LCALL DELAY ;延时LOOP: RL A ;将A循环左移,即A=1111 1101MOV P2,A ;再将A赋值给P2,则当前P2=1111 1101,第二个LED亮LCALL DELAY ;延时CJNE A,#7FH,LOOP ;A=7FH=0111 1111,则结束循环,否则跳转到LOOP处执行AJMP START ;如果A=7FH了,则跳转到START处重新开始执行DELAY: MOV R7,#250 ;延时的外循环数放在R7D1: MOV R6,#250 ;内循环数放在R6内D2: DJNZ R6,D2 ;R6内数据自减,直至为0则继续执行下一行代码DJNZ R7,D1 ;R7内数据自减,直至为0则继续执行下一行代码RET ;返回指令END ;伪代码,告诉编译器程序到此结束************************************************************************** #include <reg51.h> //包含头文件#define LED P2 //宏定义LED,即编译时程序出现LED处均用P2来代替void delay(int k); //声明delay函数,有形参int kchar code tab[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};//定义tab为代码型数组void main(void) //主函数开始{ int a; //定义a为变量LED=0xfe; delay(300);//P2=0xfe=1111 1110,最低位LED亮while(1) //循环体{for(a=1;a<8;a++) //8次循环体{LED=tab[a]; delay(300);//将数组内数据赋值给P2=tab[1]=0xfd=1111 1101}//for结束for(a=6;a>=0;a--) //7次循环体,为什么a=6???{LED=tab[a]; delay(300);//同上一个循环} //for结束} //while结束} //main结束void delay(int k) //延时函数{unsigned char i; //定义i为无符号字符型,最大255for( ;k>0;k--) //外循环{for(i=250;i>0;i--); //内循环}}一个按键控制流水灯(P1口接8接独立按键,按键按下拉低;P2口接LED,P2输出低LED点亮)************************************************************************** ORG 0000HAJMP STARTORG 0030HSTART: MOV P1,#0FFH; ;将P1写入1JB P1.0,$ ;等待P10=0,即P10被按下接地,我们的P1接了上拉电阻LCALL DELAY ;如果被按下了,延时125*200us,去除抖动JB P1.0,START ;再次判断是否被按下,是则顺序执行,否则跳转到STARTMOV A,#0FEH ;将寄存器A赋值FEH,即1111 1110MOV P2,A ;将A赋值给P2端口,即P2=1111 1110,最低位LED亮LCALL DELAY ;延时LOOP: RL A ;将A循环左移,即A=1111 1101MOV P2,A ;再将A赋值给P2,则当前P2=1111 1101,第二个LED亮LCALL DELAY ;延时CJNE A,#7FH, LOOP;当A=7FH是,LED已经循环完成1次AJMP START ;跳转到起始位置,接着等待按键DELAY: MOV R7,#250D1: MOV R6,#250D2: DJNZ R6,D2DJNZ R7,D1RETEND**************************************************************************#include <reg51.h>char code tab[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};sbit P10=P1^0; sbit P11=P1^1;void delay(int k);void main(){ char a; P2=0xfe;while(1) //等待P10被按下,则执行流水等程序,详见上节{ P1=0xff;delay(2000); //延时20ms左右去除抖动if(P10==0) //按下P10执行流水灯{ for(a=0;a<8;a++){ P2=tab[a];delay(60000);}}if(P11==0) //按下P11执行反向流水灯{ for(a=7;a>=0;a--){ P2=tab[a];delay(60000);}} }}void delay(int k) //最简单的C延时程序{ while(k--); } //直至K减为0则跳出利用中断方式实现一个灯的闪烁(P2口接LED阴极,LED阳极经过400欧电阻接VCC)************************************************************************** ORG 0000hAJMP STARTORG 000BH ;T0中断入口地址AJMP TIME0 ;跳转到真正的中断程序处START: CLR P2.0 ;取反p20,点亮最低位LEDMOV 30H,#0 ;30H地址置数,用于计算中断次数MOV TMOD,#00000001B ;设置定时器0,使用方式1,定时20msMOV TH0,#0B1H ;计数器初始值高8位MOV TL0,#0E0H ;计数器初始值低8位SETB EA ;开总中断允许位SETB ET0 ;开启T0定时器SETB TR0 ;开启T0定时器中断允许位AJMP $ ;死循环,等待中断产生TIME0: PUSH ACC ;进入中断首先将ACC\PSW压栈PUSH PSW ;然后重装计数器初始值MOV TH0,#0B1H ;计数器初始值高8位MOV TL0,#0E0H ;计数器初始值低8位INC 30H ;将30H内数据加1MOV A,30H ;然后判断是否计数到25次(即0.5秒)?CJNE A,#25,TIME01 ;未到25次,则跳转到TIME01处执行出栈操作CPL P2.0 ;到了25次,则取反P20端口MOV 30H,#0 ;同时将30H单元内数据清零TIME01: POP PSW ;出栈POP ACC ;出栈RETI ;中断返回用RETIEND ;伪指令,结束程序********************************************************************#include<reg51.h>sbit P20=P2^0;volatile int i; //声明一个变量为可能意外变化的量(相对于const而言)用语中断程序void main(){ P20=0; //先将P20位的LED点亮TMOD=0x01; //设置定时器T0工作在模式1TH0=0xb1; TL0=0xe0; //装T0的计数初值,12MHz,20ms计时EA=1; //开启总中断允许位ET0=1; //开启T0定时器TR0=1; //开启T0定时器中断允许位while(1); //循环等待中断发生}void time0(void) interrupt 1 //中断程序写法{ TH0=0xb1; TL0=0xe0; //重装T0的计数初值i++; if(i==25) //计数到25次(即0.5秒)??{i=0; //i清零P20=~P20;//取反LED端口,}}一个数码管循环显示0—F(P2口接数码管段码,P00接数码管阴极)**************************************************************************ORG 0000HAJMP STARTORG 0030HSTART: MOV 30H,#00H ;要显示的内容,写入30H单元内MAIN: MOV R5,#50 ;设置显示次数,越大时间越长main1: MOV A,30H ;将显示的数据送入A寄存器MOV DPTR,#TAB ;将TAB的地址送入DPTR寻址MOVC A,@A+DPTR ;将TAB+A处地址内的数据读出来送入A寄存器SETB P0.0 ;关闭数码管公共端,关闭显示,是为了去除显示重影MOV P2,A ;将这个数据写入P2段码端口CLR P0.0 ;打开显示LCALL DELAY ;调用延时程序DJNZ R5,main1 ;一共显示R5次INC 30H ;将30H内数据加1MOV A,30H ;将30H内数据送入寄存器A,继续上面的步骤CJNE A,#10H,MAIN ;判断是否到达16,是则运行下一行程序,否则跳转到MAIN AJMP START ;TAB为数码管显示段码值TAB: DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H,7FH,6FH,77H,7CH,39H,5EH,79H,71HDELAY: MOV R7,#50D1: MOV R6,#250D2: DJNZ R6,D2DJNZ R7,D1RETEND**************************************************************************#include <reg51.h>#define SEG P2sbit P00=P0^0; //Tab为数码管显示值,存入一个数组内unsigned char code Tab[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};void delay(int k); //延时函数,每次都会用到void main(){ int i;while(1){ for(i=0;i<16;i++) //循环16次,显示0-9,A,B,C,D,E,F一共16个数{P00=1; //将LED公共端置高,关闭显示,目的是为了去除显示重影SEG=Tab[i]; //将段码送到P2口,输出到LED段码端P00=0; //打开显示delay(50000);delay(50000); //延时,使人能看清楚}}}void delay(int k) //最简单的C延时程序{ while(k--); } //直至K减为0则跳出两个数码管循环显示00—99(P2口接数码管段码,P00接数码管各位阴极,P01接十位)************************************************************************** ORG 0000HLJMP STARTORG 0040HSTART: MOV 30H,#00H ;30H单元内放低位数据MOV 31H,#00H ;31H单元内放高位数据MOV P2,#00H ;将P2初始化为0CLR P0.1 ;设置低位显示0CLR P0.0 ;设置高位显示0LCALL DELY1 ;调用延时程序MAIN: MOV R5,#50 ;设置重复显示次数,越大显示时间越长LCALL DISP ;调用显示程序INC 30H ;将30内数据加1,即个位加1MOV A,30H ;将30H单元内数据送给A寄存器CJNE A,#0AH,MAIN ;判断30H内数据是否计到了10?未达到则跳转到MAINMOV 30H,#00H ;30H内数计到了10,则清零INC 31H ;并将31H内数据加1MOV A,31HCJNE A,#0AH,MAIN ;同理判断31H内数据是否到了10MOV 31H,#00H ;到了10则清零LJMP MAIN ;并跳转到MAIN处循环上面的过程DISP: SETB P0.1 ;首先关闭十位显示SETB P0.0 ;再关闭个位显示MOV A,30H ;MOV DPTR,#TAB ;将显示代码TAB地址送入DPTRMOVC A,@A+DPTR ;查询30H内数据的显示代码送入AMOV P2,A ;将查询到的代码送入P2显示CLR P0.1 ;打开个位显示刚才的数据LCALL DELY1 ;调用小的演示程序(10ms以内),形成视觉效果SETB P0.1 ;关闭个位显示MOV A,31H ;现在要显示31H内数据了MOV DPTR,#TAB ;将显示代码TAB地址送入DPTRMOVC A,@A+DPTR ;查询31H内数据的显示代码送入AMOV P2,A ;将查询到的代码送入P2显示CLR P0.0 ;打开十位显示刚才的数据LCALL DELY1 ;调用小的演示程序(10ms以内),形成视觉效果SETB P0.0 ;关闭显示DJNZ R5,DISP ;R5自减,不为0则跳转到DISPRET ;子程序返回TAB: DB 3FH,06H,5BH,4FHDB 66H,6DH,7DH,07HDB 7FH,6FH,77H,7CHDB 39H,5EH,79H,71HDELY1: MOV R7,#10D10: MOV R6,#250D20: DJNZ R6,D20DJNZ R7,D10RETEND************************************************************************** #include<reg51.h>sbit P0_0=P0^0; //个位数字sbit P0_1=P0^1; //十位数字void delay(int k); //Tab为数码管显示值,存入一个数组内unsigned char code Tab[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};unsigned char i,shi,ge,num,count; //num为被显示的数字void main(){P2=Tab[0];P0_0=0;P0_1=0;//显示00num=0; //被显示的数置0count=20;while(1){for(i=1;i<count;i++) //显示个位、十位count次后显示的数加1{shi=num/10; //取出十位数ge=num%10; //取出个位数P0_0=1;P0_1=1; //关闭显示P2=Tab[shi]; //P2口送出十位数据显示代码P0_0=0; //打开十位显示delay(10); //延时P0_0=1; //关闭显示P2=Tab[ge]; //P2口送出个位数据显示代码P0_1=0; //打开个位显示delay(10); //延时}if(++num==100) //num自加1,然后判断是否等于100,等于则置0 num=0;}}void delay(int k) //最简单的C延时程序{unsigned char i;while(k--) //直至K减为0则跳出{for(i=250;i>0;i--);}}按键控制加、减显示00-99(P10为加,P11为减,P2接A- -H,P0接DS1- -DS8)********************************************************************************** #include<reg51.h>sbit P0_0=P0^0; //个位数字sbit P0_1=P0^1; //十位数字void delay(int k); //Tab为数码管显示值,存入一个数组内void key_ccan(void); //声明按键扫描函数unsigned char code Tab[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};unsigned char i,shi,ge,num,count=10; //num为被显示的数字void main(){P2=Tab[0];P0_0=0;P0_1=0;//显示00num=0; //被显示的数置0while(1){key_ccan(); //调用按键扫描程序for(i=0;i<count;i++) //显示个位、十位count次后显示的数加1{shi=num/10; //取出十位数ge=num%10; //取出个位数P0_0=1;P0_1=1; //关闭显示P2=Tab[shi]; //P2口送出十位数据显示代码P0_0=0; //打开十位显示delay(10); //延时P0_0=1; //关闭显示P2=Tab[ge]; //P2口送出个位数据显示代码P0_1=0; //打开个位显示delay(10); //延时P0_1=1; //关闭个位显示}}}void key_ccan(void){unsigned char ii;P1=0xff;ii=P1;if(ii!=0xff){P1=0xff;delay(50);ii=P1;if(ii!=0xff){switch(ii){case 0xfe: num++;if(num==100) num=0;break;case 0xfd: if(num==0) num=99;else num--;break;default: break;}}}}void delay(int k) //最简单的C延时程序{ unsigned char i;while(k--) //直至K减为0则跳出{for(i=100;i>0;i--);}}定时器计数00-59(P0接ds1-ds8,P2接A-H)*******************************************************************************************#include<reg51.h>sbit P0_0=P0^0; //个位数字sbit P0_1=P0^1; //十位数字void delay(int k); //Tab为数码管显示值,存入一个数组内unsigned char ge,shi,num,count;unsigned char code Tab[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};void main(){TMOD=0x1; //T0,工作方式1TH0=0xb1; //20ms定时TL0=0xe0;TR0=1; //开启T0定时器ET0=1; //允许T0定时器中断EA=1; //开启总中断允许P2=Tab[0];P0_0=0;P0_1=0;//显示00num=0; //被显示的数置0while(1) //一直调用显示,等待T0中断修改显示数据{shi=num/10; //取出十位数ge=num%10; //取出个位数P0_0=1;P0_1=1; //关闭显示P2=Tab[shi]; //P2口送出十位数据显示代码P0_0=0; //打开十位显示delay(1000); //延时P0_0=1; //关闭显示P2=Tab[ge]; //P2口送出个位数据显示代码P0_1=0; //打开个位显示delay(1000); //延时P0_1=1; //关闭个位显示}}void time0(void) interrupt 1{TH0=0xb1;TL0=0xe0;count++; //计数值+1if(count==50) //加到50次即1秒{ count=0;num++; //显示数据+1if(num==60){ num=0; }}}void delay(int k){while(k--);}定时器计数秒表000.0-999.9(P0接ds1-ds8,P2接A-H)*******************************************************************************************#include<reg51.h>sbit P0_0=P0^0; //百位数字sbit P0_1=P0^1; //十位数字sbit P0_2=P0^2; //个位数sbit P0_3=P0^3; //小数位void delay(int k); //Tab为数码管显示值,存入一个数组内unsigned char ge,shi,bai,xiao,count;unsigned int num;unsigned char code Tab[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71};void main(){TMOD=0x1; //T0,工作方式1TH0= 0xd8; //10ms定时TL0=0xf0;TR0=1; //开启T0定时器ET0=1; //允许T0定时器中断EA=1; //开启总中断允许P2=Tab[0];P0_0=0;P0_1=0;//显示00num=0; //被显示的数置0while(1) //一直调用显示,等待T0中断修改显示数据{bai=num/1000; //取出十位数shi=num%1000/100; //取出个位数ge=num%100/10;//第一位小数xiao=num%10; //第二位小数P0_0=0xff; //关闭显示P2=Tab[bai]; //P2口送出十位数据显示代码P0_0=0; //打开十位显示delay(200); //延时P0_0=1; //关闭显示P2=Tab[shi]; //P2口送出个位数据显示代码,各位要加小数点,即最高位为1则显示小数点P0_1=0; //打开个位显示delay(200); //延时P0_1=1; //关闭个位显示P2=Tab[ge]+0x80; //P2口送出第一个小数据显示代码P0_2=0; //打开十位显示delay(200); //延时P0_2=1; //关闭显示P2=Tab[xiao]; //P2口送出第二个笑数据显示代码P0_3=0; //打开个位显示delay(200); //延时P0_3=1; //关闭个位显示}}void time0(void) interrupt 1{TH0= 0xd8; //10ms定时TL0=0xf0;count++; //显示数据+1if(count==10) //显示数据计数到9999次即99.99秒{ count=0;if(num++==10000) num=0;}}void delay(int k){while(k--);}11。
51汇编伪指令伪指令是对汇编起某种控制作用的特殊命令,其格式与通常的操作指令一样,并可加在汇编程序的任何地方,但它们并不产生机器指令。
许多伪指令要求带参数,这在定义伪指令时由“表达式”域指出,任何数值与表达式匀可以作为参数。
不同汇编程序允许的伪指令并不相同,以下所述的伪指令仅适用于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伪指令了。
51汇编伪指令伪指令是对汇编起某种控制作用的特殊命令,其格式与通常的操作指令一样,并可加在汇编程序的任何地方,但它们并不产生机器指令。
许多伪指令要求带参数,这在定义伪指令时由“表达式”域指出,任何数值与表达式匀可以作为参数。
不同汇编程序允许的伪指令并不相同,以下所述的伪指令仅适用于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伪指令了。
MCS51部分汇编指令8种常用伪指令1.ORG 16位地址;此指令用在原程序或数据块的开始,指明此语句后面目标程序或数据块存放的起始地址。
2.[标号:] DB 字节数据项表;将项表中的字节数据存放到从标号开始的连续字节单元中。
例如:SEG: DB 88H,100,“7”,“C”3.[标号:]DW 双字节数据项表;定义16位地址表,16地址按低位地址存低位字节,高位地址存高位字节。
例如:TAB: DW 1234H,7BH4.名字 EQU 表达式或名字=表达式;用与给一个表达式赋值或给字符串起名字。
之后名字可用做程序地址,数据地址或立即数地址使用。
名字必须是一字母开头的字母数字串。
例如:COUNT=10 或 SPACE EQU 10H 5.名字 DATA 直接字节地址;给8位内部RAMm单元起个名字,名字必须是一字母开头的字母数字串。
同一单元可起多个名字。
例如:ERROR DATA 80H 6.名字 XDATA 直接字节地址;给8位外部RAM起个名字,名字规定同DATA伪指令。
例如:IO_PORT XDATA 0CF04H7.名字 BIT 位指令;给一可位寻址的位单元起个名字,规定同DATA伪指令。
例如:SWT BIT 30H8.[标号:]END;指出源程序到此结束,汇编对其后的程序语句不予理睬.源程序只在主程序最后使用一个END定义指令符号符号说明Rn 目前所选定的寄存器组中的R0-R7寄存器Ri 当前选中的寄存器区中可作为地址寄存器的两个寄存器R0和R1(i=0,1)Direct8位直接寻址地址,可以是内存RAM地址(00H--7FH)或SFR(80H--FFH)@Ri 通过R0或R1做间接寻址内部RAM的地址(00H--FFH)的前缀#data 8位立即数#data1616位立即数,只有DPTR中才用到Addr 16 只用在LCALL(长指令调用,三字节)和LJMP(无条件长转移指令)指令中的16位目的地地址,可寻址64K存储器中的任何地址Addr 11 使用在ACALL(绝对调用指令,双字节)和AJMP(无条件绝对转移指令)指令中的11位目的地地址,可寻址下一个指令算起2K内的任何地址,即目的地址必须放在与下条指令第一个字节同一个2kB程序存储器空间DPTR 数据指针rel 有符号的8位偏移地址(Offset Address),其范围是从下一个指令算起的-128到+128字节中。
keil错误C51编译器识别错类型有三种1、致命错误:伪指令控制行有错,访问不存在的原文件或头文件等。
2、语法及语义错误:语法和语义错误都发生在原文件中。
有这类错误时,给出提示但不产生目标文件,错误超过一定数量才终止编译。
3、警告:警告出现并不影响目标文件的产生,但执行时有可能发生问题。
程序员应斟酌处理。
D.1 致命错误C_51 FATAL_ERRORACTION: <当前行为>LINE: <错误所在行>ERROR: <错误信息> terminated或C_51 FA TAL ERRORACTION: <当前行为>FILE: <错误所在文件>ERROR: <错误信息> terminatedC_51 TERMINATED C_51(1) ACTION 的有关信息*PARSING INVOKE-/#PRAGMA_LINE在对#pragma 指明的控制行作此法分析时出错。
*ALLOCATING MEMORY系统分配存储空间时出错。
编译较大程序需要512k空间。
*OPENING INPUT_FILE打开文件时,未找到或打不开源文件/头文件。
*CREATE LIST_FILE/OBJECT_FILE/WORK_FILE不能创建上述文件。
可能磁盘满或文件已存在而且写保护。
*PARSING SOURCE_FILE/ANALYZING DECLARATIONS分析源程序时发现外部引用名太多。
*GENERATING INTERMEDIATE CODE源代码被翻译成内部伪代码,错误可能来源于函数太大而超过内部极限。
*WRITING TO FILE在向文件(work,list,prelist或object file)写时发生错误。
(2)ERROR的有关信息*MEMORY SPACE EXHAUSTED所有可用系统空间耗尽。
至少需要512k 字节空间。
汇编语言程序的指令学习要使用单片机,就要学会编写程序。
一台计算机,无论是大型机还是微型机,如果只有硬件,而没有软件(程序),是不能工作的。
单片机也不例外,它必须配合各种各样的软件才能发挥其运算和控制功能。
单片机的程序一般用汇编语言指令来表示。
所谓指令是规定计算机进行某种操作的命令。
一条指令只能完成有限的功能,为使计算机完成一定的或复杂的功能就需要一系列指令。
计算机能够执行的各种指令的集合称为指令系统。
计算机的主要功能也是由指令系统来体现的。
一般来说,一台计算机的指令越丰富,寻址方式越多,且每条指令的执行速度越快,则它的总体功能越强。
5.1 MCS-51单片机的指令系统MCS-51单片机的指令系统使用了7种寻址方式,共有111条指令,如按字节数分类,其中单字节指令49条,双字节指令45条,三字节指令17条;如按运算速度分类,单周期指令占64条,双周期指令占45条,四周期指令占2条。
可见,MCS-51指令系统在占用存储空间方面和运行时间方面效率都比较高。
另外,MCS-51有丰富的位操作指令,这些指令与位操作部件组合在一起,可以把大量的硬件组合逻辑用软件来代替,这样可方便地用于各种逻辑控制。
指令一般由两部分组成,即操作码和操作数。
对于单字节指令有两种情况:一种是操作码、操作数均包含在这一个字节之内;另一种情况是只有操作码无操作数。
对于双字节指令,均为一个字节是操作码,一个字节是操作数;对于三字节指令,一般是一个字节为操作码,二个字节为操作数。
由于计算机只能识别二进制数,所以计算机的指令均由二进制代码组成。
为了阅读和书写方便,常把它写成十六进制形式,通常称这样的指令为机器指令。
现在一般的计算机都有几十甚至几百种指令。
显然,即便用十六进制去书写和记忆也是不容易的。
为了便于记忆和使用,制造厂家对指令系统的每一条指令都给出了助记符。
助记符是根据机器指令不同的功能和操作对象来描述指令的符号。
由于助记符是用英文缩写来描述指令的特征,因此它不但便于记忆,也便于理解和分类。
C51单片机汇编语言程序设计一、二进制数与十六进制数之间的转换1、数的表达方法为了方便编程时书写,规定在数字后面加一个字母来区别,二进制数后加B十六进制数后加H。
2、二进制数与十六进制数对应表二进制十六进二进制制0000000100100011010001010110011101234567100010011010101111001101 11101111十六进制89ABCDEF3、二进制数转换为十六进制数转换方法为:从右向左每4位二进制数转化为1位十六进制数,不足4位部分用0补齐。
例:将(1010000110110001111)2转化为十六进制数解:把1010000110110001111从右向左每4位分为1组,再写出对应的十六进制数即可。
0101000011011000111150D8F答案:(1010000110110001111)2=(50D8F)16例:将1001101B转化为十六进制数解:把10011110B从右向左每4位分为1组,再写出对应的十六进制数即可。
100111109E答案:10011110B=9EH4、十六进制数转换为二进制数转换方法为:将每1位十六进制数转换为4位二进制数。
例:将(8A)16转化为二进制数解:将每位十六进制数写成4位二进制数即可。
8A10001010答案:(8A)16=(10001010)2例:将6BH转化为二进制数解:将每位十六进制数写成4位二进制数即可。
6B01101011答案:6BH=01101011B二、计算机中常用的基本术语1、位(bit)计算机中最小的数据单位。
由于计算机采用二进制数,所以1位二进制数称作1bit,例如110110B为6bit。
2、字节(Byte,简写为B)8位的二进制数称为一个字节,1B=8bit3、字(Word)和字长两个字节构成一个字,2B=1Word。
字长是指单片机一次能处理的二进制数的位数。
如AT89S51是8位机,就是指它的字长是8位,每次参与运算的二进制数的位数为8位。
ASM-51汇编伪指令一、伪指令分类1.符号定义SEGMENT, EQU, SET, DATA, IDATA, XDATA, BIT, CODE2.存储器初始化/保留DS, DB, DW, DBIT3.程序链接PUBILC, EXTRN, NAME4.汇编程序状态控制ORG, END5.选择段的伪指令RSEG, CSEG, DSEG, XSEG, ISEG, BSEG, USING二、伪指令具体说明1.符号定义伪指令1)SEGMENT伪指令格式:段名SEGMENT 段类型说明:SEGMENT 伪指令说明一个段。
段就是一块程序代码或数据存储器。
允许使用的段类型为:●CODE代码空间●DATA 可以直接寻址的内部数据空间●XDATA外部数据空间●IDATA可以间接寻址的整个内部数据空间●BIT位空间例子:(段符号用于表达式时,代表被连接段的基地址)STACK SEGMENT IDATARSEG STACKDS 10H ;保留16字节做堆栈MOV SP , #STACK-1 ;堆栈指针初始化2)EQU伪指令格式:符号名 EQU 表达式符号名 EQU 特殊汇编符号说明:EQU表示把一个数值或特殊汇编符号赋予规定的名字。
一个表达式赋予一个符号,必须是不带向前访问的表达式。
例子:N27 EQU 27;ACCUM EQU A ;定义ACCUM代替特殊汇编符号A(累加器)HERE EQU $; HERE为当前位置计数器的值3)SET伪指令格式:符号名 SET 表达式符号名 SET 特殊汇编符号说明:SET类似EQU,区别在于可以用另一个SET伪指令在以后对定义过的符号重新定义。
例子:COUNT SET 0COUNT SET COUNT+14)BIT伪指令格式:符号名 BIT 位地址说明: BIT伪指令把一个地址赋予规定的符号名。
该符号类型取段类型BIT.例子:RSEG DATA_SEG;CONTROL: DS 1ALATM BIT CONTROL.0;OPEN_BOARD BIT ALATM+1 ;下一位RESET_BOARD BIT 60H ;下一个绝对的位5)DATA伪指令格式:符号名 DATA 表达式说明:DATA伪指令把片内的数据地址赋予所规定的符号名。
C51汇编伪指令(转载)equdatabitds1、DS ---预留存储区命令格式:〔标号:〕 DS 表达式值其功能是从指定地址开始,定义一个存储区,以备源程序使用。
存储区预留的存储单元数由表达式的值决定。
TMP: DS 1从标号TEP地址处开始保留1个存储单元(字节)。
2、BIT---定义位命令格式:字符名称 BIT 位地址其功能用于给字符名称定义位地址。
SPK BIT P3.7经定义后,允许在指令中用SPK代替P3.7。
3、USING指令USING指令通知汇编器使用8051的哪一个工作寄存器组。
格式: USING 表达式(值必须为0-3,默认值为0。
)USING 0使用第0组工作寄存器。
4、SEGMENT指令SEGMENT 指令用来声明一个再定位段和一个可选的再定位类型。
格式:再定位段名 SEGMENT 段类型〔再定位类型〕其中,“再定位段名”用于指明所声明的段。
“段类型”用于指定所声明的段将处的存储器地址空间。
可用的段类型有 CODE、XDATA、DATA、IDATA和BIT。
STACK_SEG SEGMENT IDATADATA_SEG SEGMENT DATA5、RSEG---再定位段选择指令再定位段选择指令为RSEG,用于选择一个已在前面定义过的再定位段作为当前段。
格式: RSEG 段名段名必须是在前面已经声明过的再定位段。
DATA_SEG SEGMENT DATA ;声明一个再定位DATA段RSEG DATA_SEG ;选择前面声明的再定位DATA段作为当前段6、绝对段选择指令CSEG---绝对代码段DSEG---内部绝对数据段XSEG---外部绝对数据段ISEG---内部间接寻址数据段BSEG---绝对位寻址数据段格式:CSEG [AT 绝对地址表达式]DSEG [AT 绝对地址表达式]XSEG [AT 绝对地址表达式]ISEG [AT 绝对地址表达式]BSEG [AT 绝对地址表达式]括号内是可选项,用来指定当前绝对段的基地址。
4.1 伪指令有一些指令,如指定目标程序或数据存放的地址、给一些指定的标号赋值、表示源程序结束等指令,并不产生目标程序(机器码),也不影响程序的执行,仅仅产生供汇编用的某些命令,用来对汇编过程进行某种控制或操作,这类指令称为伪指令。
1.汇编语言源程序由以下两种指令构成▪汇编语句(指令语句)▪伪指令(指示性语句)2.汇编语句的格式:标号:操作码操作数;注释数据表示形式:二进制(B)、十六进制(H)、十进制(D或省略)、ASCII码(以单引号标识)4.1.1 定义起始地址伪指令格式:ORG addr16功能:规定程序块或数据块存放的起始地址。
addr16表示一个16位的程序存储器的空间地址,一般为一个确定的地址,也可以是事先定义的标号。
例如:ORG 2000HSTART: MOV A, 30H…4.1.2 定义汇编结束伪指令格式:END功能:表示汇编结束4.1.3 标号赋值伪指令格式:标号EQU [表达式]功能:将表达式的值赋给本语句中的标号。
又称为等值指令。
例:AA EQU 30HK1 EQU 40HMOV A,AA ;(30H)→AMOV A,K1 ;(40H)→A4.1.4 定义字节伪指令格式:标号: DB [字节表]从指定单元开始定义(存储)若干个字节的数据或ASCII码字符,常用于定义数据常数表。
格式:DB 字节常数或ASCII字符例: ORG 1000HDB 34H,0DEH,’A’,’B’DB 0AH,0BH,204.1.5 定义字伪指令格式:标号: DW[字表]从指定单元开始定义(存储)若干个字的数据或ASCII码字符。
格式:DW 字常数或ASCII字符例:ORG 2000HDW 1234H,’B’DW 0AH,204.1.6 DS 预留存储区伪指令格式:标号: DS表达式功能:从标号指定的地址单元开始,定义一个存储区,以备源程序使用。
存储区内预留的存储单元数由表达式的值决定。
例如:ORG 3030HTIMER: DS 10H┇BIT位地址符号指令。
51单片机汇编伪指令命令按字母排序:1、ALTNAME功能: 这一伪指令用来自定义名字,以替换源程序中原来的保留字,替换的保留字均可等效地用于子程序中。
格式: ALTNAME 保留字自定义名注意: 自定义名与保留字之间首字符必须相同。
1、BIT功能:指令用于将一个位地址赋给指定的符号名。
指令格式:符号名 BIT 位地址经BIT 指令定义过的位符号名不能更改。
例如:X_ON BIT 60H ;定义一个绝对位地址X_OFF BIT 24h.2 ;定义一个绝对位地址BIT---定义位命令格式:字符名称BIT 位地址其功能用于给字符名称定义位地址。
SPK BIT P3.7 经定义后,允许在指令中用SPK代替P3.7。
2、BSEG功能:绝对选择指令指令BSEG选择绝对位寻址数据段指令格式如下:BSEG [AT 绝对地址表达式]3、CODE功能:用于将程序存储器ROM 地址赋给指定的符号名。
指令格式:符号名 CODE 表达式例如:RESET CODE 00H4、CSEG功能:绝对选择指令CSEG选择绝对代码段;指令格式如下:CSEG [AT 绝对地址表达式]5、DATA(BYTE)功能:指令用于将一个内部RAM 的地址赋给指定的符号名指令格式:符号名 DATA 表达式数值表达式的值应在0~255 之间,表达式必须是一个简单再定位表达式。
例如:REGBUF DATA(BYTE) 40H PORT0 DATA(BYTE) 80HDATA与BYTE的区别: DATA与BYTE是相类似的伪指令。
当程序运行到DATA伪指令定义的符号名时,该符号名将被显示;而由BYTE定义的符号名不被显示。
6、DB功能:DB伪指令用于定义一个连续的存储区,给该存储区的存储单元赋值。
该伪指令的参数即为存储单元的值,在表达式中对变元个数没有限制,只要此条伪指令能容纳在源程序的一行内,其格式为:标号:DB 表达式只要表达式不是字符串,每一表达式值都被赋给一个字节。
计算表达式值时按16位处理,但其结果只取低8位,若多个表达式出现在一个DB伪指令中,它们必须以逗号分开。
表达式中有字符串时,以单引号“'”作分隔符,每个字符占一个字节,字符串不加改变地被存在各字节中,并不将小写字母转换成大写字母。
例如:DB 00H 01H 03H 46H DB 'This is a demo!'7、DBIT功能:在内部数据区的BIT 段以位为单位保留存储空间。
指令格式:[标号:] DBIT 数值表达式其操作类似于DB。
8、DS功能:DS为定义存储内容的伪指令,用它定义一个存储区,并用指定的参数填满该存储区。
DS伪指令包含两个变元,第一个变元定义了存储区的长度的字节数,在汇编时,汇编程序将跳过这些单元把其它指令汇编在这些字节之后,因此在使用DS伪指令时第一个变元不可活力第二个变元表示在这些单元中真入什么值,第二个变元可以活力活力时这些字节将不处理。
下例中0173处有一条DS 9,则空出9个字节,下一第指令被汇编到017C处;在017C处空出1BH 个单元,在这些字节中被27H所填充。
DS指令的格式如下:标号:DS 表达式1,表达式2表达式1定义了存储区的长度(以字节为单位)。
这个变元不能省略。
表达式2是可选择的,它的值低8位用以填入所定义的存储区。
若省略则这部分存储单元不处理。
例:0000 04 INC A0001 DS 9000A 04 INC A000B DS 1BH,27H0026 04 INC ADS ---预留存储区命令格式:〔标号:〕DS 表达式值其功能是从指定地址开始,定义一个存储区,以备源程序使用。
存储区预留的存储单元数由表达式的值决定。
TMP: DS 1 从标号TEP地址处开始保留1个存储单元(字节)。
9、DSEG功能:绝对选择指令DSEG内部绝对数据段指令格式如下:DSEG [AT 绝对地址表达式]10、DW功能:DW为以字节为单元(十六位二进制)来给一个的存储区赋值,其格式为:标号:DW 表达式例如:0000 3035 D46B DW 12341,54379,10110100101110B11、END功能:END语句标志源代码的结束,汇编程序遇到END语句即停止运行。
若没有END语句,汇编将报错。
END 语句有一个参数,可以是数值0,也可以是表达式,其格式是:标号: END 表达式它的值就是程序的地址并且作为一个特殊的记录写入HEX文件。
若这个表达式省略,HEX文件中其值就是0。
12、EQU(=)存器名赋给一个指定符号名。
指令格式:符号名 EQU(=)表达式符号名 EQU(=)寄存器名经过EQU 指令赋值的符号可在程序的其它地方使用,以代替其赋值。
例如:MAX EQU 2000则在程序的其它地方出现MAX,就用2000 代替。
表达式必须是一个简单再定位表达式。
用EQU 指令赋值以后的字符名,可以用作数据地址、代码地址、位地址或者直接当做一个立即数使用。
13、EXTRNEXTRN 是与PUBLIC 配套使用的,要调用其它模块的函数,就必须先在模块前声明。
指令格式:EXTRN 段类型(符号,符号······)例如:EXTRN CODE (TONGXING,ZHUANHUAN)调用外部TONGXING和ZHUANHUAN 程序。
14、IDATAIDATA 指令用于将一个间接寻址的内部RAM 地址赋给指定的符号名。
指令格式:符号名 IDATA 表达式例如:FULLER IDATA 60H15、IF条件伪操作格式:IF 表达式[ 程序块1 ][ ELSE ][ 程序块2 ]ENDIF当IF指令中的表达式为真时,被汇编的代码段是程序块1;当IF指令中的表达式为假时,被汇编的代码段是程序块2。
在一个条件结构中,仅有一个代码段被汇编,其它的则被忽略。
16、INCLUDE功能: 利用此伪指令可将一个源文件插入到当前源文件中一起汇编,最终成为一个完整的源程序。
格式: INCLUDE [ 驱动器名: ] [ 路径名 ] 文件名注意:1、文件名中若没有扩展名,则系统默认是。
ASM(该文件必须是能打开的)。
2、被插入的源程序中不能包含END伪指令,否则汇编会停止运行。
被链接文件的每一行,在程序清单中以“I“开头。
3、链接伪指令可有8级嵌套,若要求嵌套的多,则要修改 DOS 中的CONFIG。
SYS文件的FILES参数。
17、ISEG功能:绝对选择指令ISEG内部间接寻址绝对数据段idata指令格式如下:ISEG [AT 绝对地址表达式]18、MACRO宏指令格式[ 宏指令名 ] MACRO [形式参数,。
]代码段ENDM宏调用格式[ 宏指令名 ] [实在参数,。
]19、LIST它们的格式为:$LIST功能:LIST伪指令使汇编时主生程序清单,但即使不用该指令,汇编也会自动产生清单。
但如果使用了NOLIST伪指令后需要继续主生清单则必须使用LIST伪指令。
20、NAME功能:用来给当前模块命名。
指令格式:NAME 模块名例如:NAME TIMER定义一个模块名为TIMER 的模块。
21、NOCODE其格式为$NOCODE NOCODE伪指令使得在汇编时,条件汇编程序结构中那些真值为假的条件不产生清单。
有关条件汇编结构在下面介绍。
如果没有这条伪指令,汇编将主生所有条件下的清单,不论其真值是否为真。
但是假的条件,不产生目标码。
而NOCODE伪指令使汇编清单中只列出那些由汇编程序用到的部分,因此,当使用NOCODE伪指令时,程序清单与源程序并非逐行对应。
22、NOLIST它们的格式为:$NOLIST功能:NOLIST伪指令使汇编时不产生清单,所有包含此伪指令及在这条伪指令之后的语句都不进入列表文件。
当不需要任何列表文件,并且不需要显示程序清单时,可以在启动汇编时不加.L附加项,且在源代码的第一行加上NOLIST指令。
使用NOLIST伪指令与附加项/L不同之处是NOLIST伪指令可加在源程序中,与LIST伪指令配合使用,使源程序中某些部分不产生清单。
而不加附加项/L则不产生任何程序清单。
不过,不管有无$NOLIST伪指令,程序在汇编时检查到的错误都将在屏幕上显示出错的源代码行及错误信息23、PAGE功能:PAGE伪指令用于形成新的一中定义一面的行数。
其格式为:$PAGE表达式若表达式缺省则开始新的一页,若有表达式,则每页行数重新定义。
汇编开始时页长为66行。
一页中除出页外,剩余55行用于打印源程序,这一格式适用于标准打印纸。
如果变元值小于66,页内可打印的源代码行将相应减少。
页长最小值为12。
若小于12时,每页内除页上只打印一行源程序。
页长变元是16位字节,因而每页最长可定义到65535行,这时分页打印变为连续打印,在屏幕显示程序清单或在卷筒纸上打印程序清单时,常常使用连续打印,如果在启动汇编时用/N选项,页长就是65535。
24、PUBLIC功能:声明可被其它模块使用的公共函数名。
指令格式:PUBLIC 符号 [,符号,符号[,······]] PUBLIC 后可跟多个函数名,用逗号格开。
每个函数名都必须是在模块内定义过的。
例如:PUBLIC INTER,_OUTER其中_OUTER 可供C 调用。
25、RSEG功能:再定位段选择指令为RSEG,用于选择一个已在前面定义过的再定义段作为当前段,指令格式:RSEG 段名段名必须是在前面声明过的再定位段。
例如:DATAS SEGMENT DATA :声明一个再定位DATA 段CODES SEGMENT CODE ;声明一个再定位CODE 段BSEG AT 60HRSEG CODES ;选择前面声明的再定位CODE 段作为当前段。
绝对段选择指令CSEG---绝对代码段DSEG---内部绝对数据段XSEG---外部绝对数据段ISEG ---内部间接寻址数据段BSEG---绝对位寻址数据段格式:CSEG [AT 绝对地址表达式] DSEG [AT 绝对地址表达式] XSEG [AT 绝对地址表达式] ISEG [AT 绝对地址表达式] BSEG [AT 绝对地址表达式]括号内是可选项,用来指定当前绝对段的基地址。
CSEG AT 0000H AJMP MAIN26、SEGMENT功能:SEGMENT 指令用来声明一个再定位段和一个可选的再定位类型。
指令格式:再定位段型 SEGMENT 段类型(再定位类型)段类型用于指定所声明的段将处的储存器地址空间,可用的段类型有CODE/XDATA/DATA/IDATA 和BIT。
例如: FLAG SEGMENT BIT PONITER SEGMENT IDATA27、SET功能:SET 指令类似于EQU 指令,不同的是SET 指令定义过的符号可重定义。