原创 c51和汇编

2010-11-26 21:54 3910 4 4 分类: MCU/ 嵌入式

网上关于C51的资料有很多, 市面上的书目也有很多详细的介绍的C51编程。这里就谈谈个人对这种语言的对照学习。 很多单片机初学者都是从汇编开始的,当然我也包括在内,汇编代码会让人看许久都可能没有半点头绪, 但是模块化的汇编代码也是很容易理解的。怕的就是所有的代码都在MAIN主程序中, 看上去很恼火。 呵呵, 本人以前就是这样做的。后来看看别人这么把汇编代码清晰化, 模块化。都写成子函数的形式用RET返回,既有C的韵味,又很明了。不说废话,现在我们先看个例子。
一.模块化结构:
【C51代码】
void main()
{
    unsigned int data a = 0x21;
    unsigned int data b = 0x34;
    unsigned int data c = a + b; 
}
我想这样简单的双字节整数乘法(int在KEIL编译器中占两个字节),很简单的几句就可以搞定了。让我们现在看看编译以后的汇编代码(C51程序编译以后的汇编代码大家可以在debug模式中看到)。
【汇编代码】
;**********************************************************
;                    输入参数     :R6,R7分别为被乘数的高位和低位
;                                         R4,R5分别为乘数的高位和低位
;                    输出参数     :R7,R6为结果的高位和低位
;**********************************************************
DOUBLE_BYTE_MULTIPLY:
              MOV            A,   R7
              MOV            B,    R5
              MUL             AB
              MOV            R0,  B
              XCH             A,   R7
              MOV            B,    R4
              MUL             AB
              ADD             A,   R0
              XCH             A,   R6
              MOV            B,    R5
              MUL             AB
              ADD             A,   R6
              MOV            R6,  A
              RET
;**********************************************************
呵呵,可能大家看到这样的代码不号理解, 做双字节整数乘法步骤, (256*R6+R7)*(256*R4+R5),大家只要做多项式展开以后就是上面的汇编代码了,当然数值的大小不能超过unsigned int 的数值范围(最大为65536),因为上面定义了变量c是unsigned int数据。C51中用两个数据存储器来存放int数据,long就是占用4个数据存储区,所以没有特别的必要时候少用int和long类型的数据类型,而且最好多用unsigned char类型的变量,不需要符号判别而且符合存储器的存放格式,一个字节。代码的效率才高,执行的速度才快。


二.关于中断:
51系列单片机中断都有对应的入口地址,通常用汇编伪指令ORG定义。
如:
【汇编代码】
ORG    0000H
LJMP   MAIN
ORG    0003H
LJMP   EX_INT0
ORG    0030H
MAIN:
……..
SJMP   $
EX_INT0:
…….
RETI

【C51】
Interrupt关键字
如:
void ex_int0() interrupt 0 using n
{
……
}
这里的using n可以不写,默认。这里主要是指定工作功能寄存器组。在汇编代码中切换寄存器是通过寄存器PSW中的RS0与RS1切换,默认时,第0组工作。这里多说一句就是因为两个寄存器之间不能直接传递数据,如:
MOV     R7, R6 ;这样的代码肯定是要编译出错的,当然你可以通过累加器ACC做中间替换来大道目的,个人觉得简单的就是间接访问。在第0组工作功能寄存器工作的时候用MOV  R7, 0X06 ;这样就可以达到寄存器间数据之间的传递,中间变量的代码可能有些繁琐,如:XCH              A, R7; XCH  A, R6;这里代码的写法有多种,可能执行的机器时间差不多,也没有去考究过,大家可以查查51指令集看看。

三.关于堆栈指针SP
初始化时,SP = 07H,这样设置的理由是,SP+1以后就是08H,这就进入寄存器第1工作组,而寄存器工作组切换很少用到,所以默认时候就是07H,大家在写代码的时候可以设置为30H

四.EQU指令※ define宏定义
变量定义:
【汇编代码】
CMD     EUQ    30H

【C51】
#define  CMD  0X30   //CMD替换0X30

位定义:
【汇编代码】
STATUS_MSB    BIT  00H ;定义为0x20.0

【C51】
bit  bdata(data)          STATUS_MSB

五.总线方式访问外设地址定义
【汇编】
CMD_ADDR   EQU   8000H
MOVX  CMD_ADDR,  #30H ;给外部8000H单元送30H数据
MOVX  A,  CMD_ADDR  ;外部8000H数据读入累加器A

【C51】
#define   CMD_PORT  (*(unsigned char volatile xdata * 0x8000))
CMD_PORT  =  0X30;   //给外部8000H单元送数据30H
CMD_BUFFER  =  CMD_PORT; //将外部8000H单元数据读入变量

六.C51变量存储类型关键字,不声明时候根据编译模式来定
Data    直接寻址片内数据存储区,访问速度快(128字节)
Bdata   可位寻址片内数据存储区,允许位与字节混合访问(16字节)
Idata    间接寻址片内数据存储区,可访问片内全部RAM地址空间(256字节)
Pdata   分页寻址片外数据存储区(256字节)由MOV @Ri访问(i=0,1)(最不常用)
Xdata    片外数据存储区(64 KB)由MOVX @DPTR访问
Code     程序存储器64 KB空间,由MOVC @DPTR访问(相当与DB指令,数据放表中)

七.包含文件(*.h文件※ *.inc文件)
【C51】
C语言中通常把函数声明、宏命令写成头文件,即*.h
【汇编代码】
为了保持程序结构的模块化,通常把汇编写好的函数写成*.inc文件,类比*.h文件

八.PC(16位)
PC 程序功能计数器,记录执行程序中机器码的位置,比如当前PC = 0030H,下一条指令机器代码为3个字节,执行完以后PC = 0033H。可用做查表,但很少用。如:MOVC   A, @A+PC

九.DPTR(16位)
用做查表,或者访问外部数据单元

如:
【查表】
MOV  DPTR, #TAB
CLR   A
MOV  A, @A+DPTR
SJMP  $
TAB:
DB:……
END
【访问外部设备】
MOVX  A,  @DPTR  ;DPTR单元数据读入累加器
MOVX  @DPTR,  A ;向外部DPTR单元送累加器A中的数据

十.指针
C语言重要的部分就是指针,通过变量所在数据存储器上的单元可以迅速访问其数据内容,这里我们可以通过例子来说明。
【C51】
void main()
{
    unsigned char idata a = 0x25;// 数据存储器中定义一个变量a
    unsigned char xdata * p = &a; //外部数据存储器中存放一个指针变量,该地址指向数据存储器a变量所在单元。
    unsigned char data  b = *p; //p所指向的单元数据交变量b
}

【编译后的汇编代码】
     5:         unsigned char a = 25;
C:0x000F    750819   MOV      0x08,#0x19
     6:         unsigned char xdata *p = &a;
C:0x0012    900008   MOV      DPTR,#0x0008
     7:         unsigned char b = *p;
C:0x0015    E0       MOVX     A,@DPTR
C:0x0016    F509     MOV      0x09,A

十一. 位运算
C语言中位运算的运算符分别为(~, & , |, ^)
~, 汇编可以用CPL 。
&, 汇编可以用指令代码ANL
|,  汇编代码 ORL
^,  汇编代码 XRL
C语言移位(<<, >>)
unsigned char a = 25;
a = a>>2;//右移两个单位
这里还有一中代码形式:b = a – ((a >> 2) << 2), 可以实现b = a % 4;
汇编代码:可以用RR,或者RRC,不同的这里是循环移位

十二. 条件运算符
c  =  (a > b) ? a : b(取较大者)
编译以后的汇编代码:
;******************************************************************************
:                                  R7,R6分别为两个输入参数
;                                08H单元存放结果
;*****************************************************************************
C:0x0007    EF       MOV      A,R7
C:0x0008    D3       SETB     C
C:0x0009    9E       SUBB     A,R6  ;带借位相减
C:0x000A    4002     JC       C:000E ;
C:0x000C    8002     SJMP     C:0010
C:0x000E    AF06     MOV      R7,0x06
C:0x0010    8F08     MOV      0x08,R7
C:0x0012    22       RET

十三. C与汇编混合编程
【C语言中嵌入汇编代码】
格式。
#pragma asm
;汇编代码
#pragma endasm
设置。
在工程窗口中选择包含汇编代码的C文件,右击,选择“OPTIONS FOR”,点击“GENERRATE ASSEMBLER SRC FILE”和“ASSEMBLE SRC FILE”,使得检查框由灰色编程黑色。最后把库文件KEIL\C51\LIB\C51S.LIB加入工程,编译即可。

【C语言调用汇编子程序】
void main()
{
    Delay();
}
汇编文件(保存为*.A51)
NAME  DELAYASM    //定义模块名,可任意定义
Delay_Code SEGMENT CODE
PUBLIC Delay   //外部声明,与C文件中的函数名相同
RESG Delay_Code
汇编子程序
RET
END

十四、C位段定义
关键字 STRUCT(这里个人觉得有时候当访问的积存器特殊位定义的时候非常好用)
union
{
     unsigned char Register;
     struct
            {
                unsigned RegisterBit0          :1;
                unsigned RegisterBit1          :1;
                ......
            }RegisterBit;
}RegisgterName;




 


十五、51汇编指令集中ORG伪指令的作用


      既然是伪指令也就是这个指令是不参加编译的,通常这样的指令只不过是编译器编译的时候才需要,也就是告诉编译器我的程序是从哪里存放,相当于存储器的地址数据,这个可以参考HEX文件数据格式就很清楚了,例如:ORG 0030H 程序的机器码就从ROM的30H开始存放,在编译器编译结束后自然而然大家就会发现当前的PC值就变为0030H了哈。


十六、hex文件数据格式


     hex文件为一行一行的由ASCII组成的文本文件,是可以用记事本打开内部的数据的哦,hex文件和被烧入ROM的数据并不是一样的,被烧入ROM中的数据只不过是hex文件中数据域中的内容。首先可以先看下hex文件的数据格式:


:&&****$$[......]@@


hex文件都是以冒号开头的,上面每个同种符号的数据代表不同的含义


&&为数据域的长度,也就是[......]中的内容,


****为数据存放的位置,也就是即将要被写入FLASH中的地址


$$这里仅仅当为00的时候表示数据,01表示文件结束


[......]数据内容,也就是51指令的机器码


@@数据校验,这里具体怎么校验可以不用管


首先看一个测试程序:


     ORG  0000H
     SJMP MAIN            ;机器码为802E
     ORG  0030H
MAIN:
     MOV   A,  #01H       ;机器码7401
     MOV   P1, #0FFH   ;机器码7590FF
     END


hex文件为:(空格为方便看加上的)


:02  0000  00  802E  50 //02表示数据域内容为2个byte 0000程序从0000位置存储 00表示数据 802E数据内容  50校验
:05  0030  00  74017590FF  52
:00  0000  01   FF


总之在C编译以后的汇编代码大家可以参考着学习,毕竟也是很不错的学习汇编的材料。我自己就是这样过来的, 在知识的海洋中我们会茁壮成长。(求知若饥, 虚心若愚)
PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
我要评论
0
4
×
广告
关闭 站长推荐上一条 /3 下一条