第一节 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. E - QS Network - zoj 1586(简单)

    题意:在一个叫做QS的星系,他们使用一些特殊的通讯方式,两个人之间通讯需要使用一个网络适配器,但是一个网络适配器只能跟一个人联系,所有它连接几个人就需要及格适配器,而且每个人都有一些不同的偏好,喜欢的 ...

  2. Class Prefix(Xcode6以后设置类前缀)

    点开Xcode右侧Utilities,Project Document->Class Prefix  图片:prefix.png 

  3. Drupal 实战

    <Drupal 实战> 基本信息 作者: 葛红儒    丛书名: 实战系列 出版社:机械工业出版社 ISBN:9787111429999 上架时间:2013-6-28 出版日期:2013 ...

  4. 573 The Snail(蜗牛)

      The Snail  A snail is at the bottom of a 6-foot well and wants to climb to the top. The snail can ...

  5. Android面试,与Service交互方式

    五种交互方式,分别是:通过广播交互.通过共享文件交互.通过Messenger(信使)交互.通过自定义接口交互.通过AIDL交互.(可能更多) Service与Thread的区别 Thread:Thre ...

  6. sass笔记-1|Sass是如何帮你又快又好地搞定CSS的

    Sass学习笔记持续整理中,开篇不讲怎么安装,sass是什么,这些搜索引擎会告诉你,我们从sass的作用开始讲起,知道sass用来干什么,有什么作用,我们才能相信用sass的好处,并且时时刻刻想着sa ...

  7. 一个简单的Verilog计数器模型

    一个简单的Verilog计数器模型 功能说明: 向上计数 向下计数 预装载值 一.代码 1.counter代码(counter.v) module counter( input clk, input ...

  8. POJ 2976 Dropping tests 01分数规划

    给出n(n<=1000)个考试的成绩ai和满分bi,要求去掉k个考试成绩,使得剩下的∑ai/∑bi*100最大并输出. 典型的01分数规划 要使∑ai/∑bi最大,不妨设ans=∑ai/∑bi, ...

  9. HDU 5139数据离线处理

    此题可以找到规律f(n) = 1! * 2! *...*n!, 如果直接打表的话,由于n比较大(10000000),所以会超内存,这时候就要用到离线处理数据,就是先把数据存起来,到最后在暴力一遍求解就 ...

  10. OD: DEP - Ret2Libc via VirtualProtect() & VirtualAlloc()

    一,通过 VirutalProtect() 修改内存属性绕过 DEP DEP 的四种工作模式中,OptOut 和 AlwaysOn 下所有进程默认都开启 DEP 保护,这里如果一个程序自身需要从堆栈中 ...