




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第6章基于C语言的DSP芯片开发
6.1引言6.2ANSIC编译器6.3C语言编程基础知识6.4基于C语言的DSP芯片开发的运行环境6.5C语言与汇编语言的混合编程本章小结
思考题与习题
6.1
引言
DSP的C语言编程的主要内容包括ANSIC语言编译器,运行环境,运行支持库的使用与建立,I/O编程,C语言编程的各种工具的使用,以及C调试器的使用等主要内容。限于篇幅,本章主要对ANSIC编译器和与C语言编程息息相关的运行环境以及有关C语言基础知识做一介绍。6.2ANSIC编译器6.2.1优化ANSIC编译器
TMS320C3x/C4x编译器支持ANSI(AmericanNationalStandardsInstitute)开发的C语言标准。该标准是使用最广泛的C语言标准,它规定C语言的一些特征受目标处理器、运行环境或主机环境影响。下面就TMS320C3x/C4xC语言的一些不同于标准C语言的特征给予介绍。
1.标识符和常数
(1)所有标识符的前100个字符有意义,区分大小写。这些标示符适用于内部和外部的标识符。
(2)源(主机)和执行(目标)字符集为ASCII码,不存在多字节字符。
(3)字符常数或字符串常数中的十六进制或八进制转义序列可具有高达32位的值。
(4)具有多个字符的字符常数按序列中的最后一个字符来编码,例如:'abc'=='c'。
2.数据类型
(1)整数、双精度等数据类型长度与常见编译器中的数据类型不同,所有的浮点数型都是用二进制浮点数格式来表示的。
(2) size_t(sizeof操作符的结果)定义为unsignedint。
(3) ptrdiff_t(指针加减的结果)定义为int。
3.数据转换
(1)浮点数到整数的转换部分取整数部分。
(2)指针和整数可自由转换。
4.表达式
(1)当两个带符号的整数相除时,若其中一个为负,则其商为负,余数的符号与分子的符号相同。“/”表示求商,“%”表示求余。带符号的模运算取被除数的符号。如:10/-3 =-3;-10/3 =-3;10%-3=1;-10%3 =-1
(2)带符号数的右移为算术右移,即保留符号。
5.声明
(1)寄存器存储类对所有character、short、integer和pointer类型都有效。
(2)结构成员不能被打包成字,除位域外,每个成员对准在16位字的边界。
(3)整数类型的位域带有符号,位域从字的低位开始被打包成字,并且不超过字边界。
6.预处理器预处理器忽略所有不支持的#pragma伪指令,支持#pragma的伪指令见后面的内容。
ANSIC编译器的主要功能是:首先将DSP的C语言程序转化为汇编语言程序,然后按汇编语言程序调试手段进行调试;ANSIC编译器支持代码级调试。采用ANSIC语言进行DSP软件编程具有的优点包括:
1.标准化
C语言是一种非常便于移植的编程语言,尤其是在语言的扩展方面,但缺乏标准化,而ANSIC则为这些扩展提供了新的标准。
2.兼容性
ANSIC标准是K&R标准的超集。在绝大多数编译器下调试并运行的程序都可以在优化ANSIC编译器下运行。ANSIC的兼容性还有另一个含义,就是通过简单的修改即可适用于TI公司的其他芯片。
3.补充新类型补充的新类型主要是指新的const以及Volatile类型。它们允许对C语言程序进行更有效的优化。
4.改进的函数约定函数原型允许改进的类型检查并允许对函数调用进行优化。优化ANSIC编译器时主要考虑了三个方面的优化:
(1)产生的汇编程序在效率上可与手工编写的汇编程序相比;
(2)提供简单的C运行环境的程序接口,使得关键的DSP算法可用汇编语言实现;
(3)为用C语言开发高性能DSP应用程序建立一定规模的库,它们都可以被方便地使用。总的来说,优化ANSIC编译器的主要特征包括:
·与ANSIC功能规范完全兼容。
·具有ANSIC标准运行支持库。
·程序可转化为ROM装载的格式,可重定位、重入。
·提供与汇编语言的接口,允许对时间敏感的程序用汇编语言编写。
·灵活而全面的链接器可以对内存重定位、内存设置、部分链接进行全面的控制,而且还可以对代码进行运行时的重定位。
·提供了一个外壳程序(shell),允许进一步将C语言转化为可执行文件。
·快速的开发速度。
·提供大量有用的提示,以支持对C程序进行方便的编程和调试。
·支持行内扩展。
·可以对库进行管理,包括对库进行文件的增加、删除、或替换,还可以将目标文件库用作链接器的输入。
·提供丰富的列表文件,如源代码和汇编的交叉列表、预处理输出文件等。
·
TMS320C3x/4xANSIC编译器具有可为全局变量、静态变量和常数提供无限空间的大存储器模式。缺省的小存储模式,其空间限制为64K字,使编码执行速度更快、效率更高。C外壳程序提供了许多选项用于控制C编译汇编和链接的过程。
C语言编译汇编和链接的常用的选项如表6.1所示。表6.1C语言编译汇编和链接的常用选项6.2.2优化编译器
C编译器提供了一个优化编译器。采用优化编译可以生成高效率的汇编代码,从而提高程序的运行速度,减少目标代码的长度。在一定程度上可以认为,C编译器的效率主要取决于C编译器所能进行优化的范围和数量。C编译器的优化方法可以分为两类,即通用优化和特定优化。对C语言的通用优化的范围主要包括:·简化表达式;·优化数据流;·删除公共子表达式和冗余分配;·优化跳转;·简化控制流;·优化与循环有关的重复;·将循环体内的计算值不变的表达式移至循环体前;·运行支持库函数的行内扩展;
对DSP芯片的特定优化的主要内容包括:·有效地使用寄存器;·自动增减寄存器寻址方式;·使用块重复;·使用并行指令;·使用延迟跳转。下面主要对浮点DSP芯片的特定优化作一详细介绍。
1)优化寄存器变量的使用编译器将最大程度地利用寄存器存储局部变量、参数及中间值。将数据存储在寄存器中要比将数据存储在存储器中的程序的运行效率高。在将数组结构转化为循环时,这一优化显得尤其有效。例6.1优化寄存器变量的使用。
C语言程序:
int
gvar
;
reg(inti,intj)
{
gvar=call()&i;
j=gvar+i;
}浮点DSP编译器输出程序:
_reg:***R4分配给用户变量i
*R5分配给用户变量j
…
CALL_call ;R0=call()
ANDR4,R0 ;R0&=i
STIR0,@_gvar ;gvar=R0
ADDIR4,R0,R5 ;将gvar保留在R0中
… ;将结果放到R5(j)中编译器在编译时,将i,j分别赋予R4,R5,将gvar保留在R0。这样gvar的运算成了3操作数运算,结果j直接放在RS中。可见,充分利用寄存器可提高程序的运行效率。
2)效率最优的寄存器分配编译器根据用户变量和中间值的类型与使用频率来对寄存器进行分配,在循环体内的变量比其他变量有更高的优先级,相互之间不覆盖的变量可以被分配给同一寄存器。
3)并行指令许多类型的指令,如装载/装载、存储/操作/乘/加,可以配对并行执行。当相邻符号满足并行运行的寻址要求时,编译器使它们并行运行。虽然这一功能已由代码生产工具完成,但优化还是对提高上述情况的运行效率起了很大的作用,因为操作数被更多地放到了寄存器中,进一步提高了运行速度。
4)自增寻址模式对于*P++,*--P这样的表达式,编译器可以使用高效的自增寻址模式。在许多场合,循环中的数组值被逐个使用。如:
for(i=0;i,=N;++i)a[i]…;这时,编译器通过自增寄存器变量指针把数组参数转化为间接寻址参数。
5)块重复浮点编译器通过RPTS和RPTB指令实现过零循环。编译器能发现通过计算机控制的循环,并使用高效的RPTB和RPTS来实现循环。这两种方式中,调整量可以是常数,也可以是表达式。例6.2块重复,自增寻址模式和并行运算。
C语言程序:
floata[1],b[10];
scale(floatk)
{
inti;
for(i=0;i<10;++i)
a[i]=b[i]*k;
…浮点DSP编译器输出:_scale…LDI@CONST+0,AR4 ;AR4=&a[0]LDI@CONST+1,AR5 ;AR5=&b[0]MPYFR4,*AR5++,R0 ;计算第一个结果RPTS8 ;STFR0,*AR4++ ;保存这一结果||MPYFR4,*AR5++,R0;开始计算另一个结果STFR0,*AR4++ ;保存最后一个结果
6)延迟跳转指令浮点DSP支持延迟跳转指令。它与普通的跳转指令比起来,可以节省三个指令周期的运行时间。延迟跳转指令是在执行完其后的三条指令后才发生真正的跳转,编译器尽可能实现无条件的延迟跳转指令,并在计数循环的场合使用条件延迟跳转指令。例6.3延迟跳转指令。
C语言程序:
wait(volatile
int*p)
{
for(;;)
if(*p&0x80)*p|=0xf0;
}浮点DSP编译器输出程序:
_wait:
L6:LDI*AR4,R0 ;R0=*p(AR4被分配给p)TSTB128,R0 ;test*p&0x80BZL6 ;结果为假,采用直接跳转返回BDL6;结果为真,采用延迟跳转返回LDI*AR4,R0 ;R0=*pOR0F0h,R0 ;R0=*p|0xF0STIR0,*AR4 ;*p=R0***BL6;结果为真时的真正跳转发生在这里
7)使用寄存器避免冲突编译器支持一项新的可选用的调用顺序。它通过寄存器而非压入堆栈的方法来避免冲突。这在程序调用时,可以明显改善程序的性能。
8)条件指令在C语言中往往有条件语句,如:
a=conditionalexpr1:expr2或(condition)a=b这时编译器使用条件指令来减少分支。
9)循环体重复块重复并不是用的越多越好,这要视具体情况而定。当编译器发现循环次数与循环体长度都很小时,它将不使用循环结构,改为使用多次重复运行代码段的循环体重复的方式。这样可以避免分支或块重复造成的跳转或判断开销,提高运行效率。6.3C语言编程基础知识6.3.1数据类型所有整数类型(char、short、int、long以及对应的无符号数)都是相同的,即都是由32位的二进制数来表示的。有符号类型由基2的补码表示;字符型类型是有符号类型,等同于整型;枚举类型的对象用32位数来表示,在表达上与整型相似;所有的浮点型(float、double)都是相似的,在TMS320C3x/4x中都是由32位的单精度浮点格式表示的;
Longdouble型浮点格式在TMS320C3x/4x中是由40位的扩展精度格式表示的;数据类型的字长、表达方法、数据范围列于下表6.2;一些值的范围在头文件limits.h作为标准宏利用,头文件limits.h应用在编译器中。注:TMS320C3x/4x类型为32位表6.2数据格式长双精度浮点数据类型不同于其他的浮点数据类型。长双精度浮点数据的特性如下:
(1)长双精度浮点数据类型不是用浮点和双精度所用的单精度32位格式表示的,而是用40位扩展精度格式表示。
(2)长双精度要求两个存储器字:第一个字保存高24位,第二个字保存低24位,操作的数据长度为40位,在装载和保存该长双精度浮点格式需要两条指令。
(3)长双精度保存在一个浮点寄存器中,像短浮点和双精度浮点一样,在寄存器中传输参数。
(4)寄存器变量都不是40位值;因此长双精度不能通过CALL保存为寄存器变量。在CALL之前保存在存储器中,在需要时还可以重新装载。
(5)在浮点数/双精度浮点到长双精度浮点转换时,40位寄存器的低8位用0填满。在长双精度浮点到浮点/双精度浮点转换时,常用RND指令把此值取舍为最接近的单精度浮点数值。
(6)若加、减和负值(取反)指令用扩展精度浮点数作为输入,可以用汇编指令实现,如C3X乘法用函数MPY_LD,除法用函数DIV_LD实现。
(7) 40位的汇编函数链接到名为 .float40的自身的段中。6.3.2关键字
C编译器中涉及的关键字主要包括C寄存器关键字和说明C编译器如何访问全局和静态变量以及如何调用函数的near和far关键字。
1.C寄存器关键字
TMS320C3x/4x编译器通过增加C寄存器关键字扩展C语言,从而使高级语言访问寄存器。当用户在目标文件中用C寄存器关键字时,编译器就会把目标文件名称与为C3x/C4x列出的标准控制寄存器的名称作比较。如果名称匹配,则编译器就生成控制寄存器的参考代码。如果名称不匹配,则编译器就报告出错。有效的控制寄存器如表6.3所示。表6.3有效的控制寄存器
C寄存器关键字仅用在显示文件中,不必在函数中作任何的声明。可应用在整数或指针类型对象中,但不允许应用在浮点或结构型或逻辑对象中。
C寄存器关键字指明对象不能是变量。如果所用的控制寄存器是变量(即可通过外部控制来改变其值),那么这个对象也可以用变量关键字声明。如果要用表6.3中的控制寄存器,用户必须按照下面方式声明:externcregistervolatileunsignedintregister;一旦声明了寄存器,用户就可以直接用这个寄存器的名称。例6.4定义和使用控制寄存器
externcreglsgervolatileunsignedintIE;
externcreglster
volatiltunsignedintIF;
externcreglsgervolatileunsignedintIOF;
externcreglster
volatiltunsignedintST;
unsignedmyfunc{unsignedintmask}
{
while{ST&maskintmask–0}/*Donothing;wait*/;
returnIOF;
}
2.near和far关键字
C3x/C4x的C编译器扩展了C语言功能,增加的near和far关键字是用来说明C编译器是如何访问全局和静态变量以及如何调用函数的。在句法上,near和far关键字被看做存储类变址数,它们可以出现在保存类声明和类型的之前、之后和中间。两个保存类变址不可以在一个声明中同时应用,例如:
farstaticintx;
staticnearintx;
staticintfatx;
farintf∞();
staticfarintf∞();
1) near和far数据对象全局和静态数据对象可以通过下面两种方式访问:near关键字——编译器对数据的访问采取相对数据页指针方式,如:stir0,@_var
far关键字——编译器不能通过DP访问数据。但是如果数据的量大于DP所允许的32K字时,就可以采用DP访问,例如:
ldiu@cll,ar0stir0,*ar0这里cll是包含在.bss中的变量的地址。默认情况下,编译器产生小的存储模型代码,这意味每个数据对象都被当作near关键字,除非它被声明为far关键字。如果对象声明为near关键字,一般的用相对于数据页指针的偏移寻址。数据页指针指向 .bss段的开始。如果-ml,-mb和-mf这些选项被用,则可改变默认的设定值。大存储器模型代码,意思是每个数据对象被当作far关键字来处理并生成相应的代码。如果用DATA_SECTION,对象就会被确认为far变量,并且不能更改。这样就保证对不同数据页中的变量进行存取。
2) near和far函数调用函数调用可采用以下两种方式之一
near关键字——编译器假定调用的目的操作数在所调用的224字节内,编译器用PC相对寻址方式调用指令:
call_func
far关键字——用户通知编译器调用的不在调用的224字节之内,编译器所用的调用指令为寄存器寻址模式:
ldiu@cll,ro
callu,ro这里cll常量在.bss中为函数的地址。6.3.3寄存器变量
TMS320C3x/4xC编译器对寄存器变量的处理依靠用户是否用优化器来决定。用优化器的编译——编译器忽略寄存器的任何声明,在优化时把所有的变量都当作寄存器变量来处理。不用优化器的编译——如果用户用了寄存器关键字,建议把该变量分配到所选的寄存器中。8个寄存器在每个函数中都用作寄存器变量。·寄存器R4和R5保留为整数寄存器变量;·
寄存器R6和R7保留为浮点数寄存器变量;·四个寄存器AR4~AR7保留指针或整数变量。任何标量(整数、浮点数或指针)的对象都可以声明为寄存器变量。寄存器标示符被忽略为任何类型的对象。寄存器保存类是有其意义的参数,或是逻辑变量。如果应用基于栈的调用约定,那么声明作寄存器的参数正常地传入到堆栈并通过函数入口移入到寄存器中。这有利于参数在函数中访问该参数。如果参数不被声明为寄存器,那么参数就会在函数中分配一定的逻辑空间并保存起来,作为函数开始执行的地方。6.3.4Pragma
指令(预处理指令)
Pragma
指令指示编译器所对应的处理器如何处理函数。TMS320C3x/C4xC编译器支持下面的pragmas:
CODE_SECTION
DATA_SECTION
FUNC_CANNOT_INLINE
FUNC_EXT_CALLED
FUNC_IS_PURE
FUNC_IS_SYSTEM
FUNC_NEVER_RETURNS
FUNC_NO_GLOBAL_ASG
FUNC_NO_IND_ASG
INTERRUPT
这里所指的func和symbol必须在文件内,也就是说,用户不能在函数体内定义和声明它们。即用户必须确定该pragma在函数体外,并且必须声明、定义所用到的func
和symbol操作。如果用户不遵循以下规则,则编译器会发出警告。
1) CODE_SECTIONPragma此pragma为段名sectionname分配一个代码空间,语法如下:#pragmaCODE_SECTION(symbol,"sectionname");该段在编译器用 .sect汇编器指令声明。如果段名的长度大于8个字符,用户需要应用COFF2声时。如果用户想把目标代码与从.bss段分离出来的空间链接,则该pragma是有用的。例6.5CODE_SECTION预处理的使用
(a)Csourcefile
#pragmaCODE_SECTION{fn,"my_sect"}
intfn(intx)
{
returnc;
}
(b)Assemblysourcefile
.sect"my_sect"
.global_fn
2) DATA_SECTIONPragma此pragma为段名sectionname分配一个数据空间,语法如下:
#pragmaDATA_SECTION(symbol,"sectionname");如果用户想把目标数据与从.bss段分离出来的空间链接,则该pragma是有用的。如果段名的长度大于8个字符,则用户需要应用COFF2。例6.6DATA_SECTION预处理的使用。
(a)Csourcefile
#pragmaCODE_SECTION{fn,"my_sect"}
charbufferA[512];
charbufferB[512];
(b)Assemblysourcefile
.global_bufferA
.bss_bufferA,512
.blobal_bufferB
_bufferB: .usect"my_sect",512,1
3) FUNC_CANNOT_INLINEPragma该pragma指示编译器不能扩展有名称的函数。任何用该pragma
命名的函数优于用户以任何其他方式(例如用内部的关键字)指出inlining。该pragma必须出现在声明或用户参考函数内。此pragma的语法如下:
#pragmaFUNC_CANNOT_INLINE(func);自变量func
是C函数的名称,此函数不能是内嵌的。
4) FUNC_EXT_CALLEDPragma当用户用-pm选项时,编译器用程序等级最优化。当用户用这种最优化的类型时,编译器将通过主函数直接或间接的去掉没调用的函数。这样,用户可以通过手控代码调用而不是用主函数。该pragma指明了优化器可以控制C函数,并调用这些C函数或其他的函数。这些函数就作为C语言的入口地址。该pragma的定义必须在声明或用户想引用的函数之前。语法如下:
#pragmaFUNC_EXT_CALLED(func);
变量func是用户所定义的C函数的名称。
5) FUNC_IS_PUREPragma该pragma指明了优化器对有名称函数的影响。编译器按照下面情况处理这种影响:
·如果函数的值不需要,则删除调用的函数;
·
删除相同的函数。该pragma的定义必须在声明或用到的函数之前,语法如下:
#pragmaFUNC_IS_PURE(func);变量func是C函数的名称。
6) FUNC_IS_SYSTEMPragma
该pragma指明优化器可优化有名称的函数的行为,此行为是针对有名称函数的ANSI标准定义的。该pragma的定义必须出现在声明或所用到的函数之前,语法如下:
#pragmaFUNC_IS_SYSTEM(func);变量func是C函数的名称,此函数被当作ANSI标准函数来处理。
7) FUNC_NEVER_RETURNSPragma该pragma指明优化器优化从不返回到所调用函数的位置。该pragma的定义必须出现在声明或所用到的函数之前,语法如下:
#pragmaFUNC_NEVER_RETURNS(func);变量func是不可返回调用C函数的名称。
8) FUNC_NO_GLOBAL_ASGPragma该pragma指明优化器优化没有配置有名称的全局变量和不包含asm语句的函数。该pragma的定义必须出现在声明或所用到的函数之前,语法如下:
#pragmaFUNC_NO_GLOBAL_ASG(func);变量func是没有配置的C函数的名称。
9) FUNC_NO_IND_ASGPragma该pragma指明优化器优化通过指针没有配置和不包含asm语句的函数。该pragma的定义必须出现在声明或所用到的函数之前,语法如下:
#pragmaFUNC_NO_IND_ASG(func);变量func是没有配置的C函数的名称。
10) INTERRUPTPragma该pragma可使用户用C代码直接处理中断。变量func是函数的名称。语法如下:
#pragmaINTERRUPT(func);除了c_int00需要保留为C程序复位中断名称外,其他中断名称都不需要遵循赋名的规则。6.3.5asm语句
TMS320C3x/C4xC编译器允许用户嵌套TMS320C3x/C4x汇编语言指令。Asm语句在语法构成上与调用有名称的asm一样,用一个常量串声明:
asm("assemblertext");编译器直接把声明串拷贝到用户输出文件 .assemblertext中,并且数据格式必须是双精度的。所有的字符串输出代码保留它们的初始值。例如,用户可以插入一个.string命令,格式如下:
asm("STR:.string\"abc"\");所插入的代码必须是合法的汇编语言语句。像所有的汇编语言语句一样,这一行必须以标识符(label)开始,接下来是空格(blank)、标号(tab),或注释(星号或分号开始)。编译器不检查字符串,如果有错,将由汇编器检查。若需要更多的信息,请参考TMS320C3x/C4x汇编语言工具用户手册。这里写asm语句不必严格按照正常的C语言语法:它们将作为语句或声明甚至在块外出现。在编译模块的开始处插入即可。注意:需要特别小心用asm语句时不要破坏C环境。编译器不检查插入的指令。若插入跳转和标识符到C代码中,则可能引起不可预料的结果;若用到改变段或其他影响汇编环境的指令也可能引起麻烦。在用优化器对汇编语句进行优化时要特别小心。尽管优化器不能删除asm指令,但它可以重新安排asm指令附近的代码顺序,这样就可能引起不可预料的结果。asm命令可使用户访问硬件,而C语言则不可以。6.3.6初始化静态和全局变量
ANSIC标准指明在没有初始化的静态和全局变量时,必须在程序执行前明确地初始化(赋初始化值0)。在程序被装载时,初始化的工作就已经完成。因为装载过程依靠系统的环境,TMS320C3x/C4xC编译器本身不能预先初始化变量。如果用户导入的程序不是预先初始化的变量,用户必须用连接器在目标文件中初始化变量(赋初始值0)。在连接器命令文件中,.bss段用0填充,如下:
SECTIONS
{
...
.bss:{}=0x00;
...
}由于链接器向输出COFF文件中写入了一个赋0的.bss段,这种方法可能使输出文件的尺寸急剧增加。用常量可限定类初始化静态和全局变量。限定类的静态常量和全局变量初始化不同于其他类型的静态常量和全局变量。没有明确初始化的静态常量和全局变量可能会在初始化前被赋0。例如:constintzero;/*maynotbeinitializedtozero*/
初始化为全局变量的所有常量被放置在.cinit段中。在表达式中,初始化为局部变量的所有常量或者放置在CONST表或者用立即寻址装载。限定类常量的初始化值不被放在.const段,因为这个值必须用直接寻址对其访问。利用小的存储器模块,如果该值被放在.const段,则要求.bss和.const段必须在同一页中。CONST表和.const段是不一样的。CONST表包括如下内容:整数常量表达式的宽度多于16位;浮点数常量表达式为指数多于4位或尾数多于11位;整数局部变量初始化值多于16位;浮点数局部变量初始化值为指数多于4位或尾数多于11位;全局变量的地址;字符串常量的地址。.const段内容如下:转变声明表;不许初始化全局变量的字符串常量;用作big模型的CONST表。6.4基于C语言的DSP芯片开发的运行环境6.4.1存储器模式
C编译器把存储器作为单个线性块处理,这个线性块被分成代码和数据子块。C程序生成的每个代码和数据块都被放置在连续的存储器空间中。编译器假定32位的目标存储器地址空间都是可利用的。特别需要注意的是由链接器定义的存储器映射。链接器定义的存储器映射把代码和数据分配到目标存储器中。如果编译器不清楚存储器映射的类型,则所有位置都不能装载代码或数据,或任何位置都被保留为I/O段。编译器生成的可重置代码允许链接器把代码和数据分配到合适的存储器空间中。例如,用户可以用链接器分配全局变量到内部快速RAM中或分配可执行代码到内部ROM中。
1.C编译器生成的段
C编译器对C语言程序编译后可产生6个重定位的代码和数据块,这些块也就是所谓的段,它们可以用不同的方式分配至存储器中,以符合不同系统配置的需要。这6个段可以分为两种类型,一种是已初始化段,另一种是未初始化段。已初始化的段主要包含数据表和可执行代码。C编译器有3个已初始化的段:.text、
.cinit和 .const。
.text段包含所有可执行代码以及浮点数常量;
.cinit段包含初始化变量和常量表;
.const段包含浮点数常量和swich表。未初始化段用于保留存储器空间,程序可以用这些空间在运行时创建和存储变量。编译器支持3个未初始化的段:.bss,.stack和.sysmem。
.bss段为全局和静态变量保留空间,在小模型中,.bss段也为常量表保留空间。在程序运行时,C初始化程序从.cinit段中拷贝数据(可能在ROM中)并把它保存在.bss段中。
.stack段为系统堆栈分配空间。这时存储器用作向函数传递变量以及分配局部变量。
.system段为存储器组合或存储器组,为动态存储器函数malloc、calloc和realloc分配存储器空间。当然,若C程序没有用到这些函数,编译器就不创建.system段。链接器从不同的模式下取出的单个段并且把名字相同的段合并在一起生成输出段。需要清楚的是汇编器可产生3个默认的段((.text、.bss和.data);然而C编译器不用.data段。
C语言程序是由编译器的输出段和汇编器 .data段组成的。连接器从不同的模块取出单个的段并且把名字相同的段合并在一起生成输出段。用户可以把这些输出段放在所需要的任何地址处以满足系统的需要。.text、.cinit、.const和.data段在ROM或RAM中连在一起;.bss、.stack和.system段在RAM中连在一起。需要注意的是,.bss
和.const段必须被分配在同一64K的数据页内。
2.C系统堆栈
C编译器利用TMS320C3x/C4x内置的堆栈机制实现如下功能:
(1)保护函数返回的地址;
(2)分配局部变量;
(3)传递函数变量;
(4)保存临时结果;
(5)保护寄存器;
(6)保护处理器的状态。堆栈操作是从低地址向高地址增加的。编译器利用两个辅助寄存器来管理堆栈:SP:堆栈指针(SP),指向当前堆栈的栈顶;
AR3:帧指针,指向当前局部帧的开始,每一个函数调用时都要在栈顶建立一个新的局部帧,用来保存局部和临时的变量。
C运行环境在调用C函数时自动管理这些寄存器。如果用户在汇编语言使用这些堆栈,一定要确保正确的使用它们。堆栈尺寸可由链接器利用全局符号__STACK_SIZE来设定,对其赋值即定义堆栈的大小。默认的堆栈大小为1K(400h)字,它适合于片内的任何一RAM块。用户也可在链接器的命令行中利用-stack选项和在选项后用常量指明堆栈的大小,以改变堆栈的大小。在系统初始化后,SP所指定的地址为堆栈的的底部。这个地址为.stack段中的首地址。由于堆栈的位置是由.stack段分配所决定的,而实际的位置是在连接阶段确定的。如果用户配置的堆栈在存储器的最后的段(高地址),则堆栈可在存储器空间中无限增长。特别注意的是,由于C编译器不提供检查堆栈溢出的方法,因此必须保证有足够的空间用于堆栈;否则将发生溢出现象而破坏程序的运行环境,从而导致程序的瘫痪。所以在编写DSP程序和存储器资源分配时,要注意防止堆栈溢出的发生。
3.动态存储器分配由编译器提供的运行支持库包含了几个允许用户在运行时动态分配存储器函数(例如malloc、calloc和realloc)。动态分配并不是C语言本身的标准,而是由标准的运行支持函数提供的。为全局pool或heap分配的存储器定义在 .system段中。.system段的大小在链接时用-heap选项来设定,设置的方法是在-heap选项后面加一个常量来指定。同样,链接器也创建一个全局符号__SYSMEM_SIZE,它的值为heap中的字数目,默认大小为1K字。动态分配目标不是直接寻址(经常用指针访问),并且存储器区在一个独立的块中,因此动态存储器区甚至在小的存储器模式下也有无限的尺寸。所以,即使在程序中申请了大的数据目标,也可以使用效率更高的小存储器模式。为了在 .bss段中保留空间,用户可用heap分配大的数组,以代替声明为全局或静态的。例如如下的声明:
structbigtable[10000];用指针和调用函数来替代:
structbig*table
table=(structbig*)malloc(10000*sizeof(structbig));
4.存储器大小模式编译器支持两个存储器模式:大存储器模式和小存储器模式。这两种模式影响.bss如何分配存储器空间。两种模型下的.text和/或 .cinit段的尺寸都不受限制。但两种模型下的单个函数限制在32K字的代码或更少内,这允许编译器在整个函数范围内利用相对条件跳转。
(1)小存储器模式:编译器默认的存储器模式。在这种模式下,要求整个.bss段能匹配一个64 KB字的存储器在数据页内。这意味着在程序中的静态和全局数据的空间必须小于64 K字,并且.bss也不能超过64 K字地址范围。编译器在运行初始化时,将数据页指针寄存器指向.bss的开始。随后,编译器就可以用直接寻址(@)访问.bss中所有的目标(如全局变量、静态变量和常数表),而不用修改DP寄存器。
(2)大存储器模式:大模式和小模式的区别是它不限制.bss块的大小,因此对于全局和静态变量来说,可用空间是无限的。然而当编译器访问保存在.bss中的任何全局或静态变量时,编译器首先保证DP准确的指向目标所在的存储页。为了做到这一点,编译器在每次访问静态或全局数据时,必须用LDP或LDPK指令设置DP寄存器。由于加了这条指令,不仅增加了一个指令字,而且可能引入3个指令周期或2个指令周期。例如,下面是C编译器生成的汇编语言,它是使用LPD指令在访问全局变量x之前设置DP寄存器的例子:***_xisaglobalvariable***
LDP_x;1extraword,1cycle
LDI@_x,R0;3cycles(2pipelinedelays)为了应用大存储器模式,-mb选项调用编译器。两种模式下都不限制.text和.cinit段的大小,单个函数的大小限制在32K字之内。
5.RAM和ROM模型
C编译器产生的代码适合于基于ROM系统中的应用。在这样一个系统中,.cinit段中的初始化表保存在ROM中。在系统初始化期间,C初始化程序把这些表中的数据从ROM拷贝到 .bss中。程序是直接从目标文件装载到存储器中,然后运行该程序的。这样做,避免了.cinit段占用存储器中的空间。用户定义的初始化程序直接从目标文件读取初始化表(而不是从ROM中)并且在装载时间(而非运行时间)直接执行初始化。用户可通过应用-cr链接器选项把部分内容送到链接器。6.4.2目标请求本节说明不同的数据目标如何配置大小、排列和访问。
1.数据类型保存所有的基本类型都是32位宽度并且保存在存储器中。除了位元被合成字,其他的都不能被合并。位元以声明的顺序从低位到高位配置。并不是所有的对象有排列请求的类型,任何类型被保存32位字宽。结构或数组类的单元目标被单个保存,但单元不被排列为结构或数据(除非单元是位元)。整数类型的char,short
int
和long与无符号的是一样的。列举类型的目标作为32位的字对待。浮点数和双精度类型与TMS320C3x/4x指明的32位浮点数格式相同。长双精度类型为40位
2.长立即数
TMS320C3x/C4x指令没有立即操作数长度超过16位的。编译器有时需要用常量长度大于16位。有符号整数常量有效的非符号位多于15位,无符号整数的有效位多于16位,或浮点数常量的尾数有效非符号位多于11位。
3.寻址全局变量编译器生成的全局和静态符号的地址为变址寄存器或操作指针。因为这些地址为32位宽度,而立即操作数限制在16位,所以这些地址当作长常量处理。
4.字符串常量在C语言中,字符串常量以下面两种方式应用:
(1)初始化字符数组,例如:chars[]="abc";当字符串被初始化时,它常常被当作已初始化数组处理,每个字符都必须被初始化。
(2)字符串用在表达式中,例如:strcpy(s,,"abc");
当字符串用在表达式中时,字符串本身用.byte汇编器指令在.const段中定义,与唯一的标号一起指向字符串。在下面的例子中,标号SL5指向上面例子的字符串:
.const
SL5.byte“abc”,0字符串标号有SLn,其中n为编译器分配的一个标号,这些标号从0开始逐渐加1,在源模块中所有的字符串都在编译的汇编语言模块的结束处定义。标号SLn代表字符串常量的地址。编译器用这个标号在表达式中引用该字符串。像所有的静态目标一样,为了能对这个地址访问,它必须保存在常量表中。因此处理在.const段中保存字符串本身外,编译器还用下面的指令保存常量表中字符串地址:.wordSLn。如果同样的字符串在源模块中被多次引用,则该字符串将不在存储器中复制。一个确定的字符串常量所有应用都必须先对字符串单个定义。因为字符串保存在.const段(可能在ROM)并被分享,因此对于程序来说,修改字符串常量是不实际的。下面的例子是一个不正确的使用字符串的例子:
char*a="abc"
a[1]='x' ;/*Incorrect!*/
5.常量表常量表包含了所有编译器访问的所有的目标,但是字长太宽时不能用作立即操作数。这样的目标如下:
(1)整数常量宽度多于16位;
(2)浮点常量指数多于4位或尾数位数多于11位;
(3)整数局部变量初始化长度多于16位;
(4)浮点数局部变量初始化指数多于4位或尾数多于11位;
(5)全局变量的地址;
(6)字符串常量的地址。常量表仅仅是一个存储器块包含了所有的目标。编译器建立的常量表通过应用.word和.float汇编指令在源模块的结束处。表中的每一个入口占用一个字。CONST标号为表的起始地址,例如:
CONST: .word011223344h;32bitconstant
.float3.14159265;floating–point
.word_globvar;addressofglobal
.wordSL23;addressofstring在表中的目标用直接寻址访问,如:
LDI@const+offset,R0在这个例子中,偏移offset被放入到所需目标的常量表中。它与字符串常量一起,用在源模块中明确的常量。在常量表中它们分享一个入口。在大存储器模式中,常量表在.const段中建立(不能在RAM中拷贝)。编译器必须保证DP寄存器在访问表中的目标之前(与访问全局变量完全一样)正确的装载。这就要求在对常量表的每一次访问时都需要LDP指令。然而,在小存储器模式中,为了避免正装载的DP被覆盖,故要求直接寻址的所有全局变量以及常量表目标被保存在同一存储器页中。当然,全局变量必须保存在RAM中。对于ROM中的代码,常量表必须在ROM中,为了在同一页取出它们,引导程序必须从临时保存在ROM的常量表拷贝到RAM中全局页中。编译器通过向 .cinit段中的常量表放置数据来完成该项工作并且在表 .bss中分配空间。这样常量表就通过自动操作自动地在RAM中建立。特别需要注意是,小存储器模式对全局数据页的总尺寸限制在为64K字以内。正如所有的初始化一样,用户需要避免.cinit段要求的存储器的额外应用通过-cr链接器选项和智能的引导程序直接实现初始化。6.4.3寄存器规则在C编译环境中,对寄存器分配和操作有严格的规则。如果用户要求汇编语言程序与C程序接口,掌握这些寄存器规则是很重要的。寄存器规则明确了编译器如何使用寄存器以及在函数调用过程中如何保护寄存器。有两种寄存器变量:在调用时得到保护和在入口时得到保护。这两种方式的不同在于,调用程序时保护的方法不一样。调用函数时,被调用函数的功能是保护某些寄存器,这些寄存器就不必由调用者保护。如果调用者需要使用没有保护的寄存器,则调用者在调用函数之前必须予以保护。需要注意的是,编译器使用寄存器的方式对是否使用优化选项(-o)是不同的。对优化编译来说,需用额外的寄存器作为寄存器变量以提高程序的运行效率。但是函数调用时保护寄存器的规则是一样的。表6.4列出了编译器如何应用寄存器并指明了哪些寄存器在函数调用时得到保护。表6.4寄存器使用和保护规则寄存器未优化时的使用优化时的使用调用时的保护情况R0整数和FP表达式标量返回值整数和FP表达式标量返回值不保护R1整数和FP表达式整数和FP表达式不保护R2~R3整数和FP表达式整数和FP寄存器变量不保护R4~R5整数寄存器变量整数和FP寄存器变量保护整数部分R6~R7FP寄存器变量整数和FP寄存器变量整数寄存器变量保护浮点数部分AR0~AR1指针表达式指针表达式不保护AR2指针表达式整数和指针寄存器变量不保护AR3帧指针(FP)帧指针(FP)保护AR4~AR7指针寄存器变量指针寄存器变量保护IR0扩展的帧偏移量扩展的帧偏移量不保护IR1扩展的帧偏移量整数寄存器变量不保护BK不使用整数寄存器变量不保护RS、RC、RE块(结构)拷贝块结构拷贝,块重复循环,整数寄存器变量不保护SP堆栈指针堆栈指针保护DP访问全局变量(仅大模式)访问全局变量(仅大模式)小模式保护大模式不保护
1.寄存器变量寄存器变量是定义在寄存器而不是在存储器中的局部变量或编译器临存值。在寄存器中保存局部变量可以加快访问的速度,从而提高编译代码的执行效率。有8个寄存器用于存储寄存器变量,如表6.5所示。表6.5用于寄存器变量的寄存器上述寄存器在函数调用时得以保护。当函数使用寄存器变量时,必须在在每次使用时保护寄存器的内容,然后在函数返回时予以恢复保存的内容。这样就保证了被调用函数不破坏调用者的寄存器变量。当用户不用优化器时,用户用寄存器关键字分配寄存器变量。如果寄存器中的值在调用时需要保护,那么就需要代码生成器按照表6.4列出的分配寄存器变量。如果函数声明的寄存器变量多于所能用的,那么多余变量被处理为自动变量并且被保存在存储器的局部帧中。当用户用优化器时,编译器则忽略寄存器关键字,并且把所有的变量当作寄存器变量处理。编译器分配尽可能多的变量给寄存器,这基于变量维持的时间以及应用变量的时间。优化器使用的额外寄存器如表6.6所示。表6.6优化器使用的额外寄存器一般地,未列在表6.5中的寄存器不能在函数调用时得到保护,因此它们仅用作变量,该变量在任何调用时都不覆盖。然而,如果-pm壳选项被用来产生一个汇编文件,编译器就更加准确地决定寄存器的用途并且可以安全地使用在表6.4中列出的寄存器中。
2.表达式寄存器编译器可将那些不被用作存储变量的寄存器用来存储计算式的中间结果。编译器可跟踪每个寄存器当前的内容,并且以保护寄存器内容的方式尽可能地将寄存器分配给表达式。这使得编译器能够重复使用寄存器数据,充分利用TMS320C3x/C4x的高效的寄存器寻址模式,从而可避免对变量和常量不必要的访问。表达寄存器中的内容在函数调用时不被保护。当函数调用时,被用作保存临时数据的任何寄存器在函数调用前被保存为局部帧。这样就避免了被调用函数必须得到保护和重复使用表达式寄存器。如果编译器需要另外一个寄存器保存计算表达式,则正被用作保存临时数据的寄存器被保存为局部帧并用作分析表达式。
3.函数返回值一般地,当函数返回任何类型的数据(整数、指针或浮点数)时,函数返回的值都存放在R0。然而,在寄存器变量调用规则中,指针从AR0中返回。
4.堆栈和帧指针
TMS320C3x/C4xC编译器采用传统的机制来分配局部变量(非寄存器变量)并把参数传递给函数。当函数要求局部保存时,函数就会在堆栈中产生它自己的工作区(即局部帧)。局部帧在函数调用时建立,在函数返回时撤销并重新分配。堆栈指针(SP)和帧指针(FP)这两个寄存器用于管理堆栈和局部帧。SP是堆栈管理的寄存器,编译器使用SP的传统方法为:随着高地址,堆栈增加并且堆栈指针指向堆栈的顶部。寄存器AR3定义为帧指针(FP)。FP对当前的函数来说指向局部帧起始或底部。所有在局部帧保存的所有数据用FP间接寻址。
SP和FP在函数调用时必须得到保护。函数调用自动保护SP是因为在调用函数时被压入堆栈,从而在返回时弹出堆栈。
5.其它寄存器除了上述寄存器外,其他寄存器具有特殊的功能。数据页指针(DP):用来访问全局和静态变量。在小存储模式下,DP在程序开始运行时设置并且设置后从不改变。DP在小模式下可通过汇编语言修改,此时必须给予保护。索引寄存器(IR0和IR1):用于数组下标及偏移量超过8位时的间接寻址。另外,这两个寄存器在不用作其它目的时,优化器把其用作通用的整数型寄存器变量。在函数调用时都不被保护。
BK寄存器:仅被优化器用作整数寄存器变量,其值在函数调用时不被保护。块重复寄存器(RC,RE和RS):在分配大于5个字的结构时,编译器用以产生有效的块重复操作。这些寄存器如果不被用作块重复操作时将被用作整数寄存器变量。重复寄存器值在函数调用时不被保护;因此RPTB和RPTS指令不能在循环结构内部。6.4.4函数结构和调用规则
C编译器对函数调用有一系列严格的规则。除了特殊的运行支持函数外,任何函数被C函数调用或调用C函数的汇编语言时均必须遵守这个规则。否则可能破坏C运行环境,造成程序失败。典型的函数调用的例子如图6.1所示。在这个例子中,表示出了函数中参数传递及函数应用局部变量的情况。在调用函数之前的堆栈SP指向最后保存的寄存器,FP指向前一个FP。压入自变量堆栈显示的是在执行已经调用函数的第一汇编指令之前的堆栈。局部帧堆栈显示的为所有帧分配后的堆栈。此堆栈中,SP指向最后保存的寄存器。这个例子说明了已调用函数的局部帧的分配情况。函数没有参数传递给堆栈以及没有局部变量分配给局部帧。如果用户用寄存器参数模式,则某些变量或全部变量将通过寄存器传递而不是用堆栈传递。
1.采用标准运行模式的函数调用一般地,采用标准运行模式进行函数调用时,需要执行下面一系列工作:
(1)调用程序将参数压入堆栈。压入时按照反序进行,即最右边的参数被首先压入,最左边参数被最后压入。因此,当函数调用时,把最右边的参数放入堆栈的顶端。
(2)调用程序调用函数。
(3)当被调用函数执行结束时,调用程序用下面指令将参数弹出堆栈:SUBIn,SP其中,n是为压入堆栈的参数的序号。图6.1在函数调用期间的堆栈的应用
2.采用寄存器参数运行模式的函数调用一般地,采用寄存器参数运行模式进行函数调用时,需要进行下面一系列的工作:
(1)有6个寄存器用来传递参数。它们是AR2、R2、R3、RC、RS和RE。按照下面用法传递参数:①前2个浮点参数(float、double、longdouble)用R2和R3传递。②整数或指针变量用上面列出的其它寄存器传递,顺序如6个寄存器列出的顺序。注意structuresandunions作为整型自变量通过地址传递。③所有没有分配给寄存器的参数,或者由于类型不匹配或所有寄存器已经用完,按照反序压入堆栈。第7个寄存器AR0通过地址返回到structuresandunions。下面列出几个函数原型及其寄存器的参数情况。例6.7寄存器参数规则。
intf1(int*a,intb,intc); /*函数调用*/AR2 R2R3用来放参数的寄存器
intf2(inta,floatb,int*c,structAd,floate,intf);
AR2 R2RCRSR3RE
intf3(inta,int*b,floatc,intd,floate);
R2 AR2R3RCSTACK
intf4(structxa,intb,intc,intd,inte,intf,intg,inth);
AR2 R2R3RCRSRESTACKSTACK当一个函数在说明中用省略号表明参数数目可变时,上述规则稍作变化。最后一个明确说明的参数用堆栈传递,这样它的堆栈地址可用作访问没有说明的参数的参考,如:
intvararg(inta,intb,…);
AR2STACKSTACK…
(2)调用程序调用函数。
(3)如果有参数被压入堆栈,则调用程序将参数弹出堆栈。
3.支持运行函数的寄存器变量模式运行支持的汇编文件采用了寄存器参数调用规则的修正版本。有4个寄存器,它们是R1、R2、R3和R4,按照下面的方式传递参数:
(1)整数、浮点和指针参数可在在上面列出的寄存器中传递;
(2)没有分配给寄存器的参数(原因是类型不匹配或者是寄存器已用完)按照反序压入堆栈。第5个寄存器AR0被用来通过地址返回到structuresandunions。AR0也可以用来返回指针。
4.被调用函数的工作当一个函数被另一个函数调用时,必须按照下面的步骤(1)~(7)工作。但是如果函数没有局部变量、没有堆栈参数和不需要保存局部临时参数时,下面的(1)和(6)不执行。
(1)被调用函数设置局部帧。局部帧以下面的方式分配:①将原来的帧指针FP压入堆栈保护;②将新的帧指针设置为当前的SP;③将帧的大小加到SP。
(2)如果所调用的函数改变下面任一个寄存器,则必须压入堆栈以保护它们的值。被调用函数可能在没有保护寄存器内容时就修正寄存器。
(3)执行函数代码。
(4)若函数的返回值为整数、指针或浮点数,函就数会把返回的值放入R0寄存器中。在修订的寄存器参数调用规则中,指针返回到AR0寄存器。若函数返回置是一个结构,则调用函数为这个结构分配空间,然后返回空间的地址用AR0传递给已调用的函数。为了返回一个结构,被调用的函数将结构拷贝到AR0指向的存储器块中。但是调用函数如果不用返回值,则将AR0置为0,通知被调用函数不用拷贝返回结构。在这种方式中,调用函数可智能地指定调用函数结构返回处。例如:在语句s=f()中,s是结构,f是返回结构的一个函数,调用程序把s的地址放在AR0并且返回f。函数f直接把返回的结构拷贝到s,自动地执行参数。用户一定特别注意正确声明返回结构的函数。
(5)调用函数恢复所有被保护的寄存器。
(6)如有必要,撤销局部帧。即将SP减去局部帧的大小,并且恢复原来的FP。
(7)恢复返回地址,并跳转至该地址。函数中没保存的寄存器可通过执行RETS语句完成该功能。在其它函数中,编译器把返回地址装载到R1寄存器,并且利用延迟跳转返回。延迟操作用来恢复寄存器的值和重新分配局部帧。
5.访问函数参数和局部变量函数通过FP间接地访问它的地址参数和局部非寄存器变量,FP总是指向局部帧的底部。因为FP实际指向原来的FP,因而第一个局部变量寻址方法为:*+FP(1)其它的局部变量寻址方法与此相似,只不过将偏移量递增而已,如第二个变量的寻址方法是 *+FP(2)。这种方法的最大偏移是255。当局部变量偏移值超过255时,在访问变量时,首先将偏移量值装入到索引寄存器IRn中,然后用 *+FP(IRn)的方法寻址。与局部变量的寻址方法相同,堆栈参数的寻址也是采用FP加偏移量的寻址方法。所不同的是,这里的偏移量为负值。由于返回地址直接存在FP的下面,所以第一个函数参数的寻址方法为*-FP(2)其他参数的寻址偏移依次递增,最大也为255,超过255时用IRn寄存器作偏移。所需注意的是,应尽量避免使用大的偏移量,因为这会增加程序运行的时间。超过255偏移量的寻址方法是:
LDIoffset,IRn
….*+FP(IRn)…采用寄存器参数时,调用函数和被调用函数的类型和数量完全相符,以使调用函数可以找到参数。不用优化编译时,编译器需用参数传递寄存器进行表达式计算,因此参数在函数调用时被拷贝到局部帧中。采用优化以编译时,编译器应尽量将寄存器参数保存在原来的寄存器中。6.4.5中断处理
C程序也可以作为中断程序。当C环境初始化时,初始化程序并不对处理器的中断作任何初始化操作。实际上,由于硬件复位后,所有中断是无效的,因此系统中的中断实际上并没有被使能。若系统中需要中断功能,则必须对相应的中断作相应的处理,由于没有专门的C语句对中断进行使能或屏蔽,因此必须嵌入asm语句。当C程序被中断时,中断程序必须保护所有用到的寄存器。对于扩展精度寄存器来说,由于可能包含整数或浮点数,而中断程序并不能确定寄存器中数值的类型,因此,中断程序必须保护所有的40位数据。下面给出了一个中断服务程序寄存器保护和恢复的例子。在这个例子中有两个局部变量,并且使用了FP、SP、ST、R3、R4和AR4等寄存器。入口保护:PUSH FP ;saveoldFPLDI SP,FP ;setupnewFPADDI 2,SP ;allocatelocalframePUSH ST ;saveSTPUSH R3 ;savelower32bitsofR3PUSHF R3 ;saveupper32bitsofR3PUSH R4 ;savelower32bitsofR4PUSHF R4 ;saveupper32bitsofR3PUSH AR4 ;saveAR4ExitPOP AR4 ;restoreAR4POPF R4 ;restoreupper32bitsofR4POP R4 ;restorelower32bitsofR4POPF R3 ;restoreupper32bitsofR3POP R3 ;restorelower32bitsofR3SUBI 2,SP ;deallocatelocalframePOP FP ;restoreFramePointerPOP ST ;restoreStatusregisterreti请注意R3和R4的高位和低位是如何保存和和恢复的。任何扩展精度寄存器都必须分两部分保存。所有其它的寄存器作为整数保存。
C函数通过利用一个特殊的的函数名,可以直接处理中断。其格式为c_intnn,其中nn为00到99的之间的两位数。例如:c_int01就是一个有效的中断函数名。当编译器遇到这些函数名中的其中一个时,编译器产生的代码就允许函数从中断陷阱中被激活。
C_int00为C程序的入口点,是为系统复位中断保留的。这个特殊的中断程序用于系统的初始化和调用main函数。由于c_int00并没有调用它的程序,因此它不需要保存任何寄存器。如果中断程序不调用其它函数,则只有那些在中断程序中用到的寄存器才予以保护。但是,如果C中断程序调用其它函数,则中断程序将保护所有的表达式寄存器。
C中断程序与其他的C程序一样,因为它可以有局部变量和寄存器变量。然而在说明中断程序时,不能有参数传递。下面是一个中断程序的例子:
intdataindataout
voidc_int05()
{
Datain=sample(dataout);
}中断程序只要按照
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 交通银行2025洛阳市秋招无领导模拟题角色攻略
- 工商银行2025铜川市秋招笔试创新题型专练及答案
- 中国银行2025齐齐哈尔市秋招群面模拟题及高分话术
- 农业银行2025绵阳市秋招群面案例总结模板
- 农业银行2025德阳市秋招群面案例总结模板
- 农业银行2025甘南藏族自治州秋招面试典型题目及参考答案
- 年日用百货购销合同2篇
- 建设银行2025随州市金融科技岗笔试题及答案
- 中国银行2025宿州市秋招群面模拟题及高分话术
- 工商银行2025廊坊市小语种岗笔试题及答案
- 第3课 学习有方法 第2课时(课件)2025-2026学年道德与法治三年级上册统编版
- 2025年幼儿园膳食工作计划
- 2025年中国电信校招试题及答案
- 《建筑工程资料管理》高职土建类相关专业全套教学课件
- 【物理】2025届安徽省普通高中高三下学期高考全真模拟训练(三)试题(解析版)
- 消防队伍管酒治酒课件
- 自然保护区巡护监测员技能测试题库及答案
- 2025年中铁特货物流股份有限公司招聘笔试参考题库附带答案详解
- 中国风设计元素课件
- 陕西省西工大附中2022-2023学年七年级上学期第一次月考英语试卷(含答案)
- 职业等级考评员培训课件
评论
0/150
提交评论