1·什么是CMD文件,它有什么作用。
CMD文件是用来说明对应的对应的代码、数据、变量的存放空间。
它包括两个指令SECTOINS和MEMORY。
如果把RAM和ROM看成是两个仓库的话,那MEMORY指令就是把这两个仓库再分成不同的区域。如果把不同的代码段是看成一件件货物的话,那么SECTOINS指令则指出了这些货物对应的存放位置。
.cmd文件
由汇编器产生的COFF格式的OBJ文件中的段作为构造块,当有多个文件进行链接时,链接器会将输入段结全在一起产生可执行的COFF输出模块,然后链接器为各输出段选择存储器地址。
1.1 MEMORY指令说明
存储器(MEMORY)伪指令,用来定义目标系统的存储器空间。MEMORY可以定义存储器的区域,并指定起始地址和长度。
MEMORY伪指令的一般语法:
MEMORY
{
PAGE 0: name1[(attr)]:origin=constant, length=constant;
PAGE n: name1[(attr)]:origin=constant, length=constant;
}
PAGEn中的页号n最大为255。每个PAGE代表一个完全独立的地址空间。通常PAGE0为程序存储器,PAGE1为数据存储器。
Name1:存储器区间名。可包含8个字符。不同PAGE可以取同样的name1,但在同一个PAGE内区间名不可以相同。
Attr:可选项。规定存储器属性。
R,可以对存储器执行读操作
W,可以对存储器执行写操作
X,破除可以装入可执行的程序代码
I,规定可以对存储器进行初始化
Origin:起始地址。
Length:区间长度。
初始化段用SECTIONS可定位两次:装入和运行。如:一些关键的执行代码必须装在系统的ROM中,但希望在较快的RAM中运行。
未初始化段只可被定位一次。
自己写的关于LF2406A的.cmd文件
MEMORY
{
PAGE 0: VECS: origin=0h, length=40h ;中断向量表,40h~43h为安全代码
;或保留代码区,复位向量是0h和1h
FLASH: origin=44h, length=0ffbch ;32Kflash
SARAM: origin=8000h, length=800h ;当PON=1&&DON=0,
;SARAM映射为程序存储空间
B0: origin=ff00h, length=100h ; 256 WORD DARAM,CNF=1时
PAGE 1: MMRS: origin=0h, length=60h ;内部映射寄存器,或保留区间
B2: origin=60h, length=20h ;32 WORD DARAM
B0: origin=200h, length=100h ;256 WORD DARAM,CNF=0时
B1: origin=300h, length=100h ;256 WORD DARAM
SARAM: origin=800h, length=800h ;2K WORD SARAM,DON=1&&PON=0
PF1: origin=7000, length=230h ;外设帧1
EVA: origin=7400, length=32h ;外设帧2
EVB: origin=7500, length=32h ;外设帧3
}
内部的所有的存储器都定义过了,最后的三个PF1、EVA、EVB可以不用定义的,因为是这外设的寄存器映射。
1.2 SECTOINS指令说明
段(SECTOINS)伪指令,告诉链接器如何将输入段结合成输出段并告诉链接器将输出段放在存储器的何处。
SECTIONS
{
Name:[property,property,…]
Name:[property,property,…]
Name:[property,property,…]
}
Name:源程序中的段名。如.text
Property:段的属性参数。一个段的属性参数包括下列五种:
1.2.1 Load allocation,由它定义将输出段加载到存储器中的什么位置。
语法:load: allocation、allocation、>allocation (allocation是将逻辑段定位的地址说明)
例如:.text: load=0x1000 ;将输出段定位到一个特定的地址
.text: load>ROM ;将输出段定位到命名为ROM的存储区
.text: align=0x80 ;关键词align规定输出段.text定位到从地址边界0x80开始
.text: block(128) ;关键词bolck规定段必须在两个地址边界之内,如果段太
;大,就从一个地址边界开始
.text: PAGE0 ;将输出段定位到PAGE0
如果输出段只定位一个位置,则可省去关键字load。如:.text: >ROM
如果要用到一个以上参数,可以将它们排成一行。如:.text: >ROM align 16 PAGE 2
或.text: load(ROM align(16) PAGE(2))
(地址边界是2的N次方幂的地址,如地址边界定为16,则其地址为xxx0h。)
定边界地址用在什么情况下?
1.2.2 Run allocation,由它定义输出段在存储器的什么位置开始运行。
语法:run=allocation或run>allocation
链接器为每个输出段在目标存储器中分配两个地址:加载地址和执行地址。通常这两个地址是相同的。但如要先将程序加载到ROM,然后在RAM中以较快的速度运行。则可两次定位,如:
.fir: load=ROM,run=RAM
1.2.3 Input sections,由它定义哪些输入段组成输出段。
语法:{input_sections}
.text: {*(.text)} 等价于 .text: {}
这样就把所有的.text段链接成.text段输出。
也可以明确的用文件名和段名来确定输入段:
.text:
{
F1.obj (.text,sect) ;链接F1.obj的.text、.sect段
F2.obj (sect) ;这里的sect前面的点是本来就不用写还是书上的错误?
F3.obj ;链接f3.obj的所有段
}
1.2.4 Section type,用它为输出段定义特殊形式的标志
语法:Type=COPY、Type= DSECT、Type=NOLOAD
1.2.5 Fill value,当初始化段中存在未初始化的存储区间时,对其填充一指定值。
语法:fill: value 或name:…{…}=value
1.3问题
1.3.1如果不同页中定义了相同的名字,又不指定PROG,这时默认在哪个空间呢?
如:PAGE 0: B0: origin=0ff00h, length=100h ; 256 WORD DARAM,CNF=1时
PAGE 1: B0: origin=200h, length=100h ;256 WORD DARAM,CNF=0时
这明 .const: {} >B1 是指向那个空间?
1.3.2
-o file.out
-m file.map
以上两个语句用来生成.out和.map文件。如果没写这,也没有关系,CCS->project->Build options菜单下的Linker选项页里面还可以设置。
2·代码中关于对段的定义
对段定义目的:上面已经说明了CMD文件的作用是仓库分区和指定货物的存放位置,而对代码中段的定义则是给代码,数据,变量这些货物起名字,使它对应CMD文件里的段名存放。
2.1 段的分类
2.1.1目标文件至少包含三个默认段:
.text 文本段 通常包含可执行代码
.data 数据段 通常包含初始化的数据
.bss 保留空间段 通常为没有初始化的变量保留空间
2.1.2自定义段:
.usect 保留空间段 为没有初始化的数据保留空间的自定义段
.sect 初始化段 自定义段
和默认段的使用相同,但它们被单独汇编。例如,重复使用.text段在目标文件中创建单个.text段,在链接时,这个.text段作为单个单元分配到存储器中。假中有一部分可执行代码(如初始化程序)不希望和.text段分配在一起,可以将它们汇编进一个自定义段,这样就可以分配在与.text段不同的地方。
不能用不同的伪指令定义相同的段。如.usect和.sect用同一个段名。
2.1.3初始化段
.text
.data
.sect “section name(段名)”
当汇编器第一次遇到一个.data时,.data段为空的。跟在第一个.data伪指令后的指令被汇编进.data段,直到遇到.text、.sect、.asect。如果后面还遇到.data,则将跟在这些.data后面的语句汇编时已经存在的.data段。这样就形成了单个可被连续分配到存储器中的.data段。
2.1.4未初始化段
.bss symbol(符号), size in words(字数)[blocking flag]
symbol .usect “section name(段名)”size in words,[blocking flag]
symbol:指向.bss或.usect伪指令所保留的存储空间的第一个字。这个符号与保留空间所使用的变量名相对应。这个符号可以让其化段引用,并且也可以用一个全局符号(.global)来声明。
相当于C中定义 unsigned char symbol[size in words];
Size in words:保留空间大小。
Blocking flag:可选项。如果该参数指定一个大于0的值,则汇编器会将size in words指定的字数连续存放,分配的空间不跨页面的边界,除非字数超过一页的长度,此时将从一个页面边界开始。
Section name:为保留空间的自定义段的段名。0~8个字符。最多可产生32767个不同的自定义段。
和symbol有什么不同?
段名是用来汇编器联接用的,程序里用不到;而符号是地址,程序里可以引用。
自定义的段名第一个字符也可以是 . ,如:sect “.cinit”。
初始化段定义伪指令告诉汇编器停止汇编进入当前的段,而开始汇编进入指定的段,但未初始化段定义伪指令不终止当前的段而开始一个新段,它们只是简间的临时离开当前的段。所以.bss和.usect可以出现在初始化段的任何地方而不会影响它的内容。
2.1.5 作用
链接器将段重新定位到目标系统的存储器映射。大多数系统包含几种存储器,使用段可以使目标存储器的使用更为有效。所有段都可独立地重新定位,可将任意段放到目标存储器任何已经分配的块。
2.2 问题
2.2.1
.data
Coeff .word 011h,022h,033h
.bss var1,1
.bss buffer,10
Ptr .word 0123
.text
Add: LAC 0FH
Aloop: SBLK 1
BLEZ aloop
SACL var1,0
.data
Ivals .word 0aah,0bbh
Var2 .usect “newvars”,1
Inbuf .usect “newvars”,7
.text
ADD #0FFH
.text 7(WORDS)
.data 5
.bss 11
.newvars 8(由.usect创建的段)
在两本书中看到这个例子,其中.data段都是5个字的目标码。
Ptr .word 0123不算是.data段吗?那是什么段?为什么没有计算进去,是书上写错了?
查看.map文件,可以看出.data 段是6个字的目标码,应该是书本错了。
2.2.2
编译连接都通过,为什么生成的.out文件无法下载,提示0x0地址数据错误。
查看Disassembly,8000h前面的数据全为FF,8000h~87ffh即SARAM区里有程序PRG_inti,8800H后的数据全和87FFH里的数据相同。
8000h~87ffh里的程序是否是用来清除、擦除、烧写FLASH用的?其只要打开CSS就存在。
答:SECTOINS段里的load指出的是LOAD PROGRAM与FLASH烧写时的共同地址。
所以如果LOAD PROGRAM时把load地址定到FLASH空间就会出现上面的错误。而FLASH烧写当然就不会了。
所以LOAD PROGRAM与FLASH烧写要写不同的CMD文件。
SECTOINS段里的run指出的是软件调试运行时的地址,如:
MEMORY
{
PAGE 0: VECS: origin=0h, length=40h /*中断向量表*/
FLASH: origin=100h, length=7000h /*;32Kflash*/
SARAM: origin=8000h, length=800h /*;当PON=1&&DON=0,*/
/*;SARAM映射为程序存储空间*/
PAGE 1: MMRS: origin=0h, length=60h /*;内部映射寄存器,或保留区间*/
B2: origin=60h, length=20h /*;32 WORDS DARAM*/
B0: origin=200h, length=100h /*;256 WORDS DARAM,CNF=0时*/
B1: origin=300h, length=100h /*;256 WORDS DARAM*/
}
SECTIONS
{
vectors: {} load=VECS, run=SARAM PAGE 0
.asminti: {} load=FLASH, run=SARAM PAGE 0/**/
.text: {} load=FLASH, run=SARAM PAGE 0
.data: {} >SARAM PAGE 0
.bss : {} >B2 PAGE 1
}
烧写完FLASH后,复位,地址定位到0000H。单步执行,跳转到主程序入口。.text段烧写在100H地址处开始,但程序却跳到了8043H处,这是关键字run映射的地址。把
vectors: {} load=VECS, run=SARAM PAGE 0
.asminti: {} load=FLASH, run=SARAM PAGE 0/**/
.text: {} load=FLASH, run=SARAM PAGE 0
改成
vectors: {} load=VECS PAGE 0
.asminti: {} load=FLASH PAGE 0/**/
.text: {} load=FLASH PAGE 0
从0000H执行后就跳到了100H。
从中可以看出run指定的只是软件仿真时程序段的映射地址。