汇编是从org 0000h开始启动,那么keil c51是如何启动main()函数的?keil c51有一个启动程序startup.a51,它总是和c程序一起编译和链接。下面看看它和main()函数是如何编译的;
//主函数如下;
void main(void)
{
    while (1)    这是个无条件空循环。    
    {
    }
}

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

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

?STACK          SEGMENT   IDATA

RSEG    ?STACK
                DS      1

CSEG    AT      0
?C_STARTUP:     LJMP    STARTUP1 
C:0x0000    020003   LJMP     STARTUP1(C:0003)

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 
C:0x0007    D8FD     DJNZ     R0,IDATALOOP(C:0006)
;          MOV     SP,#?STACK-1             ;设制CPU的堆栈起始地址
C:0x0009    758107   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;
    while (1)
    {
        c=SumXY(a,b);
    }
}

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

编译后的反汇编代码列表;
C:0x0000    020027   LJMP     STARTUP1(C:0027)

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

8:         while (1) 
     9:         { 
    10:                 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
C:0x0017    120020   LCALL    SumXY(C:0020)    ;调用函数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
    11:         } 
    12: } 
    13:  
C:0x001E    80EF     SJMP     C:000F

14: unsigned int SumXY(unsigned int X,Y) 
    15: {unsigned int Z; 
    16:         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里
    17:         return Z;             ;由寄存器R6和R7里返回函数的值
C:0x0026    22       RET

151:                 MOV     SP,#?STACK-1 
   152: ; This code is required if you use L51_BANK.A51 with Banking Mode 4 
   153: ; EXTRN CODE (?B_SWITCH0) 
   154: ;               CALL    ?B_SWITCH0      ; init bank mechanism to code bank 0 
C:0x0027    75810D   MOV      SP(0x81),#0x0D
   155:                 LJMP    ?C_START 
C:0x002A    020003   LJMP     main(C:0003)

函数的入口地址,如何调用汇编函数,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;
    while (1)
    {
        Delay(100);
        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;
    for (i=0;i<T;i++)
    {
        for (i=0;i<T;i++) {}
    }
}

/**********************************************************************/
上面程序编译后的反汇编代码列表;
                 C_STARTUP:
C:0x0000    020041   LJMP     STARTUP1(C:0041)

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

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

//求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:0x002A    500D     JNC      C0007(C:0039)
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:0x0031    5003     JNC      C0003(C:0036)
C:0x0033    0E       INC      R6
C:0x0034    80F8     SJMP     C0004(C:002E)
                 C0003:
C:0x0036    0E       INC      R6
C:0x0037    80EE     SJMP     C0001(C:0027)
                 C0007:
C:0x0039    22       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
C:0x0040    22       RET

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

最新文章

  1. .net手机号码归属地查询
  2. unity, 按类型查找文件
  3. exiv2 如何改变时间戳
  4. spring 管理 jdbc 事务
  5. BZOJ3571 : [Hnoi2014]画框
  6. Jersey(1.19.1) - Use of @Context
  7. rowid
  8. MEF学习小结 z
  9. win7安装memcached
  10. C# DataSet数据导入Excel 修正版- .net FrameWork 4.0以上
  11. 剑指架构师系列-spring boot的logback日志记录
  12. 《读书报告 -- Elasticsearch入门 》-- 安装以及简单使用(1)
  13. Swift 之cannot load module &#39;main&#39; as &#39;Swift&#39;
  14. FhqTreap的区间翻转
  15. .Net 从零开始构建一个框架之基本实体结构与基本仓储构建
  16. 《剑指offer》 合并两个排序的链表
  17. PHP 打开已有图片进行编辑
  18. Linux 防火墙iptables开放特定端口
  19. Centos7部署kubernetes集群CA证书创建和分发(二)
  20. final阶段140字评论

热门文章

  1. 硬盘安装windows7
  2. android Handler及消息处理机制的简单介绍
  3. struct2(四)编写Struct2 的Action
  4. android面试题之六
  5. swift 自定义导航栏颜色
  6. java如何从方法返回多个值
  7. Java八个并发学习——线程同步工具CyclicBarrier
  8. Samba-ADS/WINBIND
  9. 关于sys、system、sysman等在EM中登录的问题
  10. Action重定向总结