第一节 main()函数和启动代码
  汇编是从org 0000h开始启动,那么keil c51是如何启动main()函数的?keil c51有一个启动程序startup.a51,它总是和c程序一起编译和链接。下面看看它和main()函数是如何编译的;

//主函数如下;
void main(void)
{
    )    这是个无条件空循环。
    {
    }
}

把上面的main()函数编译后的汇编程序和反汇编代码整理后对照如下;

?C_C51STARTUP           SEGMENT   CODE
?PR?main?TESTMAIN       SEGMENT CODE 

?STACK                  SEGMENT   IDATA

                        RSEG    ?STACK
                        DS      

                        CSEG    AT
?C_STARTUP:             LJMP    STARTUP1
      LJMP     STARTUP1()

                        RSEG    ?C_C51STARTUP
STARTUP1:                                                     ;该段程序把内存清零
;                       MOV     R0,#IDATALEN - 1
C:0x0003    787F        MOV     R0,#0x7F
;                       CLR     A
C:0x0005                E4      CLR      A
;                       MOV     @R0,A
IDATALOOP:
C:0x0006                F6      MOV      @R0,A
;                       DJNZ    R0,IDATALOOP
)
;                       MOV     SP,#?STACK-1                ;设制CPU的堆栈起始地址
      MOV     SP(0x81),#0x07
;                       LJMP    ?C_START
C:0x000C    02000F      LJMP    main(C:000F)

        RSEG  ?PR?main?TESTMAIN
main:
;        void main(void)
C:0x000F    80FE        SJMP     main(C:000F)                ;main()函数 

现在分析上面的汇编程序就会明白c51程序是如何启动的。
该程序有三个代码段;
第一个代码段?C_STARTUP在0x0000地址,是CPU第一条指令的入口,它只有一条长跳转指令,直接跳到第二个代码段.
第二个代码段?C_C51STARTUP是可重定位的段,该程序把内存清零,然后再设置CPU的堆栈,最后跳转到main()函数.
第三个代码段就是main()函数,在keil c51编译器里main()的段地址名就是?C_START。

还有一个IDATA数据段?STACK就是堆栈,?STACK用于设制CPU的堆栈起始地址,这是由keil编译器自动完成的。

/*******************************************************************/
keil c51函数的返回值是存储在r0-r7中的。
多字节变量在存储器里都是低地址存高位,高地址存低位。
main()函数的局部变量都是放在存储器里的,不象别的函数先选寄存器r0-r7存放,如果不够用再存入存储器里。

看下面的示例:

c51程序;
unsigned int SumXY(unsigned int X,Y);
void main(void)
{unsigned int a,b,c;
    a=0x5500;
    b=0xaa;
    )
    {
        c=SumXY(a,b);
    }
}

unsigned int SumXY(unsigned int X,Y)
{unsigned int Z;
    Z=X+Y;
    return Z;
}

编译后的反汇编代码列表;

   LJMP     STARTUP1()

     : void main(void)
     : {unsigned int a,b,c;
     :         a=0x5500;
   MOV      0x08,#0x55    ;ram地址0x08和0x09存放变量a=0x5500。
   MOV      0x09,#0x00
     :         b=0xaa;
C:0x0009    750A00   MOV      0x0A,#0x00    ;ram地址0x0A和0x0B存放变量b=0x00AA。
C:0x000C    750BAA   MOV      0x0B,#0xAA

     :         while ()
     :         {
    :                 c=SumXY(a,b);
C:0x000F    AD0B     MOV      R5,0x0B        ;寄存器R4和R5传递变量a的值。
C:0x0011    AC0A     MOV      R4,0x0A
C:0x0013    AF09     MOV      R7,0x09        ;寄存器R6和R7传递变量b的值。
C:0x0015    AE08     MOV      R6,0x08
   LCALL    SumXY()  ;调用函数SumXY(a,b)求c=a+b
C:0x001A    8E0C     MOV      0x0C,R6        ;函数SumXY(a,b)返回的整型值存在R6和R7里,
C:0x001C    8F0D     MOV      0x0D,R7        ;把返回值存入变量c,ram地址0x0C和0x0D存放变量c
    :         }
    : }
    :
C:0x001E    80EF     SJMP     C:000F

    : unsigned int SumXY(unsigned int X,Y)
    : {unsigned int Z;
    :         Z=X+Y;
C:0x0020    EF       MOV      A,R7        ;参数变量X放在寄存器R6和R7里
C:0x0021    2D       ADD      A,R5        ;参数变量Y放在寄存器R4和R5里
C:0x0022    FF       MOV      R7,A
C:0x0023    EE       MOV      A,R6
C:0x0024    3C       ADDC     A,R4        ;计算Z=X+Y;
C:0x0025    FE       MOV      R6,A        ;局部变量Z也放在寄存器R6和R7里
    :         return Z;                   ;由寄存器R6和R7里返回函数的值
       RET    

   :
   : ; This code is required if you use L51_BANK.A51 with Banking Mode 4
   : ; EXTRN CODE (?B_SWITCH0)
   : ;               CALL    ?B_SWITCH0      ; init bank mechanism to code bank 0
C:0x0027    75810D   MOV      SP(0x81),#0x0D
   :                 LJMP    ?C_START
   LJMP     main()

函数的入口地址,如何调用汇编函数,c和汇编的混合编程

/*******************************************************************/
c函数的函数名是一个指向函数的指针常量,它的值就是函数的入口地址。
从汇编程序上看,函数名也是该汇编函数代码段的入口地址标号。
调用汇编函数就是调用汇编函数的入口地址标号,但是要注意c函数名和汇编函数标号之间的转换规则。
1. 不带参数的汇编函数标号和c函数名相同.
2. 带参数的汇编函数标号在c函数名前加字符_,例如;如果c函数名是SumXY,汇编函数标号是_SumXY
3. 再入函数的汇编函数标号在c函数名前加_?,例如;如果c函数名是DoTask,汇编函数标号是_?DoTask

程序示例,该例有两个文件,一个文件是exam1.c,一个文件是funcasm.c
主程序文件: exam1.c

extern unsigned int SumXY(unsigned int X,Y);    //声明外部汇编语言函数,和声明c函数方法相同。
extern void Delay(unsigned char T);

void main(void)
{unsigned int a,b,c;
    a=0x5500;
    b=0x00aa;
    )
    {
        Delay();
        c=SumXY(a,b);
    }
}

混合编程文件: funcasm.c

//c和汇编的混合编程演示.
//注意要把汇编语言函数放在文件前面。

//求Z=X+Y的汇编语言函数,Z,X,Y是整型数。
#pragma ASM
        PUBLIC  _SumXY
?PR?_SumXY?FUNCASM    SEGMENT CODE
        RSEG     ?PR?_SumXY?FUNCASM
_SumXY:        ;求Z=X+Y
       MOV      A,R7        ;参数X放在寄存器R6和R7里
       ADD      A,R5        ;参数Y放在寄存器R4和R5里
       MOV      R7,A
       MOV      A,R6
       ADDC     A,R4        ;扑鉠=X+Y;
       MOV      R6,A        ;局部变量Z也放在寄存器R6和R7里
       RET
#pragma ENDASM

//c语言函数,延时函数。
void Delay(unsigned char T)
{unsigned char i;
    ;i<T;i++)
    {
        ;i<T;i++) {}
    }
}

/**********************************************************************/
上面程序编译后的反汇编代码列表;

                 C_STARTUP:
   LJMP     STARTUP1()

                 main:
//给变量a和b赋值 a=0x5500;    b=0x00aa;
   MOV      0x08,#0x55
   MOV      0x09,#0x00
C:0x0009    750A00   MOV      0x0A,#0x00
C:0x000C    750BAA   MOV      0x0B,#0xAA

//调用延时函数Delay();
C:0x000F    7F64     MOV      R7,#0x64
   LCALL    DELAY()

//求c=SumXY(a,b);
C:0x0014    AD0B     MOV      R5,0x0B
C:0x0016    AC0A     MOV      R4,0x0A
C:0x0018    AF09     MOV      R7,0x09
C:0x001A    AE08     MOV      R6,0x08
C:0x001C    12003A   LCALL    SUMXY(C:003A)    ;调用汇编语言函数SumXY(unsigned int X,Y)
C:0x001F    8E0C     MOV      0x0C,R6
C:0x0021    8F0D     MOV      0x0D,R7
C:0x0023    80EA     SJMP     C:000F

//c语言延时函数的反汇编代码
//void Delay(unsigned char T)
                 DELAY:
C:0x0025    E4       CLR      A
C:0x0026    FE       MOV      R6,A
                 C0001:
C:0x0027    EE       MOV      A,R6
C:0x0028    C3       CLR      C
C:0x0029    9F       SUBB     A,R7
)
C:0x002C    E4       CLR      A
C:0x002D    FE       MOV      R6,A
                 C0004:
C:0x002E    EE       MOV      A,R6
C:0x002F    C3       CLR      C
C:0x0030    9F       SUBB     A,R7
     )
C:0x0033    0E       INC      R6
C:0x0034    80F8     SJMP     C0004(C:002E)
                 C0003:
C:0x0036    0E       INC      R6
)
                 C0007:
       RET     

//汇编语言函数SumXY(unsigned int X,Y)的反汇编代码,求Z=X+Y
                 SUMXY:
C:0x003A    EF       MOV      A,R7
C:0x003B    2D       ADD      A,R5
C:0x003C    FF       MOV      R7,A
C:0x003D    EE       MOV      A,R6
C:0x003E    3C       ADDC     A,R4
C:0x003F    FE       MOV      R6,A
       RET   

//程序启动代码;
                 STARTUP1:
C:0x0041    75810D   MOV      SP(0x81),#0x0D
   LJMP     main()

深入剖析keil c51 --- 从汇编到c51的更多相关文章

  1. KEIL C51 中嵌入汇编以及C51与A51间的相互调用

    如何在 KEIL C51(v6.21) 中调用汇编函数的一个示例 有关c51调用汇编的方法已经有很多帖子讲到,但是一般只讲要点,很少有对整个过程作详细描述,对于初学者是不够的,这里笔者通过一个简单例子 ...

  2. C51与汇编混合编程详解

    C51和汇编混合编程(1)-C语言中嵌入汇编 1.在 C文件中要嵌入汇编代码片以如下方式加入汇编代码: #pragma ASM ;Assembler Code Here #pragma ENDASM ...

  3. 实用C51编程的高级技巧(C51编程)

    一.C51热启动代码的编制 void main() { char data *HotPoint=(char *)0x7f; if((*HotPoint==0xaa)&&(*(--Hot ...

  4. 使用Keil软件编写汇编源程序应注意事项

    1)一定要使用微软的txt文本编辑器,否则键入逗号时编译通不过.应该是这个样('),不该是这个样(,). 2) 用数字做标号时,前面一定要加一个英文字母,否则编译通不过. 3) 有时编译通过的.asm ...

  5. KEIL C51程序中如何嵌入汇编

    模块内接口:使用如下标志符:#pragma asm汇编语句#pragma endasm注意:如果在c51程序中使用了汇编语言,注意在Keil编译器中需要激活Properties中的“Generate ...

  6. Keil C51编译及连接技术

    主要介绍Keil C51的预处理方法如宏定义.常用的预处理指令及文件包含指令,C51编译库的选择及代码优化原理,C51与汇编混合编程的方法与实现以及超过64KB空间的地址分页方法的C51实现. 教学目 ...

  7. Keil C51调试程序时, 对ROM的查看以及RAM 查看或修改

    在Keil 里使用 DeBug 模式时,如要 查看外部 RAM 的数据 或查看 ACC 的内容可以进行以下操作; sysGetTxMode: LCALL Com0185(C:2B95) ,sysGet ...

  8. KEIL C51高级编程

    第一节 绝对地址访问C51提供了三种访问绝对地址的方法: 1. 绝对宏:在程序中,用“#include”即可使用其中定义的宏来访问绝对地址,包括:CBYTE.XBYTE.PWORD.DBYTE.CWO ...

  9. Keil C51中函数指针的使用

    函数指针在C语言中应用较为灵活.在单片机系统中,嵌入式操作系统.文件系统和网络协议栈等一些较为复杂的应用都大量地使用了函数指针.Keil公司推出的C51编译器是事实上80C51 C编程的工业标准,它针 ...

随机推荐

  1. K - Transformation-hdu 4578(多操作混合区间更新)线段树

    题意:有四种操作 1,  区间 [l, r] 的值都加上 C 2,  区间 [l, r] 的值都乘上 C 3,  区间 [l, r] 的值都变为C 4,  求区间 [l, r]所有数的p次方的和 分析 ...

  2. motan源码分析八:涉及到底层的客户端调用

    之前我们分析了客户端调用服务端的源码,但是没有涉及到通讯层和序列化层,本文将之前讲过的内容做一次串联. 1.上层通过动态代理调用refer的call,每个refer又对应一个nettyclient,下 ...

  3. SKPhysicsJointSpring类

    继承自 NSObject 符合 NSCoding(SKPhysicsJoint)NSObject(NSObject) 框架  /System/Library/Frameworks/SpriteKit. ...

  4. 使用EMOJI表情

    因为IOS系统支持日文中的字块编码,所以在UILable,UITextField,UIAlertView等控件中使用emoji表情编码(emoji就是表情符号:词义来自日语(えもじ,e-moji,mo ...

  5. 大数据笔记07:大数据之Hadoop的HDFS(特点)

    1. HDFS的特点: (1)数据冗余,硬件容错 (2)流式的数据访问(写一次读多次,不能直接修改已写入的数据,只能删除之后再去写入) (3)存储大文件 2. HDFS适用性和局限性 适用性:(1)适 ...

  6. 安装android studio 出现的路径问题 tools.jar' seems to be not in Android Studio classpath

    尝试一下android studio  ,谁知出现路径问题 'tools.jar' seems to be not in Android Studio classpath. Please ensure ...

  7. poj 3349 (最小表示法)

    开始按hash做的 交上去就wa 但是和标称拍了半天也没有不一样的 可能是生成的数据太水了吧... #include<iostream> #include<cstdio> #i ...

  8. CSS 之 margin知识点

    1.margin的百分比值 普通元素的百分比maigin相对于容器元素的宽度(width) 进行计算的. 这里我们在图片外面设置一个宽高分别为800 * 600的容器.设置img{ margin: 1 ...

  9. CSS3 字体

    CSS3 @font-face 规则 在 CSS3 之前,web 设计师必须使用已在用户计算机上安装好的字体. 通过 CSS3,web 设计师可以使用他们喜欢的任意字体. 当您您找到或购买到希望使用的 ...

  10. Property type 'id<tabBarDelegate>' is incompatible with type 'id<UITabBarDelegate> _Nullable' inherited from 'UITabBar'

    iOS报错:Property type 'id' is incompatible with type 'id _Nullable' inherited from 'UITabBar' 如图: 可能原因 ...