一些汇编伪指令可将代码和数据的各个部分与相应的段相联系,可以更有效地利用存储器,用户可以将任何段放到存储器的任何存储块上
2. 汇编器对段的处理
汇编器通过段伪指令自动识别各个段,并将段名相同的语句汇编在一起
汇编器有5条伪指令可以识别汇编语言程序的各个不同段.text、.data、.sect创建初始化段
.bss和.usect创建未初始化段
.sect与.usect创建自定义段和子段
2.1 未初始化段
未初始化段占用处理器存储空间,常常分配到RAM
未初始化段在目标文件里没有实际内容,仅仅用于保留存储空间,当程序在运行时用这些空间来创建和存储变量
汇编命令.bss和.usect用来创建未初始化数据区域
.bss symbol, size [,[blocking flag][,alignment flag]]symbol .usect “section name”, size [,[blocking flag][,alignment flag]]
symbol:指向.bss指令创建的段的第一个字,对应该存储空间的变量名。可被其他段引用,被声明为一个全局符号size:为对应段开辟的存储空间大小,单位为字
blocking flag:可选参数。如果赋予一个非零值给该参数,汇编器会连续分配字节空间,这些区域不会超出一页边界,除非该段大于一页。
alignment flag: 可选参数。如果赋予一个非零值给该参数,该段会在一个长字边界开始section name:段名
2.2 .初始化段
初始化段包含可执行代码或者初始化数据;当程序被装载时,它们就被放到处理器存储空间里
每个初始化段独立分配空间,可以引用在其他段定义的标识(symbol),链接器自动处理这些段间引用
定义初始化段的指令:
.text
.data
.sect ”section name”[,value] value表示段指针SPC的初值,默认为0
2.3 自定义段
usect 创建像.bss段那样的段,这些段为变量在RAM开辟存储空间。
.sect创建像.text和.data段那样包含代码和数据的段,可以创建可重分配地址的自定义段。
用户可以创建多达32767个自定义段,段名多至200个字符。
每次使用这两个指令可以用不同的section name来创建不同的段,如果用一个已经使用的section name,那么汇编器将代码和数据都汇编到同一个段。
2.4 子段
子段是更大的段中的较小的段,链接器可以像段一样操作它
子段让用户可以更好的控制存储器映射
可以使用.sect或者.usect指令来创建子段,格式为:
section name:subsection name
例,在段.text中创建一个_func子段如下:
.sect “text:_func”
用户可以为其单独分配地址,也可以和.text段的其他部分一起分配地址
2.5 段指针SPCs
汇编器为每个段安排一个独立的程序计数器,即段程序计数器(SPC)。SPC表示一个程序代码段或数据段内的当前地址。开始时,汇编器将每个SPC置0,当汇编器将程序代码或数据加到一个段内时,相应的SPC增加。如果汇编器再次遇到相同段名的段,继续汇编至相应的段,且相应的SPC在先前的基础上继续增加。3 链接器对段的处理
链接器对段的处理:将一个或多个COFF目标文件(.obj)中的各种段作为链接器的输入段,经链接后在一个可执行的COFF模块(.out)中建立各个输出段为各个输出段选定存储器地址
链接器有2条伪指令支持上述任务:
● MEMORY伪指令——用来定义目标系统的存储器配置空间,包括对存储器各部分命名,以及规定它们的起始地址和长度。● SECTIONS伪指令——用来指定链接器将输入段组合成输出段方式,以及输出段在存储器中的位置,也可用于指定子段。
若未使用伪指令,则链接器将使用目标处理器默认的方法将段放入存储空间。
如果在链接时不使用MEMORY和SECTIONS指令,则链接器使用目的处理器的默认分配算法;有时用户不想使用默认设置,要自己进行存储器映射,就要使用MEMORY和SECTIONS等链接指令。
4 链接器对程序的重新定位
汇编器对每个段汇编时都是从0地址开始,而所有需要重新定位的符号(标号)在段内都是相对于0地址的。事实上,所有段都不可能从存储器中0地址单元开始,因此链接器必须对各个段进行重新定位。
1.地址重定位
重新定位的方法:
将各个段配置到存储器中,使每个段都有一个合适的起始地址;
将符号变量调整到相对于新的段地址的位置;
将引用调整到重新定位后的符号,这些符号
反映了调整后的新符号值。
汇编器在需要引用重新定位的符号处都留了一个重定位入口。链接器在对符号重新定位时,利用这些入口修正对符号的引用值。
2.运行时间重定位
在实际运行中,有时需要将代码装入存储器的一个地方,而在另一个地方运行。
如:一些关键的执行代码必须装在系统的ROM中,但运行时希望在较快的RAM中进行。
利用SECTIONS伪指令选项可让链接器对其定位2次,其方法:
① 使用装入关键字设置装入地址;
② 使用运行关键字设置它的运行地址。
装入地址确定段的原始数据或代码装入的位置,而任何对段的使用(例如其中的标号),则参考它的运行地址。在应用中必须将该段从装入地址复制到运行地址。
如果只为段提供了一次定位(装入或运行),则该段将只定位一次,并且装入和运行地址相同。如果提供了2个地址,则段将被自动定位。
5 COFF文件中的符号
COFF文件中有一张存放程序中的外部符号信息的符号表.该表在连接器重定位和程序调试时都要用到。外部符号:
在一个模块中定义而在另一个模块中引用的符号,由.def、.ref和.global伪指令来定义。.def (定义):在当前模块中定义而在别的模块中引用的符号
.ref(引用):在当前模块中引用而在别的模块中定义的符号
.global(全局):在当前模块中定义而在别的模块中引用的符
符号表:
每当遇到一个外部符号,无论是定义的还是引用的,汇编器都将在符号表中产生一个条目。汇编器还产生一个指到每段的专门符号,链接器使用这些符号将其他引用符号重新定位。
6 C55x汇编器的特点
1.代码字节寻址,数据字寻址。
2.可变长度指令解码
3.存储器模式选择:C54x兼容模式,CPL模式,ARMS模式。
4.MMR寻址警告。
7.汇编伪指令
汇编伪指令为程序提供数据和汇编过程的控制,功能有:把代码和数据汇编到指定的段
在存储区为非初始化变量保留存储空间
控制列表文件的内容
初始化存储器
声明全局变量
指定程序要调用宏指令的宏指令库
检查符号调试信息
汇编伪指令和它的参数必须书写在一行中,可以带有标号和注释
8.宏指令
如果程序中需要多次执行某段程序,可以把这段程序定义(宏定义)为一个宏,然后在需要重复执行这段程序的地方调用这条宏。
如果需要多次引用一个宏,但是每次都有不同的数据,可以在宏里使用参数,每次使用时赋予参数不同值即可
宏指令的作用主要是:
定义自己的宏指令和重新定义已存在的宏指令
简化长的或者复杂的汇编代码
访问指令库
在一个宏里定义有条件和可重复块
在一个宏里操作字符串
控制扩展列表
可以在程序的任何位置定义宏
必须在使用前定义
可以在源文件的开始,在.include/.copy文件或者在一个宏指令库里定义一个宏
宏定义可以嵌套。可以在定义里引用其他的宏,但是这些宏都必须和当前定义的宏在同一个文件里
宏定义的格式:macname .macro [parameter 1][,…,parameter n]
……..宏程序语句或宏伪指令
[.mexit]
.endm
macname: 宏程序名称,必须将名称放在源程序标号域。
.macro: 用来说明该语句为宏定义的第一行伪指令,
必须放在助记符操作码区域。
parameters: 为任选的替代参数,作为宏指令的操作数。
宏程序语句: 每次宏调用时要执行的指令或汇编命令。
宏伪指令: 用于控制宏指令展开的命令。
.mexit: 相当于一条跳到.endm语句。
.endm: 结束宏定义。
9.其他伪指令
.mmregs — 定义存储器映射寄存器的名称,这样就可以用AR0、PMST等助记符替换实际的存储器地址。
...........
10.C55x汇编语言源文件的书写格式
一条语句占源程序的一行
总长度可以是源文件编辑器格式允许的长度
语句的执行部分必须限制在200个字符以内
如果源程序很长,需要书写若干行,可以在前一行用反斜杠字符(\)结束,余下部分接着在下一行继续书写
标号是由字母、数字以及下划线和$等组成,最多可32个字符
标号分大小写,且第一个字符不能是数字助记符用来表示指令所完成的操作,可以是汇编语言指令、汇编伪指令、宏指令。其中:
助记符指令:一般用大写,不能从第一列开始
汇编伪指令:用来为程序提供数据和控制汇编进程,
以句号“.”开始,且用小写
宏指令:用来定义一段程序,以便宏调用来调用这段程序,
以句号“.”开始,且用小写
宏调用:用来调用由宏伪指令定义的程序段
作为操作数的前缀有三种情况:
使用“#”号作为前缀,汇编器将操作数作为立即数处理
使用“*”符号作为前缀,汇编器将操作数作为间接地址,即把操作数的内容作为地址
使用“@” 符号作为操作数的前缀。汇编器将操作数作为直接地址,即操作数由直接地址码赋值
汇编程序中的符号用于标号、常数和替代字符。由字母、数字及下划线和美元符号(A~Z,a~z,0~9,_和$)等组成。
符号名最多可长达200个字符。
在符号中,第1位不能是数字,并且符号中不能含空格。
内建数学函数中的表达式必须为常数
11 TMS320C55x链接器
TMS320C55x链接器有两个功能强大的指令,即MEMORY和SECTIONS。
MEMORY指令允许用户定义一个目标系统的存储器映射,可以命名存储器的各个部分,并且指定开始地址和大小。
SECTIONS指令告诉链接器合成输入段为输出段,并且告诉链接器把这些输出段放在存储器的某个位置。
MEMORY: 用来指定目标存储器结构
SECTIONS:用来控制段的构成与地址分配
1 、MEMORY指令
MEMORY{
[PAGE 0:] name_1[(attr)]:o=constant,l=constant;
[PAGE n:] name_n[(attr)]:o=constant,l=constant;
}
其中:
PAGE:
用于识别一个存储空间,可以使用多达255个页
通常页0对应程序存储空间,页1对应存储器空间
每个页面表现为一个完全独立的地址空间
Name:
命名一个存储空间
可以是任意合法字符
存储空间名字仅对链接器有用,在输出文件或者符号里不再保留
在不同页的存储空间范围可以有相同的名字,但在一页内不允许不同空间段有相同名字和交叠。
attr:
指定与命名的存储空间范围相联系的
1~4个属性,使用时必须放在小括号里
合法的属性包括:
R:表示该存储空间可读
W:表示该存储空间可写
X:表示该存储空间可以包含可执行代码
I:表示该存储空间可以初始化
o:
指定存储段的开始地址。
值为24位常数,可以是十进制、八进制或十六进制,单位为字节,也可写为org
l:
指定存储段的长度
值为24位常数,可以是十进制、八进制或者十六进制,单位为字节,也可以写len
SECTIONS
{
oname_1:[property, property, property …]
oname_2:[property, property, property …]
oname_3:[property, property, property …]
}
其中:以oname开始的一行定义了一个输出段。段名oname后是属性列表,这些属性定义了段的内容和段如何分配到存储器。一个段可能的属性包括:
Load allocation 定义在存储器中段被装载的位置: load=allocation 或 allocation 或 >allocation
Run allocation 定义在存储器中段运行的位置:
run=allocation 或run > allocation
Input sections 定义组成输出段的输入段:
句法为 {input_sections}