第5章 TMS320C54x软件开发 5.1 软件开发过程及开发工具 5.2 公共目标文件格式 5.3 常用汇编伪指令 5.1 软件开发过程及开发工具 5.2 公共目标文件格式 5.3 常用汇编伪指令 5.4 链接器命令文件的编写与使用 5.5 汇编语言程序编写方法 5.6 TMS320C54x C语言编程 5.7 用C语言和汇编语言混合编程
5.1 软件开发过程及开发工具 1.建立源程序 2.C编译器(C Compiler) 3.汇编器(Assembler) 5.1 软件开发过程及开发工具 1.建立源程序 2.C编译器(C Compiler) 3.汇编器(Assembler) 4.连接器(Linker) 5.调试工具 6.十六进制转换公用程序(Hex Conversion Utility) 返回首页
图5-1 TMS320C54x DSP软件开发流程(1)
图5-1 TMS320C54x DSP软件开发流程(2) C源文件 C编译器 汇编 源文件 汇编器 COFF 目标文件 链接器 可执行的 宏源文件 存档器 宏库 目标 文件库 建库工具 运行时 支持库 EPROM 编程器 交叉引用 列表器 调试工具 TMS320C54x 绝对地址 HEX代码 转换工具 图5-1 TMS320C54x DSP软件开发流程(2)
C编译器:用来将C/C++语言源程序自动编译为’C54x的汇编语言源程序。 汇编器:用来将汇编语言源文件汇编成机器语言COFF目标文件。 链接器:将汇编生成的、可重新定位的COFF目标模块组合成一个可执行的COFF目标模块。 归档器:允许用户将一组文件(源文件或目标文件)集中为一个文档文件库。 交叉引用列表器:利用目标文件生成一个交叉引用清单,列出链接的源文件中的符号以及它们的定义和引用情况。
汇编器 调试程序 链接器 . cmd 链接命 令文件 文本编辑器 十六进制 . lst . map 转换程序 列表文件 存储器 HEX500 .asm 源文件 .obj 目标文件 .out 输出文件 . cmd 链接命 令文件 . lst 列表文件 . map 存储器 映像文件 十六进制 转换程序 HEX500 - o - m - l
调试工具 软件仿真器:是一种模拟DSP芯片各种功能并在非实时条件下进行软件调试的调试工具,它不需目标硬件支持,只需在计算机上运行。 硬件仿真器:可扩展的开发系统仿真器(XDS510) ,可用来进行系统级的实时集成调试,是进行DSP芯片软硬件开发的最佳工具。 评价模块EVM板:是一种低成本的开发板,可进行DSP芯片评价、性能评估和有限的系统调试。
表5-1 TMS320C54xV3.50版代码生成工具程序 返回本节
十六进制转换程序 用C/C++语言或汇编语言编写源文件,经C编译器、汇编器生成COFF格式的目标文件,再用链接器进行链接,生成在C54x上可执行的目标代码,然后利用调试工具对可执行的目标代码进行仿真和调试。 当调试完成后,通过Hex代码转换工具,将调试后的可执行目标代码转换成EPROM编程器能接受的代码,并将该代码固化到EPROM中或加载到用户的应用系统中,以便DSP目标系统脱离计算机单独运行。
5.2 公共目标文件格式 5.2.1 COFF文件的基本单元——段 5.2.2 汇编器对段的处理 5.2.3 链接器对段的处理 5.2 公共目标文件格式 5.2.1 COFF文件的基本单元——段 5.2.2 汇编器对段的处理 5.2.3 链接器对段的处理 5.2.4 重新定位 5.2.5 程序装入 5.2.6 COFF文件中的符号 返回首页
5.2.1 COFF文件的基本单元——段 段(sections)是COFF文件中最重要的概念。一个段就是最终在存储器映象中占据连续空间的一个数据或代码块。目标文件中的每一个段都是相互独立的。一般地,COFF目标文件包含3个缺省的段:text段、data段、bss段。 段可以分为两大类,即已初始化段和未初始化段。如图5-2所示为目标文件中的段与目标系统中存储器的关系。
图5-2 目标文件中的段与目标存储器的关系 返回本节
5.2.2 汇编器对段的处理 1.未初始化段 未初始化段主要用来在存储器中保留空间,通常将它们定位到RAM中。这些段在目标文件中没有实际内容,只是保留空间而已。程序可以在运行时利用这些空间建立和存储变量。未初始化段是通过使用.bss和.usect汇编伪指令建立的,两条伪指令的句法分别为: .bss 符号,字数 符号 .usect “段名”,字数
2.已初始化段 已初始化段包含可执行代码或已初始化数据。这些段的内容存储在目标文件中,加载程序时再放到TMS320C54X存储器中。三个用于建立初始化段的伪指令句法分别为: .text [段起点] .data [段起点] .sect “段名”[,段起点]
3.命名段 命名段就是程序员自己定义的段,它与缺省的.text、.data和.bss段一样使用,但与缺省段分开汇编。 data段不同的存储器中,将未初始化的变量汇编到与.bss段不同的存储器中。产生命名段的伪指令为: 符号 .usect “段名”,字数 .sect “段名”[,段起点]
4.子段 子段(Subsections)是大段中的小段。链接器可以像处理段一样处理子段。采用子段可以使存储器图更加紧密。子段的命名句法为: 基段名:子段名 子段也有两种,用.sect命令建立的是已初始化段,用.usect命令建立的是未初始化段。 例如,若要在.text段内建立一个称之为_func的子段,其命令格式: .sect “.text:_func”
5.段程序计数器(SPC) 汇编器为每个段安排一个独立的程序计数器,即段程序计数器(SPC)。SPC表示一个程序代码段或数据段内的当前地址。开始时,汇编器将每个SPC置0,当汇编器将程序代码或数据加到一个段内时,相应的SPC增加。如果汇编器再次遇到相同段名的段,继续汇编至相应的段,且相应的SPC在先前的基础上继续增加。
【例5-1】段命令应用举例。 段命令应用举例 汇编语言源程序: .data coeff .word 044h,055h,066h .bss buffer,8 prt .word 0456h .text add: LD 0Dh,A aloop:SUB #1,A BC aloop,AGEQ ivals .word 0CCh,0DDh,0EEh ;初始化数据段 ;3组数据放入.data段 ;在.bss段保留8个单元 ;0456h放入.data段 ;初始化文本段 ;1字指令 ;2字指令 共计5个字 ;初始化数据段 ;3组数据放入.data段
inbuf .usect “newvars”,8 .text mpy: LD 0Ah,B mloop: MPY #0Ah,B 段命令应用举例 汇编语言源程序: ;建立newvars命名段,保留2个单元 ;在newvars段保留8个单元 ;初始化文本段 ;1字指令 ;2字指令 var2 .usect “newvars”,2 inbuf .usect “newvars”,8 .text mpy: LD 0Ah,B mloop: MPY #0Ah,B BC mloop,BNOV .sect “vectors” .word 044h,088h 共计5个字 ;建立vectors命名段 ;2组数据放入vectors命名段
段命令应用举例 经汇编后,得列表文件(部分): 2 ********************************** 2 ********************************** 3 ** 汇编一个初始化表到.data段 ** 4 ********************************** 5 0000 .data 6 0000 0044 coeff .word 044h,055h,066h 0001 0055 0002 0066 7 ********************************** 8 ** 在.bss段中为变量保留空间 ** 9 ********************************** 10 0000 .bss buffer,8 11 ********************************** 12 ** 仍然在.data 段中 ** 13 ********************************** 14 0003 0456 prt .word 0456h
段命令应用举例 15 ********************************** 16 ** 汇编代码到.text段 ** 15 ********************************** 16 ** 汇编代码到.text段 ** 17 ********************************** 18 0000 .text 19 0000 100d add: LD 0Dh,A 20 0001 f010 aloop: SUB #1, A 0002 0001 21 0003 f842 BC aloop,AGEQ 0004 0001’ 22 ********************************** 23 ** 汇编另一个初始化表到.data 段 ** 24 ********************************** 25 0004 .data 26 0004 00cc ivals .word 0CCh,0DDh,0EEh 0005 00dd 0006 00ee 27 ********************************** 28 ** 为更多的变量定义另一个段 ** 29 ********************************** 30 0000 var2 .usect “newvars”,2 31 0001 inbuf .usect “newvars”,8
段命令应用举例 32 **************************************** 32 **************************************** 33 ** 汇编更多代码到.text段 ** 34 **************************************** 35 0005 .text 36 0005 110a mpy: LD 0Ah,B 37 0006 f166 mloop MPY #0Ah,B 0007 000a 38 0008 f868 BC mloop,BNOV 0009 0006’ 39 **************************************** 40 ** 为中断向量.vectors定义一个自定义段 ** 41 **************************************** 42 0000 .sect “vectors” 43 0000 0044 .word 044h,088h 0001 0088 源程序的行号 段程序 计数器 目标 代码 汇编语言 源程序
汇编语言源程序经过汇编后,共建立了5个段: ● .text段——文本段,段内有10个字可执行 的程序代码。 段命令应用举例 汇编语言源程序经过汇编后,共建立了5个段: ● .text段——文本段,段内有10个字可执行 的程序代码。 ● .data段——已初始化的数据段,段内有7 个字的数据。 ● vectors段——用.sect命令生成的命名段, 段内有2个字的初始化数据。 ● .bss段——未初始化的数据段,在存储器中 为变量保留8个存储单元。 ● newvars段——用.usect命令建立的命名段, 为变量保留10个存储单元。
段命令应用举例 经汇编后,得列表文件(部分): 2 ******************************* 行号 目标代码 段名 100d f010 0001 f842 110a f166 000a F868 0006 0044 0055 0066 0456 00cc 00dd 00ee 0088 没有数据 保留10个字 2 ******************************* 3 ** 汇编一个初始化表到.data段 ** 4 ******************************* 5 0000 .data 6 0000 0044 coeff .word 044h,055h,066h 0001 0055 0002 0066 7 ******************************* 8 ** 在.bss段中为变量保留空间 ** 9 ******************************* 10 0000 .bss buffer,8 11 ******************************* 12 ** 仍然在.data 段中 ** 13 ******************************* 14 0003 0456 prt .word 0456h 5 0000 .data 6 0000 0044 coeff .word 044h,055h,066h 6 0044 .data 6 0055 6 0066 14 0456 10 0000 .bss buffer,8 .bss 10 没有数据 保留8个字 14 0003 0456 prt .word 0456h
段命令应用举例 15 ******************************** 16 ** 汇编代码到.text段 ** 行号 目标代码 段名 100d f010 0001 f842 110a f166 000a F868 0006 6 14 0044 0055 0066 0456 00cc 00dd 00ee .data 0088 10 没有数据 保留8个字 .bss 保留10个字 15 ******************************** 16 ** 汇编代码到.text段 ** 17 ******************************** 18 0000 .text 19 0000 100d add: LD 0Dh,A 20 0001 f010 aloop: SUB #1, A 0002 0001 21 0003 f842 BC aloop,AGEQ 0004 0001’ 22 ********************************** 23 ** 汇编另一个初始化表到.data 段 ** 24 ********************************** 25 0004 .data 26 0004 00cc ivals .word 0CCh,0DDh,0EEh 0005 00dd 0006 00ee 27 ******************************** 28 ** 为更多的变量定义另一个段 ** 29 ******************************** 30 0000 var2 .usect “newvars”,2 31 0001 inbuf .usect “newvars”,8 19 .text 100d 20 f010 20 0001 18 0000 .text 21 f842 19 0000 100d add: LD 0Dh,A 21 0001 20 0001 f010 aloop: SUB #1, A 21 0003 f842 BC aloop,AGEQ .data 25 0004 .data 26 00cc 26 00dd 26 0004 00cc ivals .word 0CCh,0DDh,0EEh 26 00ee 30 0000 var2 .usect “newvars”,2 newvars 31 0001 inbuf .usect “newvars”,8 30 保留2个字 31 保留8个字
段命令应用举例 32 ********************************* 33 ** 汇编更多代码到.text段 ** 32 ********************************* 33 ** 汇编更多代码到.text段 ** 34 ********************************* 35 0005 .text 36 0005 110a mpy: LD 0Ah,B 37 0006 f166 mloop MPY #0Ah,B 0007 000a 38 0008 f868 BC mloop,BNOV 0009 0006’ 39 **************************************** 40 ** 为中断向量.vectors定义一个自定义段 ** 41 **************************************** 42 0000 .sect “vectors” 43 0000 0044 .word 044h,088h 0001 0088 行号 目标代码 段名 19 20 21 100d f010 0001 f842 110a f166 000a F868 0006 .text 6 14 26 0044 0055 0066 0456 00cc 00dd 00ee .data 0088 10 没有数据 保留8个字 .bss 30 31 保留10个字 newvars .text 35 0005 .text 36 0005 110a mpy: LD 0Ah,B 36 110a 37 37 0006 f166 mloop MPY #0Ah,B f168 37 000a 38 f868 38 0006 38 0008 f868 BC mloop,BNOV 42 0000 .sect “vectors” vectors 43 0000 0044 .word 044h,088h 43 0044 43 0088
5.2.3 链接器对段的处理 链接器是开发’C54x器件必不可少的开发工具之一,它对段处理时有2个主要任务: ① 将一个或多个COFF目标文件(.obj文件)中的各种段作为链接器的输入段,经链接后在一个执行的COFF输出模块中建立各个输出段; ② 在程序装入时对其重新定位,为各个输出段选定存储器地址。
链接器有2条伪指令支持上述任务: ● MEMORY伪指令——用来定义目标系统的存储器配置空间,包括对存储器各部分命名,以及规定它们的起始地址和长度。 ● SECTIONS伪指令——用来指定链接器将输入段组合成输出段方式,以及输出段在存储器中的位置,也可用于指定子段。 若未使用伪指令,则链接器将使用目标处理器默认的方法将段放入存储空间。
一、默认的存储器分配 链接器可对多个目标文件进行链接。若链接文件中不使用MEMORY和SECTIONS命令,则为默认方式。 每个目标文件都有.text,.data、.bss段和命名段。若采用默认链接,链接器将对多个目标文件中的各个段进行组合,形成各自的对应段,并将各个段配置到所指定的存储器中,形成可执行的目标模块。 在默认的方式下,链接器将从存储器的0080h开始,对组合后的各段进行存储器配置。
默认的存储器分配方法: ① 将所有.text段组合在一起,形成一个.text段,并分配到程序存储器中; ② 将多个目标文件中的.data段组合在一起,分配到紧接着.text段的程序存储空间中; ③ 将.bss段组合,配置到数据存储器中; ④ 组合命名段。初始化的命名段按顺序分配到紧随.data段的程序存储器,而未初始化命名段将被配置到紧随.bss段的数据存储器中。
默认的存储器分配过程: File1.obj .text1 没有配置 .text .text2 .data1 .data .data2 程序存储器 File1.obj .text1 .data1 .bss1 table_1 (初始化的命名段) u_vars1 (未初始化的命名段) 数据存储器 .text1 没有配置 .text .text2 .data1 .data .data2 .bss1 .bss table_1 .bss2 u_vars1 table table_2 u_vars1 File2.obj .text2 .data2 .bss2 table_2 (初始化的命名段) u_vars2 (未初始化的命名段) FFT u_vars2 FFT FFT 没有使用 没有使用 没有配置
二、段放入存储器空间 若不希望链接器将所有的.text段结合在一起形成单个的.text段,就不能采用默认的方式。 由于DSP硬件系统中可能配置多种类型的存储器,若要把某一段分配到特定类型的存储器中,或将命名段配置特定的地址,则需采用MEMORY和SECTIONS伪指令来配置。 若不采用默认的方式,通常需要建立一个链接命令文件,在命令文件中用MEMORY和SECTIONS伪指令定义存储器和配置段地址。
5.2.4 链接器对程序的重新定位 1. 链接器重新定位 汇编器对每个段汇编时都是从0地址开始,而所有需要重新定位的符号(标号)在段内都是相对于0地址的。事实上,所有段都不可能从存储器中0地址单元开始,因此链接器必须对各个段进行重新定位。 重新定位的方法: 将各个段配置到存储器中,使每个段都有一个 合适的起始地址; 将符号变量调整到相对于新的段地址的位置; 将引用调整到重新定位后的符号,这些符号 反映了调整后的新符号值。
汇编器在需要引用重新定位的符号处都留了一个重定位入口。链接器在对符号重新定位时,利用这些入口修正对符号的引用值。 1. 链接器重新定位 【例4.3.2】一段采用助记符指令编写的程序,经汇编后得列表文件如下: 汇编器在需要引用重新定位的符号处都留了一个重定位入口。链接器在对符号重新定位时,利用这些入口修正对符号的引用值。 1 . ref X 2 . ref Z 3 0000 . text 4 0000 F073 B Y ;产生一个重定位入口 0001 0006’ 5 0002 F073 B Z ;产生一个重定位入口 0003 0000! 6 0004 F020 LD #X,A ;产生一个重定位入口 0005 0000! 7 0006 F7E0 Y: RESET
程序中有三个符号: X、Z——是在另一个模块中定义的; Y——在.text段中定义的。 当程序汇编时,X、Z的值为0——未定义的外部符号 Y的值为6——相对于.text段地址0定义 汇编器形成了两个重定位入口: X和Z:在.text段中为一次外部引用,用符号!表示; Y:是一次内部引用,用符号’表示。 假设链接时,X重新定位在地址7100h .text段起始地址重新定位在7200h
链接器利用两个重定位入口,对目标文件中的两次引用进行修正: f073 B Y 0006’ f020 LD #X,A 0000! 变成 f073 7206’ 变成 f020 7100!
2. 运行时重新定位 在实际运行中,有时需要将代码装入存储器的一个地方,而在另一个地方运行。 如:一些关键的执行代码必须装在系统的ROM中,但运行时希望在较快的RAM中进行。 利用SECTIONS伪指令选项可让链接器对其定位2次,其方法: ① 使用装入关键字设置装入地址; ② 使用运行关键字设置它的运行地址。
装入地址确定段的原始数据或代码装入的位置,而任何对段的使用(例如其中的标号),则参考它的运行地址。在应用中必须将该段从装入地址复制到运行地址。 如果只为段提供了一次定位(装入或运行),则该段将只定位一次,并且装入和运行地址相同。如果提供了2个地址,则段将被自动定位。
5.2.5 程序装入 可以采用以下方法装入程序: 链接器产生可执行的COFF目标文件。可执行的目标文件模块与链接器输入的目标文件具有相同的COFF格式。为了运行程序,在可执行模块中的数据必须传输或装入目标系统存储器中。 使用调试工具转入程序 ’C54x的调试工具包括软件模拟器,XDS仿真器和集成系统CCS。它们都具有内部的装入器,调用装入器的LOAD命令,装入器将程序复制到目标系统的存储器中。 采用Hex转换工具转入程序 可以使用转换工具Hex500,将可执行COFF目标模块转换成几种其他目标格式文件,然后将转换后的文件通过编程器将程序装(烧)进EPROM。
5.2.6 COFF文件中的符号 COFF文件中有一个符号表,主要用来存储程序中有关符号的信息。链接器在执行程序定位时,要使用符号表提供的信息,而调试工具也要使用该表来提供符号调试。 1. 外部符号 是指在一个模块中定义、而在另一个模块中引用的符号。它可以用伪指令.def、.ref或.global来定义。 ●.def在当前模块中定义,并可在别的模块中使用的符号; ●.ref 在当前模块中使用,但在别的模块中定义的符号; ●.global可以是上面的任何一种情况。
【例5-3】说明代码段中外部符号的定义。 x: ADD #56h,A B y .def x .ref y ;定义x ;引用y 的模块引用 ;y在这里引用,它在别的模 块中定义
2. 符号表 每当遇到一个外部符号,无论是定义的还是引用的,汇编器都将在符号表中产生一个条目。 汇编器还产生一个指到每段的专门符号,链接器使用这些符号将其他引用符号重新定位。
5.3 常用汇编伪指令 表5-2 常用的汇编伪指令 返回首页
1.段定义伪指令 为便于链接器将程序、数据分段定位于指定的(物理存在的)存储器空间,并将不同的obj文件链接起来。段的使用非常灵活,但常用以下约定: .text — 此段存放程序代码。 .data — 此段存放初始化了的数据。 .bss — 此段存入未初始化的变量。 .sect ‘名称’ — 定义一个有名段,放初始化了的数 据或程序代码。
2.条件汇编伪指令 .if、.elseif、.else、.endif伪指令告诉汇编器按照表达式的计算结果对代码块进行条件汇编。 .if expression — 标志条件块的开始,仅当条件为真(expression的值非0即为真)时汇编代码。 .elseif expression — 标志若.if条件为假,而.elseif条件为真时要汇编代码块。 .else — 标志若.if条件为假时要汇编代码块。 .endif — 标志条件块的结束,并终止该条件代码块。
如: .copy “d:\dsp\file.asm” 3.引用其他文件的伪指令 .copy “文件名” — 将指定文件复制到当前位置,其内容可以是程序、数据、符号定义等。 .include “文件名” — 与.copy类似。只是被copy的文件名会出现在汇编时生成的列表中。 如: .copy “d:\dsp\file.asm”
如:.def new1,new2 .def 符号 [,…,符号] .global 符号 [,…,符号] .ref 符号 [,…,符号] .global 符号名 — 其作用相当于.def、.ref效果之和。 .def 符号 [,…,符号] .global 符号 [,…,符号] .ref 符号 [,…,符号] 如:.def new1,new2
4.初始化常数伪指令 .word 数1,数2 — 指定一个或多个16位带符号整数(十六进制)连续放置到存储器中。 .int 数1,数2 — 指定一个或多个16位无符号整数(十六进制)连续放置到存储器中。 .float 数1,数2 — 指定的各浮点数连续放置到存储器中(从当前段指针开始)。 .space n — 以位为单位,空出n位存储空间。 .string “字符串” [,…,“字符串”] —初始化一个或多个字符串。把8位字符从一个或多个字符串放进当前段。
【例5-4】比较.word ,.int,. float和.string伪指令。 源程序: .word 0CCCh .int 0DDDDh .float 1.99999 .string “help” 15 0 0 C C C .word:将一个或多个16位值放入当前段的连续字中。 .int:将一个或多个16位值放入当前段的连续的字中。 1 D D D D 15 0 15 0 2,3 3 F F F F F A C .float:初始化单精度(32位) 浮点数,并保存在当前段的两个连续的字中。 .string:将一个或多个字符串中的8位字符放入当前段中。 h e 4,5 0 0 6 8 0 0 6 5 l P 6,7 0 0 6 C 0 0 7 0
列表文件: 1 000000 0ccc .word 0CCCh 2 000001 dddd .int 0DDDDh 3 000002 3fff .float 1.99999 000003 ffac 8 000004 0068 .string “help” 000005 0065 000006 006c 000007 0070 .float:自动对准最近长字边界
5.在汇编时定义符号的命令 助记符及语法格式 说 明 把一个字符串赋给一个替代符号。替代符号也可以重新被定义。 说 明 .asg “字符串”,替代符号 把一个字符串赋给一个替代符号。替代符号也可以重新被定义。 .label 符号 定义一个特殊的符号,用来指向在当前段内的装载时间地址。 符号 .set 值 用于给符号赋值。符号被存放在符号表中,而且不能被重新定义。
6.对准段程序计数器的伪指令 .align 操作数 — 用于将段程序计数器(SPC)对准在1~128字的边界。操作数必须是在20~216之间且等于2的幂(当然超过27是没有意义的)。 例如:操作数为1时,对准SPC到字的边界; 操作数为2时,对准SPC到长字/偶字的边界; 操作数为128时,对准SPC到页面的边界; 没有操作数时,.align伪指令默认为128,对准页面边界。
【例5-5】 .align伪指令的使用。 源程序: .word 1234H ; 将数值1234放入字指定域中 .align 2 .string “Errorcnt” .align .int 4000H ; 将数值1234放入字指定域中 ; 对准长字边界,SPC=02h ; 初始化字符串 ; 对准页边界,SPC=80h ;将数值4000放入指定域中
列表文件: 1 000000 1234 .WORD 1234H 2 .align 2 3 000002 0045 .string “Errorcnt” 000003 0072 000004 0072 000005 006f 000006 0072 000007 0063 000008 006e 000009 0074 4 .align 5 000080 0004 .int 4000H 000000 SPC 1234 000001 SPC XXXX 000002 SPC 0045 000003 SPC 0072 000004 SPC 0072 000005 SPC 006F 000006 SPC 0072 000007 SPC 0063 000008 SPC 006E 000009 SPC 0074 000080 SPC 4000
7.宏定义和宏调用伪指令 TMS320C54x汇编支持宏语言。如果程序中需要多次执行某段程序,可以把这段程序定义(宏定义)为一个宏,然后在需要重复执行这段程序的地方调用这条宏。 返回本节
宏定义的格式: macname .macro [parameter 1][,…,parameter n] ……..宏程序语句或宏伪指令 [.mexit] .endm macname: 宏程序名称,必须将名称放在源程序标号域。 .macro: 用来说明该语句为宏定义的第一行伪指令, 必须放在助记符操作码区域。 parameters: 为任选的替代参数,作为宏指令的操作数。 宏程序语句: 每次宏调用时要执行的指令或汇编命令。 宏伪指令: 用于控制宏指令展开的命令。 .mexit: 相当于一条跳到.endm语句。 .endm: 结束宏定义。
【例5-6】宏定义、宏调用和宏展开举例 1 * 2 * add3 4 * 5 * ADDRP=P1+P2+P3 6 1 * 2 * add3 4 * 5 * ADDRP=P1+P2+P3 6 7 add3 .macro P1,P2,P3,ADDRP 8 9 LD P1,A 10 ADD P2,A 11 ADD P3,A 12 STL A,ADDRP 13 .endm 14 第7~14行 定义宏:add3 4个参数:P1 P2 P3 ADDRP
15 16 .global abc,def,ghi,adr 17 18 add3 abc,def,ghi,adr 1 15 16 .global abc,def,ghi,adr 17 18 add3 abc,def,ghi,adr 1 1 000000 1000! LD abc,A 1 000001 0000! ADD def,A 1 000002 0000! ADD ghi,A 1 000003 8000! STL A,adr 第18行 调用宏:add3 所用变量:abc def ghi adr 共4行 扩展宏 将变量传递给参数 abc P1 def P2 ghi P3 adr ADDRP
8.其他伪指令 .mmregs — 定义存储器映射寄存器的名称,这样就可以用AR0、PMST等助记符替换实际的存储器地址。 .end — 程序块结束。
5.4 链接器命令文件的编写与使用 5.4.1 MEMORY伪指令及其使用 5.4.2 SECTIONS伪指令及其使用 返回首页
链接器的主要任务是根据链接命令文件(. cmd),将一个或多个COFF目标文件链接起来,生成存储器映像文件(. map)和可执行的输出文件( 链接器的主要任务是根据链接命令文件(.cmd),将一个或多个COFF目标文件链接起来,生成存储器映像文件(.map)和可执行的输出文件(.out)。 在链接过程中,链接器将各个目标文件合并,并完成以下工作: ● 将各个段配置到目标系统的存储器。 ● 对各个符号和段进行重新定位,并给它们指 定一个最终的地址。 ● 解决输入文件之间未定义的外部引用。
【例5-7】 链接器命令文件举例。 a.obj b.obj /* 输入文件名 */ -o prog.out /*指定输出文件的选项*/ -m prog.map /*指定map文件的选项*/ MEMORY /*MEMORY 伪指令*/ { PAGE 0: ROM:origin=1000h, length=0100h PAGE 1: RAM:origin=0100h, length=0100h } SECTIONS /*SECTIONS伪指令*/ .text : >ROM .data : >ROM .bss : >RAM
注意:在命令文件中,不能采用下列符号作为段名或符号名: align DSECT len o run ALIGN f length org RUN attr fill LENGTH origin SECTIONS ATTR FILL load ORIGIN spare block group LOAD page type BLOCK GROUP MEMORY PAGE TYPE COPY l(小写L) NOLOAD range UNION
5.4.1 MEMORY伪指令及其使用 MEMORY指令用来规定目标存储器的结构。 { PAGE0:name 1[(attr)]:origin=constant, length=constant; PAGEn:name n[(attr)]:origin=constant, length=constant; } 指令字 存储区间说明语句 书写方式:① 已大写MEMORY指令字开始; ② 由大括号括起来的存储器区间说明。 存储区间: 存储页面 区间名称 区间属性 起始地址 区间长度 返回本节
存储区间说明语句: PAGE: 指定存储器空间页面,最多为255页, 取决于目标存储器的配置。 每一个PAGE代表一个完全独立的地址空间。 通常,PAGE 0用于程序存储器; PAGE 1用于数据存储器。 若没有规定PAGE,则链接器默认为PAGE 0。
存储区间说明语句: name: 存储器区间名称。可由用字母、$、.、_ 等组成。 存储器区间为内部记号,因此不需要保留在输出文件或者符号表中。 不同PAGE上的存储器区间可以取相同的名字,但在同一PAGE内的名字不能相同,且不许重叠配置。
存储区间说明语句: attr: 为任选项,用来为命名的存储器区间规 定1~4个属性。 当对输出段定位时,可利用属性限制输出段分配到一定的存储区间。 属性选项共有4项: R 规定可以对存储器执行读操作。 W 规定可以对存储器执行写操作。 X 规定存储器可以装入可执行的程序代码。 I 规定可以对存储器进行初始化。 若未选属性,可将输出段不受限制地定位到任何一个存储器的位置。 任何一个没有规定属性的存储器(包括所有默认方式的存储器)都有全部4项属性。
存储区间说明语句: origin: 用来指定存储区间的起始地址,可简 写为org或o。其值以字为单位,可以 用十进制、八进制或十六进制数表示。 Length: 用来指定存储器空间的长度,可简写 为len或l,其值以字为单位,可以用 十进制、八进制或十六进制数表示。
存储区间说明语句: fill: 为任选项。用来为没有定位输出段的存储 器空单元充填一个数,键入fill或f均可。 该值是2个字节的整型常数,可以是十进制 数、八进制数或十六制数。
【例5--8】用MEMORY伪指令编写连接命令文件。要求: 程序存储器:4K字ROM,起始地址为C00h,取名为ROM。 数据存储器:32字RAM,起始地址为60h,取名为SCR。 512字RAM,起始地址为80h,取名为CHIP。 file1.obj fiel2.obj -o Prog.out MEMORY { PAGE 0: ROM: origin=C00h, length=1000h PAGE 1: SCR: origin=60h, length=20h CHIP: origin=80h, length=200h } 两个输入文件 链接命令选项 指令字 起始地址 页面名称 区间名称 区间长度
【例5-8】用MEMORY伪指令编写连接命令文件。 file1.obj fiel2.obj -o Prog.out MEMORY { PAGE 0: ROM: org=C00h, len=1000h PAGE 1: SCR: org=60h, len=20h CHIP:org=80h, len=200h } 数据存储器 00000h 0FFFFh 程序存储器 00000h 0FFFFh 00060h SCR 20h PAGE 0: ROM: org=C00h, len=1000h 0007Fh 00080h CHIP PAGE 1: SCR: org=60h, len=20h 200h CHIP:org=80h, len=200h 0027Fh 00C00h ROM 1000h 01C00h
5.4.2 SECTIONS伪指令及其使用 SECTIONS伪指令用来控制段的构成与地址分配。 指令功能: ① 说明如何将输入段组合成输出段; ② 在可执行程序中定义输出段; ③ 规定输出段在存储器中的存放位置; ④ 允许重新命名输出段。
1. SECTIONS指令语法 段名:定义输出段的名称。 属性:定义该段的内容和存储器的分配。 SECTIONS指令的句法: 指令字 输出段 { name:[property, property, property, …] } 指令字 输出段 说明语句 段名 属性 属性 属性 段名:定义输出段的名称。 属性:定义该段的内容和存储器的分配。
1. SECTIONS指令语法 段属性用来定义输出段的内容和存储地址的分配。包括的内容如下: ① 装入存储器分配 ② 运行存储器分配 ③ 输入段 ④ 段的类型 ⑤ 充填值
1. SECTIONS指令语法 ① 装入存储器分配 用于定义段装入时的存储器地址。 语法格式: load=allocation 或 allocation 或 > allocation allocation: 关于段地址的说明,即给段分配存储 单元。
1. SECTIONS指令语法 ① 装入存储器分配 例如: .text: load=0x1000 .text: load>ROM .bss: load>(RW) .text: align=0x80 .text: PAGE 0 .bss: load=block(0x80) 将.text段定位到一个特定的地址。 将.text段定位到命名为ROM的存储区。 将.bss段定位到属性为R、W的存储区。 将.text段定位到从地址0x80开始。 将.text段定位到PAGE 0。 将.bss段定位到一个n字存储器块 的任何一个位置(n为2的幂次)。
1. SECTIONS指令语法 ① 装入存储器分配 若用到一个以上参数,可以将它们排成一行。 例如: .text: >ROM (align(16)PAGE (2 )) 。
1. SECTIONS指令语法 链接器为段在目标存储器中分配两个地址: 加载的地址——由装入存储器分配完成 执行程序的地址——由运行存储器分配完成 通常,这两个地址是相同的。 若要想把程序的加载区分开,先将程序加载到ROM,然后在RAM中运行,则用SECTIONS命令让链接器对这个段定位两次即可。 例如: .fir: load=ROM,run=RAM
语法格式: {input_sections} ③ 输入段 用于定义组成输出段的输入段。 语法格式: {input_sections} 大多数情况下,在SECTIONS命令中是不列出每个输入文件的输入段的段名。 例如: SECTIONS { .text: .data: .bss } 链接时:在输入文件中的 所有.text段链接成.text输出段 所有.data段链接成.data输出段 所有.bss段链接成.bss输出段
1. SECTIONS指令语法 用文件名和段名来规定输入段。 SECTIONS { .text: /*创建 .text 输出段*/ f1.obj(.text) /*链接来自f1.obj文件中的.text 段*/ f2.obj(sec1) /*链接来自f2.obj文件中的sec1 段*/ f3.obj /*链接来自f3.obj文件中的所有段*/ f4.obj(.text,sec2) /*链接f4.obj文件中的.text 段和sec2段*/ }
1. SECTIONS指令语法 ④ 段的类型 用于为输出段定义特殊形式的标记 。 语法格式: type=COPY 或 type=DSECT 或 type=NOLOAD ⑤ 充填值 用于对未初始化空单元定义一个数值。 语法格式: fill=value 或 name:…{…}=value
2. SECTIONS指令的使用 【例5-9】SECTIONS指令的使用。 file1.obj file2.obj -o Prog.out { .text: load=ROM,run=800h .con: load=ROM .bss: load=RAM .vec: load=FF80h t1.obj(.int1) t2.obj(.int2) endvec=.; } .data: align=16 两个输入文件 链接命令选项 指令字 输 出 段 说 明 语 句
2. SECTIONS指令的使用 【例5-9】SECTIONS指令的使用。 file1.obj file2.obj -o Prog.out { .text: load=ROM,run=800h .con: load=ROM .bss: load=RAM .vec: load=FF80h t1.obj(.int1) t2.obj(.int2) endvec=.; } .data: align=16 输出段 加载地址 运行地址 .text ROM RAM 800h .con ROM .bss RAM .vec ROM FF80 .data RAM 16位边界
2. SECTIONS指令的使用 【例5-9 】SECTIONS指令的使用。 file1.obj file2.obj -o Prog.out 输出段 加载地址 运行地址 .text ROM RAM 800h .con .bss RAM .vec ROM FF80 .data RAM 16字边界 【例5-9 】SECTIONS指令的使用。 file1.obj file2.obj -o Prog.out SECTIONS { .text: load=ROM,run=800h .con: load=ROM .bss: load=RAM .vec: load=FF80h t1.obj(.int1) t2.obj(.int2) endvec=.; } .data: align=16 ROM RAM 00h .bss .text .text .text 运行时 .con .data FF80h .vec 800h .text
5.5 汇编语言程序编写方法 5.5.1 汇编语言源程序格式 5.5.2 汇编语言中的常数和字符串 5.5.3 汇编源程序中的符号 5.5 汇编语言程序编写方法 5.5.1 汇编语言源程序格式 5.5.2 汇编语言中的常数和字符串 5.5.3 汇编源程序中的符号 5.5.4 汇编源程序中的表达式 返回首页
5.5.1 汇编语言源程序格式 汇编语言程序以.asm为扩展名,可以用任意的编辑器编写源文件。一条语句占源程序的一行,长度可以是源文件编辑器格式允许的长度,但汇编器每行最多读200个字符。因此,语句的执行部分必须限制在200个字符以内。
助记符指令源语句的每一行通常包含4个部分:标号区、助记符区、操作数区和注释区。 1. 源文件格式 助记符指令源语句的每一行通常包含4个部分:标号区、助记符区、操作数区和注释区。 助记符指令语法格式: [标号][:] 助记符 [操作数] [; 注释] 【例5-10 】 助记符指令源语句举例。 NANHUA .set 1 ; 符号NANHUA=1 Begin: LD #NANHUA,AR1 ; 将1加载到AR1 标 号 助记符 操作数 注 释
1. 源文件格式 语句的书写规则: ① 所有语句必须以标号、空格、星号或分号(*或;)开始; ② 标号是可选项,若使用标号,则标号必须从第一列开始; ③ 所有包含有汇编伪指令的语句必须在一行完成指定; ④ 各部分之间必须用空格分开,Tab字符与空格等效;
1. 源文件格式 语句的书写规则: ⑤ 程序中注释是可选项。如果注释在第一列开始时,前面必须标上星号或分号,在其他列开始的注释前面必须以分号开头; ⑥ 如果源程序很长,需要书写若干行,可以在前一行用反斜杠字符(\)结束,余下部分接着在下一行继续书写。
2.标号 所有汇编指令和大多数汇编伪指令都可以选用标号,供本程序或其它程序调用。 ① 标号必须从语句的第1列写起,其后的冒号“:”可任选; ② 标号为任选项,若不使用标号,则语句的第一列必须是空格、星号或分号; ③ 标号是由字母、数字以及下划线和美元符号等组成,最多可达32个字符; ④ 标号分大小写,且第一个字符不能是数字。
在使用标号时,标号的值是段程序计数器SPC的当前值。 例如,若使用.word伪指令初始化几个字,则标号将指到第一个字。 2.标号 在使用标号时,标号的值是段程序计数器SPC的当前值。 例如,若使用.word伪指令初始化几个字,则标号将指到第一个字。 【例5-11】 标号格式举例。 … … 9 000000 10 000040 000A Start: .word 0Ah,3,7 000041 0003 000042 0007 ;假设汇编了某个其他代码 标号,值为40h
3.助记符 助记符用来表示指令所完成的操作,可以是汇编语言指令、汇编伪指令、宏伪指令。 助记符指令:一般用大写,不能从第一列开始 ; 汇编伪指令:用来为程序提供数据和控制汇编进程。以句号“.”开始,且用小写; 宏伪指令:用来定义一段程序,以便宏调用来调用这段程序。以句号“.”开始,且用小写; 宏调用:用来调用由宏伪指令定义的程序段。
4.操作数 操作数是指指令中参与操作的数值或汇编伪指令定义的内容,紧跟在助记符的后面,由一个或多个空格分开。 操作数之间必须用逗号“,”分隔; 操作数可以是常数、符号或表达式; 操作数中的常数、符号或表达式可用来作为地址、立即数或间接地址;
4.操作数 指令的操作数前缀: 汇编器允许指定的常数、符号或表达式作为地址、立即数或间接地址。 作为操作数的前缀有三种情况: 使用“#”符号作为操作数的前缀; 使用“*”符号作为操作数的前缀; 使用“@” 符号作为操作数的前缀。
① 用“#” 作前缀 使用“#”号作为前缀,汇编器将操作数作为立即数处理。即使操作数是寄存器或地址,也将作为立即数。 如果操作数是地址,汇编器将把地址处理为一个数值,而不使用地址的内容。 例如: Label: ADD # 99, B 操作数# 99是一个立即数。
② 用“*”作前缀 使用“*”符号作为前缀,汇编器将操作数作为间接地址,即把操作数的内容作为地址。 例如: Label: LD * AR3, B 操作数*AR3指定一个间接地址。该指令将引导汇编器找到寄存器AR3的内容作为地址,然后将该地址中的内容装入指定的累加器B中。
③ 用“@”作前缀 使用“@”符号作为前缀,汇编器将操作数作为直接地址,即操作数由直接地址码赋值。 例如: Label: LD @ x, A 只要DP=0,将直接地址x中的内容装入指定的累加器A中。
注释可位于句首或句尾,位于句首时,以“*”或“;”开始,位于句尾时,以分号“;”开始。 5.注释 用来说明指令功能的文字,便于用户阅读。 注释可位于句首或句尾,位于句首时,以“*”或“;”开始,位于句尾时,以分号“;”开始。 注释可单独一行或数行; 注释是任选项。 例如: 11 00000 .bss sym, ; 保留空间于.bss ************************************** * 改变段,允许第五个‘mylab’定义 *
5.5.2 汇编语言中的常数和字符串 表5-3 COFF常数与字符串 汇编器可支持7种类型的常数(常量)。 返回本节
字符常数是包括在单引号内的字符串。若单引号之间没有字符,则值为0。每个字符在内部表示为8位ASCII码。 例如: ‘a’ 内部表示为61 h ‘B’ 内部表示为42 h “‘D’ 内部表示为2744h 注意:字符常数与字符串的差别。 字符常数代表单个整数值。 字符串只是一串字符。
5.5.3 汇编源程序中的符号 汇编程序中的符号用于标号、常数和替代字符。 5.5.3 汇编源程序中的符号 汇编程序中的符号用于标号、常数和替代字符。 由字母、数字以及下划线和美元符号(A~Z,a~z,0~9,_和$)等组成; 符号名最多可长达200个字符; 在符号中,第1位不能是数字,并且符号中不能含空格。
1. 标号 作为标号的符号代表在程序中对应位置的符号地址。 通常,标号是局部变量,在一个文件中局部使用的标号必须是唯一的。 标号分大小写。 在调用汇编器时使用-c选项,可以不分大小写。 例如:ABC,Abc,abc是3个不同的符号。
1. 标号 标号还可以作为.global,.ref,.def或.bss等汇编伪指令的操作数。 如: .global label lable1 NOP ADD label,B B label1
2. 局部标号 局部标号是一种特殊的标号,使用的范围和影响是临时性的。 定义方法: ① 用$n来定义。n是0~9的十进制数; ② 用NAME?定义。NAME是任何一个合法的符号名。汇编器用紧随其后一个唯一的数值代替问号。宏中定义标号不声明为全局标号。 注意:局部标号不能用伪指令来定义。
局部标号可以被取消定义,并可以再次被定义或自动产生。 取消局部变量的方法: ① 使用.newblock伪指令; ② 使用伪指令.sect,.text或.data改变段 ; ③ 使用伪指令.include或.copy,进入include文件; ④ 达到include文件的结尾,离开include文件。
【例5-12】合法、非法局部标号$n举例。 假设符号ADDRA,ADDRB,ADDRC已经在前面作了定义。 Label1: LD ADDRA,A SUB ADDRB,A BC $1,ALT LD ADDRB,A B $2 $1 LD ADDRA,A $2 ADD ADDRC,A .newblock STL A,ADDRC $1 NOP ;将ADDRA装入累加器A ;减去地址B ;如果小于0,分支转移到$1 ;否则将ADDRB装入累加器A ;分支转移到$2 ;$1:将ADDRA装入累加器A ;$2:加上ADDRC ;取消$1的定义,使它可被再次使用 ;若小于0,分支转移到$1 ;存ACC的低16位到ADDRC .newblock BC $1,ALT ;若小于0,分支转移到$1 STL A,ADDRC ;存ACC的低16位到ADDRC $1 NOP .newblock ;错误:$1被多次定义
【例5-13】name?形式的局部标号的使用方法。 ;******************************************** ; 局部标号‘mylab’的第1个定义 NOP mylab? NOP B mylab? ;******************************************* .copy“a.inc” ;包括文件中有‘mylab’第2次定义 ;从包括文件中退出复位后, ‘mylab’的第3个定义
;******************************************** ; 在宏中‘mylab’的第4个定义, ; 为了避免冲突,宏使用不同的名称空间 maymac .macro mylab? NOP B mylab? .endm ;******************************************* mymac B mylab? ;宏调用。引用‘mylab'的第3个定义 ;既不被宏调用复位,也不与定义在 宏中的相同名冲突
;******************************************** ; 改变段,允许‘mylab'的第5个定义 .sect “Secto_One” NOP mylab? .word 0 B mylab?
3. 符号常数 符号也可被设置成常数值。为了提高程序的可读性,可以用有意义的名称来代表一些重要的常数值。 伪指令.set和.struct/.tag/.endstruct可以用来将常数赋给符号名。 注意:符号常数不能被重新定义。
【例5-14】 定义符号常数举例。 N .set 512 buffer .set 4 * N nzg1 .set 1 nzg2 .set 2 nzg3 .set 3 AUX .set AR1 MVMM AUX , SP ;定义符号常数 ;将符号常数赋给寄;存器,符号名作为;寄存器的别名
4.预先定义的符号常数 汇编器有若干预先定义符号,包括: ① 美元符号$,代表段程序指针SPC的当前值; ② 映像寄存器符号,包括AR0~AR7; ③ 映像寄存器由汇编器设置为符号。
可将字符串值(变量)赋给符号,使符号名与该变量等效,成为字符串的别名,这种用来代表变量的符号称为替代符号。 5. 替代符号 可将字符串值(变量)赋给符号,使符号名与该变量等效,成为字符串的别名,这种用来代表变量的符号称为替代符号。 当汇编器遇到替代符号时,将用字符串值替代符号。和符号常数不同,替代符号可以被重新定义。可在程序中的任何地方将变量赋给替代符号。 例如: .asg "high",AR2 ;寄存器AR2
5.5.4 汇编源程序中的表达式 1. 运算符 表达式可以是常数、符号,或者是由算术运算符分开的一系列常数和符号。 有效表达式的值: -32 768~32 767 序号 符 号 运算操作 求值顺序 1 + - ~ ! 取正、取负、按位求补、逻辑负 从右至左 2 * / % 乘法、除法、求模 从左至右 3 + - 加法、减法 4 ^ 指数 从左到右 5 << >> 左移、右移 6 < <= 小于、小于等于 7 > >= 大于、大于等于 8 != = 不等于、等于 9 & 按位与运算 10 ∧ 按位异或运算 11 | 按位或运算 影响表达式的主要因素: ① 圆括号( )。圆括号内的表达式最先计算; 不能用大括号{ }或中括号[ ]代替圆括号( )。 ② 优先级。’C54x汇编器使用与C语言相似的优 先级,优先级高的先计算; ③ 从左到右运算。具有相同的优先级,按从左 到右的顺序计算。
2. 合格的表达式 某些汇编器要求合格的表达式作为操作数。操作数是汇编时间常数或链接时可重定位的符号。 合格的表达式是指表达式中的符号或汇编时间常数在表达式之前就已经被定义。 合格的表达式的计算必须是绝对的。
goodsym4 .set label2-label1 【例5-15】 有效定义的表达式。 .data label1 .word 0 .word 1 .word 2 label2 .word 3 X .set 50h goodsym1 .set l00h + X goodsym2 .set $ goodsym3 .set label1 goodsym4 .set label2-label1 ; 将16位值0,1,2放入标号为 label1的当前段连续字中 ; 将3放入标号为label2的字中 ; 定义X的值 ; 有效定义的表达式 ; 引用已定义的所有局部标号 ; 有效定义的表达式
【例-16】无效定义的表达式。 .global Y badsym1 .set Y badsym2 .set 50h + Y badsym3 .set 50h + Z Z .set 60h ; 定义Y为全局外部符号 ; Y在当前文件中未定义 ; 无效的表达式 ; 无效的表达式,Z还未定义 ; 定义Z,但应在表达式使用之前
3. 表达式上溢和下溢 汇编时执行了算术操作以后,汇编器检查上溢和下溢的条件。当出现上溢或下溢时,汇编器会发出一个值被截断了的警告。 汇编器不检查乘法的溢出状态。
对于绝对符号、可重新定位符号以及外部符号的有效操作,可参见下表。 4. 可重新定位符号和合法表达式 对于绝对符号、可重新定位符号以及外部符号的有效操作,可参见下表。 带有绝对符号和可重新定位符号的表达式 如果A为… 并且B为… A+B为… A-B为… 绝对 可重新定位 外部 非法
表达式不能包含可重新定位符号和外部符号的乘或除; 表达式中不能含有对其他的段为可重新定位的符号; 用.global伪指令定义为全局的符号和寄存器也可以用在表达式中。这些符号和寄存器被声明为外部符号; 可重新定位的寄存器也可以用在表达式中,这些寄存器的地址相对于定义它们的寄存器段是可重新定位的,除非将它们声明为外部符号。 返回本节
例如:在下面的程序中,使用了4个定义在相同段的符号。 .global extern_1 intern_1: .word ‘’’D’ LAB1: .set 2 intern_2: .word 3 LD #LAB1+((5+4)*3),A LD #LAB1+3+(4*7),A .global extern_1 intern_1: .word ‘’’D’ LAB1: .set 2 intern_2: .word 3 LD #LAB1+((5+4)*3),A LD #LAB1+3+(4*7),A ;定义在外部的全局符号 ;定义在现行模块中,可重新定位 ;LAB1=2,绝对符号 ;定义在现行模块中,可重新定位 ;LAB1为绝对符号,A=29 ;LAB1为绝对符号,A=33
所有合法表达式可以化简为两种形式: ① 可重新定位符号±绝对符号; ② 绝对符号。 单操作数运算仅能应用于绝对符号,不能应用于可重新定位符号。 表达式简化为仅含有可重新定位符号是非法的。
【例5-17】 判断下列指令中表达式的合法性。 LD extern_1-10,B 合法 LD 10-extern_1,B LD -(intern_1),B LD extern_1/10,B LD intern_1 + extern_1,B 可重新定位 合法 非法 不可将重新定位符号变负 非法 可重新定位符号不可变负 非法 可重新定位符号不可乘除 非法 可重新定位+可重新定位=非法
LD intern_1 - intern_2 + extern_1,B 【例5-18】 判断下列指令中表达式的合法性。 LD intern_1 - intern_2 + extern_1,B LD intern_1 + intern_2 + extern_1,B LD intern_1 + extern_1 - intern_2,B 合法 绝对符号+可重新定位 绝对符号 可重新定义-可重新定义=绝对符号 可重新定位 可重新定位 可重新定位 非法 可重新定义+可重新定义=非法 非法 可重新定义+可重新定义=非法
5.6 TMS320C54x C语言编程 5.6.1 存储器模式 5.6.2 寄存器规则 5.6.3 函数调用规则 5.6.4 中断处理 5.6.1 存储器模式 5.6.2 寄存器规则 5.6.3 函数调用规则 5.6.4 中断处理 5.6.5 表达式分析 返回首页
5.6.1 存储器模式 1.段 C54x将存储器处理为程序存储器和数据存储器两个线性块。程序存储器包含可执行代码;数据存储器主要包含外部变量、静态变量和系统堆栈。编译器的任务是产生可重定位的代码,允许链接器将代码和数据定位进合适的存储空间。C编译器对C语言编译后除了生成3个基本段,即.text、.data、.bss外,还生成.cinit、.const、.stack、.sysmem段。
2.C/C++系统堆栈 .stack不同于DSP汇编指令定义的堆栈。DSP汇编程序中要将堆栈指针SP指向一块RAM,用于保存中断、调用时的返回地址,存放PUSH指令的压栈内容。 .stack定义的系统堆栈实现的功能是保护函数的返回地址,分配局部变量,在调用函数时用于传递参数,保护临时结果。 .stack定义的段大小(堆栈大小)可用链接器选项-stack size设定,链接器还产生一个全局符号_ _STACK_SIZE,并赋给它等于堆栈长度的值,以字为单位,缺省值为1K。
3.存储器分配 (1)运行时间支持函数。 (2)动态存储器分配。 (3)静态和全局变量的存储器分配。 (4)位域/结构的对准。 返回本节
5.6.2 寄存器规则 寄存器规则明确了编译器如何使用寄存器以及在函数调用过程中如何保护寄存器。 (1)辅助寄存器 (2)堆栈指针SP 5.6.2 寄存器规则 寄存器规则明确了编译器如何使用寄存器以及在函数调用过程中如何保护寄存器。 (1)辅助寄存器 (2)堆栈指针SP (3)ARP (4)在默认情况下,编译器总是假定ST1中的OVM在硬件复位时被清0。若在汇编代码中对OVM置位为1,返回到C环境时必须复位。 (5)寄存器变量 返回本节
5.6.3 函数调用规则 (1)局部帧的产生 (2)参数传递 (3)函数的返回 返回本节
5.6.4 中断处理 (1)中断的使能和屏蔽必须由程序员自己来设置。 (2)中断程序没有参数传递,即使说明,也会被忽略 5.6.4 中断处理 (1)中断的使能和屏蔽必须由程序员自己来设置。 (2)中断程序没有参数传递,即使说明,也会被忽略 (3)中断处理程序不能被正常的C程序调用。 (4)为了使中断程序与中断一致,在相应的中断矢量中必须放置一条转移指令,可以用.sect汇编伪指令建立一个简单的跳转指令表来完成此项功能。
(5)在汇编语言中,注意在符号名前面加上一个下划线,例如c_int00记为_ c_int00。 (6)中断程序使用的所有寄存器,包括状态寄存器和程序中调用函数使用的寄存器都必须予以保护。 (7)TMS320C54x C编译器将C语言进行了扩展,中断可以利用interrupt关键字由C/C++函数直接处理。 返回本节
5.6.5 表达式分析 当C程序中需要计算整型表达式时,必须注意以下几点: (1)算术上溢和下溢。 (2)整除和取模。 5.6.5 表达式分析 当C程序中需要计算整型表达式时,必须注意以下几点: (1)算术上溢和下溢。 (2)整除和取模。 (3)C代码对16位乘法结果高16位的访问。 返回本节
5.7 用C语言和汇编语言混合编程 5.7.1 独立的C模块和汇编模块接口 5.7.2 从C程序中访问汇编程序变量 返回首页
5.7.1 独立的C模块和汇编模块接口 在编写独立的汇编程序时,必须注意以下几点: (2)必须保护函数要用到的几个特定寄存器。 (3)中断程序必须保护所有用到的寄存器。 (4)从汇编程序调用C函数时,第一个参数(最左边)必须放入累加器A中,剩下的参数按自右向左的顺序压入堆栈。
(5)调用C函数时,注意C函数只保护了几个特定的寄存器,而其他是可以自由使用的。 (6)长整型和浮点数在存储器中存放的顺序是低位字在高地址,高位字在低地址。 (7)如果函数有返回值,返回值存放在累加器A中。 (8)汇编语言模块不能改变由C模块产生的.cinit段,如果改变其内容将会引起不可预测的后果。
(9)编译器在所有标识符(函数名、变量名等)前加下划线“_”。 (10)任何在汇编程序中定义的对象或函数,如果需要在C程序中访问或调用,则必须用汇编指令.global定义。 (11)编辑模式CPL指示采用何种指针寻址,如果CPL=1,则采用堆栈指针SP寻址;如果CPL=0,则选择页指针DP进行寻址。 返回本节
5.7.2 从C程序中访问汇编程序变量 从C程序中访问在汇编程序中定义的变量或常数,可以分为以下3种情况: (1)访问在.bss块中定义的变量 (2)对于访问不在.bss块中定义的变量 (3)对于在汇编程序中用.set和.global伪指令定义的全局常数,也可以使用特殊的操作从C程序中访问它们。 返回本节
5.7.3 在C程序中直接嵌入汇编语句 在C程序中嵌入汇编语句是一种直接的C模块和汇编模块接口方法。采用这种方法一方面可以在C程序中实现用C语言难以实现的一些硬件控制功能。另一方面,也可以用这种方法在C程序中的关键部分用汇编语句代替C语句以优化程序。 采用这种方法的一个缺点是它比较容易破坏C环境,因为C编译器在编译嵌入了汇编语句的C程序时并不检查或分析所嵌入的汇编语句。 返回本节