KEIL段重定位

对于一个大的文件,为了便于管理,一个好的办法时把一个大文件分为若干个小文件,每个小文件包含一部分相关的功
能,这样功能将显得很整洁,而且移植到其它工程的时候也很方便,把文件copy过去即可。


对于汇编,我们也许知道,可以使用orgxxxx来指定函数的地址(org是一个段内指定偏移的伪指令),但是当指定这个地址时,是否与其它函数冲突呢?有可能其它函数过长已经占用了这个地址。难道要数手指计算函数的长度吗?另一个问题是主函数怎样调用被调函数呢?

    在回答这些问题之前,先来看看使用的开发工具是怎 样工作的。首先A51汇编器汇编文件后会生成.obj文件,这并不是最终需要的文件。通常还要使用一个叫链接器(如BL51)的工具,把多个obj文件组合起 来,生成最终的二进制代码文件(或其它格式的文件)。虽然传递给链接器的参数是一个或多个obj文件,但是链接器在链接时却是按段来计算的。每
个obj文件里包含的都是一些绝对地址段或/和可重定位段。链接器必须为可重定位段重新计算及分配地址。并计算绝对地址段是否重叠。

    因 此:可得定位段可以保证多目标文件链接时不发生目标地址重叠,故绝对地址段只适用用某些特殊场合,如固定I/O口或中断向量的入口地址

    绝对地址段就是这个段的地址在传入链接器的时候已 经固定,链接器不能改变这个段的地址。

    可重定位段就是这个段的地址需要链接器分配,如是 代码段的段内的跳转等指令的偏移也需要重新进算。

    段的另一种分法可分为代码段,数据段。数据段又分为以初始化数据段和未初始化数据段。这里不细说。


通过上面可知,如果一个函数是一个段,而这个段是可重定位的,那么就可以不用考虑代码的地址问题了,把这个问题留给了链接器。

    剩下来的问题就是怎样把一个函数变为一个段的问题,(当然一个段可包括很多函数,在汇编中,函数只是一个说法而已)。大部分 的汇编器都会提供伪指令来定义绝对段及可重定位段(写法上有可能有区别而已)。
 
在A51中,定义段的伪指令是
cseg -- 绝对代码段
bseg -- 绝 对位段
dseg -- 绝对内部直接寻址(data)数据段
iseg -- 绝 对内部间接寻址(idata)数据段
xseg -- 绝对外部(xdata)数据段

segment -- 定义一个可重定位段
rseg -- 选择一个可重定位段

使用说明:

    NAME 模块名   //用来指明当前程序模块

    重定位段名 SEGMENT 段类型 [重定位类型]     //[重定位类型为可选,段类型为可用的段类型有CODE,DATA,         XDATA,IDATA ,BIT    重定位类型为PAGE,INPAGE,INBLOCK,BITADDRESSABLE,UNIT,OVERLAYABLE    段名等价于段符号,在表达式中用段名表示复合段中的基地址。

    每个函数都包含在一个段内。org 0 改为了 cseg at 0, org 50h则被删除,这样链接程序会自动的为每个重定位段分配地址,也就相当于为函数分配的地址这 样函数与函数之间就没有了gas(^_^,这是Keil的术语,也就相当于空隙),当有gas时,程序是没有使用的,白白浪费了。对于51这样的系统可重定位段的另一个好处就是空间覆盖处 理,这将在下一节细说。

  也许读者会有这样的疑问,之前的示例并没有定义任 何的段,它们工作得也很好啊,那汇编器及链接器是怎么工作的呢?链 接器的工作仍然是不变的,只是汇编器在扫描文件时 发现文件没有定义段,会为每个空间分配一个绝对段,地址从0开始。以code空间为例,如果没有定义段,则默认段从0开始,这时文件中的第一条指令的地址就是0开始,而org伪指令是指定段内偏移的,这里的org 0 有没有也就无所谓了。不信读者可以把这条语句去掉再
汇编一次。

    而当工程里有多个文件的时候,每一个都不定义段,那么每个文件的默认段地址都从地址0开始,这时链接 器就会提示地址空间重叠。 因此可重定位段的用处就在 于此:它不会造成地址空间重叠。

    下面将介绍的几个伪指令对于多文档工作就特别的有 用了。
extrn  -- 指 示符号是外部的,并告诉模块是什么类型
extern -- 跟extrn差不多,相当于extrn的一种特例
public -- 指示符号可在其它模块使用

使用方法如下:
EXTRN class (symbol [ , symbol ... ])
EXTRN class:type (symbol [ , symbol ... ])

EXTERN class:type (symbol [ , symbol ... ])

PUBLIC symbol [ , symbol ... ]

中括号内是可选部分。

 

C51编译器采用了一个扩展关键字 reentrant作为定义函数时的选项,需要将一个函数定义为可重入函数时,只要在函数后面加上关键字reentrant即可。

        与非可重入函数的参数传递和局部变量的存储分配方法不同,C51编译器为可重入函数生成一个模拟栈(相对于系统堆栈或是硬件堆栈来说),通过这个模拟栈来 完成参数传递和存放局部变量。模拟栈以全局变量?C_IBP、?C_PBP和?C_XBP作为栈指针(系统 堆栈栈顶指针为SP),这些变量定义在DATA地址空间,并且可在文件startup.a51中进行初始化。根据编译时采用的存储器模式, 模拟栈区可位于内部(IDATA)或外部(PDATA或XDATA)存储器中。如表1所示:

 

存储模式

栈指针

栈区域

Small

?C_IBP(1  字节)

间接访问的内部数据存储器(IDATA),栈区最大为256字节

Compact

?C_PBP(1字节)

分页寻址的外部数据存储器(PDATA),栈区最大为256字节

Large

?C_XBP(2字节)

外部数据存储器(XDATA),栈区最大为64K

表1注意:51系列单片机的系统堆栈(也叫硬件堆栈或常规栈)总是位于内部数据存储器中 (SP为 8位寄存器,只能指向内部),而且是“向上生长”型的(从低地址向高地址),而模 拟栈是“向下生长”型的


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值