Keil C51程序自动加载了一个名为”STARTUP.A51”的文件,在这个文件里面进行了一系列的初始化操作后进入用户编写的C语言程序入口main函数中,main函数执行完毕后,STARTUP.A51文件后有一句跳转到程序入口main函数的语句,所以会再次进入C语言主程序main函数中执行相关内容。

通过Keil编译器建立工程时,Keil会提示是否添加STARTUP.A51文件到工程,该文件即为51单片机启动代码。

51单片机复位后马上执行STARTUP.A51文件中的启动代码,根据启动代码中的设置依次执行以下操作:

  1. 内部RAM清零

  2. 外部RAM清零

  3. 3.清零分页的外部RAM

  4. 初始化SMALL内存模型的可重入模拟堆栈及其堆栈指针

  5. 初始化LARGE内存模型的可重入模拟堆栈及其堆栈指针

  6. 初始化COMPACT内存模型的可重入模拟堆栈及其堆栈指针

  7. 初始化8051单片机的硬件堆栈指针

  8. 将系统控制权转交给初始化全局变量的代码,如果没有被初始化的全局变量则转交给C程序文件中的main函数。

STARTUP.A51启动文件中定义了一些常量,修改这些常量可以控制单片机复位时执行的动作:

1、IDATALEN

指定idata区需要清零的字节数。默认值为80H,这是由于8051系列单片机都包含至少128字节内部ram。对于8052系列单片机或者其他系列具有256字节内部ram的,可以将此值改为100H。

2、XDATASTART

指定外部ram需要清零的区域起始地址。

3、XDATALEN

指示xdata区需要清零的字节数,默认值为0.。

4、PDATASTART

指示需要清零的pdata区起始地址。

5、PDATALEN

指示pdata区需要清零的字节数,默认值为0。

6、IBPSTACK

指示是否初始化small内存模型的可重入模拟堆栈指针(?C_IBP)。默认值为0,不初始化该指针。值设为1,编译器将初始化该指针。

7、IBPSTACKTOP

指示small内存模型下可重入堆栈栈顶。默认值为idata区的0xff。该堆栈区是否可用完全由用户负责,编译器并不会帮助检查该地址指定的堆栈区是否与程序使用的ram、硬件堆栈存在冲突。

8、XBPSTACK

指示是否初始化large内存模型的可重入堆栈指针(?C_XBP)。默认值为0,不初始化该指针。值设为1,编译器将初始化该指针。

9、XBPSTACKTOP

指示large内存模型的可重入堆栈栈顶。默认值为xdata区的0xffff。该值指定的区域是否与程序使用的ram、硬件堆栈冲突由用户负责。编译器不做检查。

10、PBPSTACK

指示是否初始化compact内存模型的可重入堆栈栈指针(?C_PBP)。默认值为0,不初始化该指针。值设为1,编译器将初始化该指针。

11、PBPSTACKTOP

指示compact内存模型可重入堆栈栈顶。默认值为pdata区的0xff。该区域选择是否合理完全由用户负责,编译器不做检查。

12、PPAGEENABLE

该值决定是否初始化Port 2的值,以便pdata区寻址。默认值为0,不初始化Port 2。pdata寻址使用Port 2的值作为地址的高字节。

13、PPAGE

指示对Port 2 设置的值,用于pdata寻址时作为地址高字节。例如,pdata区起始位置为xdata区的0x1000,则PPAGEENABLE应该设为1,并且PPAGE应该设为0x10。

keil在STARTUP.A51中的原代码

$NOMOD51;Ax51宏汇编器控制命令,禁止预定义的8051。使编译器不使能预定义的;8051符号,避免产生重复定义的错误。
;------------------------------------------------------------------------------
;  This file is part of the C51 Compiler package
;  Copyright (c) 1988-2005 Keil Elektronik GmbH and Keil Software, Inc.
;  Version 8.01
;
;  *** <<< Use Configuration Wizard in Context Menu >>> ***
;------------------------------------------------------------------------------
;  STARTUP.A51:  This code is executed after processor reset.
;
;  To translate this file use A51 with the following invocation:
;
;     A51 STARTUP.A51
;
;  To link the modified STARTUP.OBJ file to your application use the following
;  Lx51 invocation:
;
;     Lx51 your object file list, STARTUP.OBJ  controls
;
;------------------------------------------------------------------------------
;
;  User-defined <h> Power-On Initialization of Memory
;
;  With the following EQU statements the initialization of memory
;  at processor reset can be defined:
;
; <o> IDATALEN: IDATA memory size <0x0-0x100>
;     <i> Note: The absolute start-address of IDATA memory is always 0
;     <i>       The IDATA space overlaps physically the DATA and BIT areas.
IDATALEN        EQU     80H
;
; <o> XDATASTART: XDATA memory start address <0x0-0xFFFF>
;     <i> The absolute start address of XDATA memory
XDATASTART      EQU     0
;
; <o> XDATALEN: XDATA memory size <0x0-0xFFFF>
;     <i> The length of XDATA memory in bytes.
XDATALEN        EQU     0
;
; <o> PDATASTART: PDATA memory start address <0x0-0xFFFF>
;     <i> The absolute start address of PDATA memory
PDATASTART      EQU     0H
;
; <o> PDATALEN: PDATA memory size <0x0-0xFF>
;     <i> The length of PDATA memory in bytes.
PDATALEN        EQU     0H
;
;</h>
;------------------------------------------------------------------------------
;
;<h> Reentrant Stack Initialization
;
;  The following EQU statements define the stack pointer for reentrant
;  functions and initialized it:
;
; <h> Stack Space for reentrant functions in the SMALL model.
;  <q> IBPSTACK: Enable SMALL model reentrant stack
;     <i> Stack space for reentrant functions in the SMALL model.
IBPSTACK        EQU     0       ; set to 1 if small reentrant is used.
;  <o> IBPSTACKTOP: End address of SMALL model stack <0x0-0xFF>
;     <i> Set the top of the stack to the highest location.
IBPSTACKTOP     EQU     0xFF +1     ; default 0FFH+1
; </h>
;
; <h> Stack Space for reentrant functions in the LARGE model.
;  <q> XBPSTACK: Enable LARGE model reentrant stack
;     <i> Stack space for reentrant functions in the LARGE model.
XBPSTACK        EQU     0       ; set to 1 if large reentrant is used.
;  <o> XBPSTACKTOP: End address of LARGE model stack <0x0-0xFFFF>
;     <i> Set the top of the stack to the highest location.
XBPSTACKTOP     EQU     0xFFFF +1   ; default 0FFFFH+1
; </h>
;
; <h> Stack Space for reentrant functions in the COMPACT model.
;  <q> PBPSTACK: Enable COMPACT model reentrant stack
;     <i> Stack space for reentrant functions in the COMPACT model.
PBPSTACK        EQU     0       ; set to 1 if compact reentrant is used.
;
;   <o> PBPSTACKTOP: End address of COMPACT model stack <0x0-0xFFFF>
;     <i> Set the top of the stack to the highest location.
PBPSTACKTOP     EQU     0xFF +1     ; default 0FFH+1
; </h>
;</h>
;------------------------------------------------------------------------------
;
;  Memory Page for Using the Compact Model with 64 KByte xdata RAM
;  <e>Compact Model Page Definition
;
;  <i>Define the XDATA page used for PDATA variables.
;  <i>PPAGE must conform with the PPAGE set in the linker invocation.
;
; Enable pdata memory page initalization
PPAGEENABLE     EQU     0       ; set to 1 if pdata object are used.
;
; <o> PPAGE number <0x0-0xFF>
; <i> uppermost 256-byte address of the page used for PDATA variables.
PPAGE           EQU     0
;
; <o> SFR address which supplies uppermost address byte <0x0-0xFF>
; <i> most 8051 variants use P2 as uppermost address byte
PPAGE_SFR       DATA    0A0H
;
; </e>
;------------------------------------------------------------------------------

; Standard SFR Symbols
ACC     DATA    0E0H
B       DATA    0F0H
SP      DATA    81H
DPL     DATA    82H
DPH     DATA    83H

                NAME    ?C_STARTUP


?C_C51STARTUP   SEGMENT   CODE
?STACK          SEGMENT   IDATA

                RSEG    ?STACK
                DS      1

                EXTRN CODE (?C_START)
                PUBLIC  ?C_STARTUP

                CSEG    AT      0
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       ?C_STARTUP:     LJMP    STARTUP1

                RSEG    ?C_C51STARTUP

STARTUP1:

IF IDATALEN <> 0
                MOV     R0,#IDATALEN - 1
                CLR     A
IDATALOOP:      MOV     @R0,A
                DJNZ    R0,IDATALOOP
ENDIF

IF XDATALEN <> 0
                MOV     DPTR,#XDATASTART
                MOV     R7,#LOW (XDATALEN)
  IF (LOW (XDATALEN)) <> 0
                MOV     R6,#(HIGH (XDATALEN)) +1
  ELSE
                MOV     R6,#HIGH (XDATALEN)
  ENDIF
                CLR     A
XDATALOOP:      MOVX    @DPTR,A
                INC     DPTR
                DJNZ    R7,XDATALOOP
                DJNZ    R6,XDATALOOP
ENDIF

IF PPAGEENABLE <> 0
                MOV     PPAGE_SFR,#PPAGE
ENDIF

IF PDATALEN <> 0
                MOV     R0,#LOW (PDATASTART)
                MOV     R7,#LOW (PDATALEN)
                CLR     A
PDATALOOP:      MOVX    @R0,A
                INC     R0
                DJNZ    R7,PDATALOOP
ENDIF

IF IBPSTACK <> 0
EXTRN DATA (?C_IBP)

                MOV     ?C_IBP,#LOW IBPSTACKTOP
ENDIF

IF XBPSTACK <> 0
EXTRN DATA (?C_XBP)

                MOV     ?C_XBP,#HIGH XBPSTACKTOP
                MOV     ?C_XBP+1,#LOW XBPSTACKTOP
ENDIF

IF PBPSTACK <> 0
EXTRN DATA (?C_PBP)
                MOV     ?C_PBP,#LOW PBPSTACKTOP
ENDIF

                MOV     SP,#?STACK-1

; This code is required if you use L51_BANK.A51 with Banking Mode 4
;<h> Code Banking
; <q> Select Bank 0 for L51_BANK.A51 Mode 4
#if 0
;     <i> Initialize bank mechanism to code bank 0 when using L51_BANK.A51 with Banking Mode 4.
EXTRN CODE (?B_SWITCH0)
                CALL    ?B_SWITCH0      ; init bank mechanism to code bank 0
#endif
;</h>
                LJMP    ?C_START

                END

翻译后的STARTUP.A51

$NOMOD51;Ax51宏汇编器控制命令,禁止预定义的8051。使编译器不使能预定义的;8051符号,避免产生重复定义的错误。
;------------------------------------------------------------------------------
;  该文件是C51编译器包的一部分
;  版权所有(c)1988-2005 Keil Elektronik GmbH 和 Keil Software, Inc.
;  版本 8.01
;
;  *** <<< 在上下文菜单中使用配置向导 >>> ***
;------------------------------------------------------------------------------
;  STARTUP.A51:此代码在处理器复位后执行。
;
;  要编译此文件,请调用A51使用以下配置:
;
;     A51 STARTUP.A51
;
;  要将修改后的STARTUP.OBJ文件链接到您的应用程序,请使用以下命令
;  Lx51 invocation:
;
;     Lx51您的目标文件列表,STARTUP.OBJ控件
;
;------------------------------------------------------------------------------
;
;  用户定义的头文件(.h) 内存的上电初始化
;
;  用以下EQU语句初始化内存
;  在处理器重置时可以定义:
;
; <o> IDATALEN: IDATA内存大小<0x0-0x100>
;     <i> 注意: IDATA存储器的绝对起始地址始终为0
;     <i>       IDATA空间在物理上与DATA和BIT区域重叠。
IDATALEN        EQU     80H
;
; <o> XDATASTART: XDATA存储器起始地址<0x0-0xFFFF>
;     <i> XDATA 存储器的绝对起始地址(不一定为0)
XDATASTART      EQU     0 ;XDATA 起始位置
;
; <o> XDATALEN: XDATA 内存大小 <0x0-0xFFFF>
;     <i> XDATA 内存的长度,以字节为单位。
XDATALEN        EQU     0 ; XDATA 结束位置(不设置XDATA)
;
; <o> PDATASTART: PDATA 存储器起始地址<0x0-0xFFFF>
;     <i> PDATA 存储器的绝对起始地址
PDATASTART      EQU     0H    ;PDATA 设置起始地址
;
; <o> PDATALEN: PDATA 内存大小 <0x0-0xFF>
;     <i> PDATA 内存的长度,以字节为单位。
PDATALEN        EQU     0H     ;设置PDATA结束为止(不设置 PDATA)
;
;</h>
;------------------------------------------------------------------------------
;
;<h> 可重入堆栈初始化
;
;  以下EQU语句定义了可重入函数的堆栈指针并对其进行了初始化:
;
;
; <h> 用于SMALL模型中的可重入函数的堆栈空间。
;  <q> IBPSTACK: 启用SMALL模型可重入堆栈
;     <i> 在SMALL模型中为可重入函数堆栈空间。
IBPSTACK        EQU     0       ; 如果使用小重载,则设置为1。
;  <o> IBPSTACKTOP: SMALL模型堆栈的结束地址 <0x0-0xFF>
;     <i> 将堆栈顶部设置为最高位置.
IBPSTACKTOP     EQU     0xFF +1     ; 默认 0FFH+1
; </h>
;
; <h> 堆栈空间用于LARGE模型中的可重入函数.
;  <q> XBPSTACK: 启用LARGE模型可重入堆栈
;     <i> Stack 在LARGE模型中为可重入函数堆栈空间.
XBPSTACK        EQU     0       ; 如果使用大的重入,则设置为1。
;  <o> XBPSTACKTOP: LARGE模型堆栈的结束地址 <0x0-0xFFFF>
;     <i> 将堆栈顶部设置为最高位置.
XBPSTACKTOP     EQU     0xFFFF +1   ; 默认 0FFFFH+1
; </h>
;
; <h> 用于COMPACT模型中的重入函数的堆栈空间.
;  <q> PBPSTACK: 启用COMPACT模型可重入堆栈
;     <i> COMPACT模型中可重入函数的空间.
PBPSTACK        EQU     0       ; 如果使用紧凑重入,则设置为1.
;
;   <o> PBPSTACKTOP: COMPACT模型堆栈的结束地址 <0x0-0xFFFF>
;     <i> 将堆栈顶部设置为最高位置.
PBPSTACKTOP     EQU     0xFF +1     ; 默认 0FFH+1
; </h>
;</h>
;------------------------------------------------------------------------------
;
;  使用64 KB xteata RAM的紧凑型号的内存页面
;  <e>紧凑型模型页面定义
;
;  <i>定义用于PDATA变量的XDATA页面.
;  <i>PAGE必须符合链接器调用中设置的PAGE.
;
; 启用数据存储页面初始化
PPAGEENABLE     EQU     0       ; 如果使用pdata对象,则设置为1.
;
; <o> 页码 <0x0-0xFF>
; <i> 用于PDATA变量的页面的最高256字节地址.
PPAGE           EQU     0
;
; <o> 提供最高地址字节的SFR地址 <0x0-0xFF>
; <i> 大多数8051变体使用P2作为最高地址字节
PPAGE_SFR       DATA    0A0H
;
; </e>
;------------------------------------------------------------------------------

; 标准SFR(特殊功能寄存器)符号(寄存器定义声称别名)(sfr特殊功能寄存器 sfr也是一种扩充数据类型,点用一个内存单元,值域为0~255。利用它可以访问51单片机内部的所有特殊功能寄存器。)
ACC     DATA    0E0H;累加器
B       DATA    0F0H;通用寄存器
SP      DATA    81H;堆栈指针
DPL     DATA    82H;数据指针低8位
DPH     DATA    83H;数据指针高8位

                NAME    ?C_STARTUP;定义一个模块名: ?C_STARTUP


?C_C51STARTUP   SEGMENT   CODE;声明(ROM)代码段
?STACK          SEGMENT   IDATA;声明(RAM)堆栈

                RSEG    ?STACK;RSEG选择一个先前声明的可重定位段
                DS      1;位堆栈预留一个低阶的储存空间

                EXTRN CODE (?C_START);当前文件的代码储存区
                PUBLIC  ?C_STARTUP;声明可以用于其他目标模块的全局符号?C_STARTUP,用于和 C 链接在.src文件中可以看到这个符号

                CSEG    AT      0;选择代码存储区的一个绝对段,汇编从上面命令中的地址0开始执行这个段。
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       ?C_STARTUP:     LJMP    STARTUP1

                RSEG    ?C_C51STARTUP;选择代码段?C_C51STARTUP

STARTUP1:        ;代码块STARTUP1

IF IDATALEN <> 0;如果IDATALEN不为零,则将长度减1送R0
                MOV     R0,#IDATALEN - 1    ;将长度-1送R0
                CLR     A                ;清零累加器
IDATALOOP:      MOV     @R0,A            ;初始化IDATA
                DJNZ    R0,IDATALOOP    ;(DJNZ)R0减1后不为0则转移(跳转)IDATALOOP
ENDIF

IF XDATALEN <> 0;如果有外部数据储存区 XDATALEN为外部空间的长度
                MOV     DPTR,#XDATASTART;起始地址送DPTR
                MOV     R7,#LOW (XDATALEN);长度的低8位送到R7
  IF (LOW (XDATALEN)) <> 0    ;长度的低8位不为零继续
                MOV     R6,#(HIGH (XDATALEN)) +1;高8位+1送R6,下面清0用
  ELSE                        ;长度的低8位为零继续
                MOV     R6,#HIGH (XDATALEN);高8位直接送R6,下面清0用
  ENDIF
                CLR     A    ;清零累加器
XDATALOOP:      MOVX    @DPTR,A;XDATA环节 送A(清零累加器)的数据到单片机外部(ROM)(DPTR是16位地址位)
                INC     DPTR;数据指针加 1
                DJNZ    R7,XDATALOOP;R7减1后不为0则转移XDATALOOP
                DJNZ    R6,XDATALOOP;R6减1后不为0则转移XDATALOOP
ENDIF

IF PPAGEENABLE <> 0;外部ram页面地址使能,(pdata     --->   分页寻址片外ram (MOVX @R0))
                MOV     PPAGE_SFR,#PPAGE;上面一条成立,PPAGE_SFR设置为大多数8051变体使用P2作为最高地址字节,如果不成立,说明没有使用
ENDIF

IF PDATALEN <> 0;判断外部PDATA的长度是否为0(有没有配置外部RAM),如果不为零(使用了外部RAM),则进行下面的操作。
                MOV     R0,#LOW (PDATASTART);获取外部的RAM(PDATA)起始地址的低8位
                MOV     R7,#LOW (PDATALEN);获取外部的RAM(PDATA)长度的低8位
                CLR     A;清零累加器
PDATALOOP:      MOVX    @R0,A;PDATA环节,送A(清零累加器)的数据到单片机外部(RAM)(@R0是8位地址位)
                INC     R0;数据指针加 1
                DJNZ    R7,PDATALOOP;R7减1后不为0则转移PDATALOOP
ENDIF

IF IBPSTACK <> 0;可重载的模拟栈设置:http://www.21ic.com/jichuzhishi/mcu/questions/2018-05-02/759519.html
EXTRN DATA (?C_IBP);使用其他目标模块中定义的?C_IBP(模拟栈指针)(.M51文件中)

                MOV     ?C_IBP,#LOW IBPSTACKTOP;模拟栈指针指向栈顶
ENDIF

IF XBPSTACK <> 0 ;Large模式下使能模拟栈
EXTRN DATA (?C_XBP)
                 ;栈指针指向栈顶
                MOV     ?C_XBP,#HIGH XBPSTACKTOP
                MOV     ?C_XBP+1,#LOW XBPSTACKTOP
ENDIF

IF PBPSTACK <> 0 ;COMPACT模式下的模拟栈
EXTRN DATA (?C_PBP)
                MOV     ?C_PBP,#LOW PBPSTACKTOP;指向栈顶
ENDIF

                MOV     SP,#?STACK-1;硬件栈SP赋值

; 如果您将L51_BANK.A51与Banking Mode 4一起使用,则需要此代码
;<h> 代码银行
; <q> 为L51_BANK.A51模式4选择Bank 0
#if 0
;     <i> 在银行模式4下使用L51_BANK.A51时,将银行机制初始化为代码库0.
EXTRN CODE (?B_SWITCH0)
                CALL    ?B_SWITCH0      ; 代码银行0的初始银行机制
#endif
;</h>
                LJMP    ?C_START ;执行main函数

                END