1、三、TMS320C54X的指令系统TMS320C5000指令系统 TMS320C54x的指令集 TMS320C54x的指令集有近两百条指令,按功能分为如下几类:算术运算指令逻辑运算指令程序控制指令装入和存储指令一、算术运算指令算术运算指令可分为如下几类:加法指令减法指令乘法指令乘加指令乘减指令双数/双精度指令特殊操作指令定点DSP中数据表示方法当它表示一个整数时,其最低位(D0)表示1,D1位表示2的1次方,次高位(D14)表示2的14方。如果表示一个有符号数时,最高位(D15)为符号位,0表示正数,1表示负数。例如,7FFFH表示最大的正数32767(十进制),而0FFFFH表示最大的负数-
2、1(负数用2的补码方式显示)。实现16位定点加法C54X中提供了多条用于加法的指令,如ADD,ADDC,ADDM和ADDS。其中ADDS用于无符号数的加法运算,ADDC用于带进位的加法运算(如32位扩展精度加法),而ADDM专用于长立即数的加法。1、 加法指令使用ADD完成加法LD temp1, A;将变量temp1装入寄存器AADD temp2, A ;将变量temp2与寄存器A相加;结果放入A中STL A, temp3;将结果(低16位)存入变量;temp3中。注意:这里完成计算temp3=temp1+temp2,我们没有特意考虑temp1和temp2是整数还是小数,在加法和下面的减法中整
3、数运算和定点的小数运算都是一样的。利用ADDS实现32位数据装入:LD #0,DP ; 设置数据页指针LD 60H,16,A; 将60H的内容装到A的高16位ADDS 61H, A; 将61H的内容加到A的低16位DLD 60H,B; 直接装入32位到B寄存器2、 减法指令TMS320C54x汇编指令集:符号与缩写:ALUARARxBITCCCCondd,DDABDARdmad算术逻辑单元辅助寄存器,通用某一指定的辅助寄存器(0 x 7)被测试位(0 BITC 15)2位的条件码(0 CC 3)条件执行指令中的操作数延迟选项D地址总线DAB的地址寄存器16位数据存储器立即地址Dmemdstds
4、t_EABEARextpmadhi(A) Kk3k5数据存储器操作数目的累加器(A或B)相对目的累加器的另一个累加器E地址总线EAB的地址寄存器23位程序存储器的立即地址累加器A中的高位部分(位3216)少于9位的短立即数3位立即数(0 k3 7)5位立即数(-16 k5 15)Dmemdstdst_EABEARextpmadhi(A) Kk3k5数据存储器操作数目的累加器(A或B)相对目的累加器的另一个累加器E地址总线EAB的地址寄存器23位程序存储器的立即地址累加器A中的高位部分(位3216)少于9位的短立即数3位立即数(0 k3 7)5位立即数(-16 k5 15)Dmemdstdst_
5、EABEARextpmadhi(A) Kk3k5数据存储器操作数目的累加器(A或B)相对目的累加器的另一个累加器E地址总线EAB的地址寄存器23位程序存储器的立即地址累加器A中的高位部分(位3216)少于9位的短立即数3位立即数(0 k3 7)5位立即数(-16 k5 15)Dmemdstdst_EABEARextpmadhi(A) Kk3k5数据存储器操作数目的累加器(A或B)相对目的累加器的另一个累加器E地址总线EAB的地址寄存器23位程序存储器的立即地址累加器A中的高位部分(位3216)少于9位的短立即数3位立即数(0 k3 7)5位立即数(-16 k5 15)实现16位定点减法:C54
6、X中提供了多条用于减法的指令,如SUB,SUBB,SUBC和SUBS。其中SUBS用于无符号数的减法运算,SUBB用于带借位的减法运算(如32位扩展精度的减法),而SUBC为带条件的移位减,DSP中的除法就是用该指令来实现的。SUB指令与ADD指令一样,有许多的寻址方式减法指令使用举例SUB *AR1+, 14, Aq SUB A, -8, Bq SUBB 5, Aq SUBB *AR1+, B利用SUBC实现除法在C54X中没有提供专门的除法指令,一般有两种方法来完成除法。一种是用乘法来代替,除以某个数相当于乘以其倒数,所以先求出其倒数,然后相乘。这种方法对于除以常数特别适用。另一种方法是使
7、用SUBC指令,重复16次减法完成除法运算。利用SUBC完成Temp1/Temp2LDtemp1,B;将被除数temp1装入B寄存器的低16位RPT#15;重复SUBC指令16次SUBCtemp2,B;使用SUBC指令完成除法STLB,temp3;将商(B寄存器的低16位)存入变量temp3STHB,temp4;将余数(B寄存器的高16位)存入变量temp4注:实际上是完成整数除法3、 乘法指令实现16定点整数乘法在C54X中提供了大量的乘法运算指令,其结果都是32位,放在A或B寄存器中。乘数在C54X的乘法指令很灵活,可以是T寄存器、立即数、存贮单元和A或B寄存器的高16位。如果是无符号数乘
8、时,请使用MPYU指令。这是一条专用于无符号数乘法运算的指令,而其它指令都是有符号数的乘法。整数乘法举例:RSBX FRCT;清FRCT标志,准备整数乘LDtemp1, T;将变量temp1装入T寄存器MPYtemp2, A;完成temp2*temp1,结果放入A寄存器(32位)FRCT标志为小数方式标志位实现小数乘法在C54X中,小数的乘法与整数乘法基本一致,只是由于两个有符号的小数相乘,其结果的小数点的位置在次高的后面,所以必须左移一位,才能得到正确的结果。C54X中提供了一个状态位FRCT,将其设置为1时,系统自动将乘积结果左移移位。两个小数(16位)相乘后结果为32位,如果精度允许的话
9、,可以只存高16位,将低16位丢弃,这样仍可得到16位的结果。小数乘法举例SSBX FRCT;FRCT=1,准备小数乘法 LD temp1,16,A;将变量temp1装入寄存器A的高16位 MPYA temp2 ;完成temp2乘寄存器A的高16位,结;果在B中,同时将temp2装入T寄存器 STH B,temp3;将乘积结果的高16位存入变量temp3如:0.1(0x0ccd) * 0.7(0x599a) = 0.06997680664063(0x08f5 F0A4)4、 乘加与乘减指令:5、 双精度/双数操作指令6、 特殊指令数据归一化相关指令EXP、NORM归一化一个数是先求其指数,然后
10、把它调整到最大精度格式。实现方法如下:二、逻辑运算指令逻辑指令包括与、或、异或、移位和测试指令1、 与指令(AND)2、 或、异或指令3、移位和测试指令三、程序控制指令程序控制指令包括:分支指令、调用指令、中断指令、返回指令、重复指令、堆栈操作指令、混合程序控制指令四、装入和存储指令装入和存储指令包括:一般的装入和存储指令2、存贮指令5、并行存储和加、减、乘法指令6、并行装入和乘法指令7、混合装入和存储指令注意:同一条指令在不同存储器中可能有不同执行时间操作数在双寻址空间、单寻址空间和外部程序代码在双寻址、单寻址和外部空间操作数和代码在相同存储器块中数据空间插入了等待周期程序空间插入了等待周期
11、特殊指令使用说明FIRS指令 FIRS指令用于线性相位滤波器的处理。一个如下图的8阶线性相位滤波器的输出表达式:FIR滤波的两指令MAC、FIRS数据块移动MVDD、MVDP、MVPD在C54x系列DSP,数据与数据存储器、数据与程序存储器之间可以方便的进行数据传输,结合单指令循环可实现数据块移动。如16个系数的移动:最小均方运算LMS在进行自适应滤波等操作中经常会使用LMS算法,C54x提供的LMS指令方便了编程。如下图所示的自适应滤波器设计中,滤波器系数修正公式为:基于LMS的自适应滤波编程数据块移动指令MVDD、MVDP、MVPD在C54x系列DSP,数据与数据存储器、数据与程序存储器之
12、间可以方便的进行数据传输,结合单指令循环可实现数据块移动。如16个系数的移动:4、C语言DSP程序设计一、存储器模式 二、寄存器规则 三、函数调用规则 四、中断函数 五、表达式分析一、存储器模式(5.5.1节)TMS320C2X/C5X定点处理器: 程序存储器:可执行的程序代码; 数据存储器:外部变量、静态变量和系统堆栈。 由C程序生成的每一块程序或数据存放于存储空间的一个连续段中。(一)C编译器生成的块 由C5X编译器编译生成5个初始化段,3个未初始化段和1个.data段(表5.5.1) (二)C系统堆栈定点C编译器采用1个寄存器来管理这个堆栈:AR1作为堆栈指针(SP被保护起来),它指向堆
13、栈的顶部。 激活每个函数时,都在堆栈中建立一个新的帧,以用于分配局部变量和临时变量。C环境能够自动管理这些寄存器。如果需要编写操作堆栈的汇编程序,必须注意这些寄存器的正确使用。定点C编译器的堆栈长度由链接器确定,全局符号_STACK_SIZE的值等于堆栈长度,单位为字节,缺省值为1K字节。同样,需要改变堆栈长度时,在链接时用-stack选项,并在其后指定一个数值。(三)动态存储器分配在运行支持库中,有几个允许在运行时进行动态存储器分配的函数,如malloc、calloc、realloc,动态存储器分配的目标一般采用指针进行寻址。(四)静态和全局变量的存储器分配在C程序中说明的每一个外部或静态变
14、量被分配给一个唯一的连续空间。空间的地址由链接器确定。编译器保证这些变量的空间分配在多个字中以使每个变量按字边界对准。(五)域/结构的对准编译器为结构分配空间时,它分配足够的字以包含所有的结构成员,在一组结构中,每个结构开始于字边界。所有的非域类型对准于字的边界。对域分配足够多的比特,相邻域组装进一个字的相邻比特,但不跨越两个字。如果一个域要跨越两个字,则整个域分配到下一个字中。 二、寄存器规则 (5.5.3节)在定点c编译器中定义了严格的寄存器使用规则。这些规则对于编写汇编语言与C语言的接口非常重要。如果编写的汇编程序不符合寄存器使用规则,则C环境将被破坏。C编译器使用寄存器的方法在使用和不
15、使用优化器时是不一样的。因为优化器需要使用额外的寄存器作为寄存器变量以提高程序的运行效率。但函数调用时保护寄存器的规则在使用和不使用优化器时是一样的。下面我们来介绍定点C编译器使用寄存器的规则。 函数采用汇编编写时,可以使用辅助寄存器、T寄存器和SP寄存器、各种状态寄存器,在使用时必须符合下列规则:1. 辅助寄存器(ARP和AR0 AR7) AR2、AR3、AR4和AR5可以自由使用,也就是说,在函数执行过程中可以修改,被调用函数不会保护这些寄存器,如果要保护的话需要调用者来进行。 AR6和AR7用作寄存器变量。分别分配给第一个和第二个寄存器变量2. 状态寄存器 在C编译器中,ARP、ASM、
16、C、OVA、OVB、OVM、SMUL、SXM和TC可以修改。其他位域不可修改。3. 其它寄存器 累加器A/B可以自由使用,不必保护和恢复。SP、BRC和T寄存器也可以自由使用。(三)寄存器变量 在一个函数中,定点编译器可以自由使用多至两个寄存器变量。如果要在函数中使用寄存器变量,则应在函数的参数表或函数的第一块中定义。否则,作为一般的变量处理。编译器用AR6和AR7作为寄存器变量,其中AR6分配给第一个寄存器变量,AR7分配给第二个寄存器变量。由于在运行时建立一个寄存器变量约需4个指令周期,因此,只有当一个变量访问2次以上,使用寄存器变量的效果才能明显地体现出来。 三、函数调用规则(5.5.4
17、节) C编译器规定了一组严格的函数调用规则。除了特殊函数的运行支持外,任何调用C函数或被C函数所调用的函数都必须遵循这些规则,否则就会破坏C环境。(一)参数传递 将参数传递给一个C函数时,必须遵循下列规则:(1)函数调用前,将参数压入运行堆栈。(2)以逆序传递参数。也就是说:第一个参数(最左边)最后压栈,而最后一个参数(最右边)最先压。 (3)若参数是浮点数或长整型数,则低位字先压栈,高位字后压栈。(4)传递结构时,采用多字方式。(二)局部帧的产生函数被调用时,编译器在运行栈中建立一个帧以存储信息。当前函数帧称为局部帧。C环境利用局部帧来保护调用者的有关信息、传递参数和生成局部变量。每调用一个
18、函数,就建立一个新的帧。(三)函数调用过程 1、父函数将第一个参数(左)置入A,其他参数逆序压栈;SP传给AR1; 2、如果函数返回的是一个结构,则父函数为该结构分配空间,并将返回空间的地址通过A传递给子函数;3、函数调用的7个工作;四、中断函数 在定点C编译器中,中断可以用C函数直接处理。每个中断采用固定的程序名。如下所示:c_int0 系统复位中断c_int1 外部中断0c_int2 外部中断1c_int3 外部中断2c_int4 内部定时器中断c_int5 串行口接收中断c_int6 串行口发送中断c_int7 TDM 口接收中断c_int8 TDM口发送中断c_int9 外部中断3用C
19、语言编写中断程序时,必须注意以下几点:(1)对由SP(AR1)指向的字,编译器可能正在使用,必须加以保护。(2)中断的屏蔽和使能必须由程序员设置,设置的方法是用嵌入汇编语句的方法修改IMR寄存器。这样修改不会破坏C环境或C指针。(3)中断程序没有参数传递,即使说明,也将被忽略。(4)由于用C编写中断程序时,需要保护所有的寄存器,因此效率不高。(5)将一个程序与某个中断关联时,必须在相应的中断矢量处放置一条跳转指令。(6)在汇编语言中,注意必须在中断程序名前加一下划杠。例如,c语言中的c_int1,在汇编语言中为_c_int1。(7)中断程序或在中断程序中需要调用的程序都不能用_oe选项进行优化
20、编译。五、表达式分析当C程序中需要计算整型表达式时,必须注意到以下几点: 1. 算术上溢和下溢。即使采用16位操作数,TMS320C5X也会产生32位结果。因此,算术溢出是不能以一种可预测的方式进行处理的。 2. 整除和取模。TMS320C5X没有直接提供整除指令,因此,所有的整除和取模运算都需要调用库函数来实现。这些函数将运算表达式的右操作数压入堆栈,将左操作数放入累加器的低16位。函数的计算结果在累加器中返回。 3. 表达式分析。下面的一些运算在函数调用时并不遵循标准的C调用规则,目的在于提高程序运行速度和减少程序代码空间。 1)变量的左移;2)变量的右移;3)除法;4)取模(5)乘法4.
21、 C代码访问16位乘法结果的高16位。DSP中增加的C关键字 p280Const:定义常量,保证不被改变例:int * const p = &x; const int * q = &x;Volatile:定义一个变量,保证其不被优化掉 例:unsigned int * ctrl while(*ctrl!=0xFF) volatile unsigned int * ctrlIoport:定义端口 例子:ioport unsigned port10Interrupt:定义一个中断服务函数 例子:interrupt void int_handler()Near/far:定义函数的调用方式 例子: n
22、ear foo()用call来调用 far int foo()用fcall远程调用 定点DSP芯片C程序的开发过程主要分以下几个步骤: 1. 用编辑器(如EDIT、ccs等)编辑一个或多个C程 序,如example1.c,example2.c。2. 编译C程序,形成目标文件,如example1.obj,example2.obj:dspcl_v25_g_mn_o2 example1.cdspc_v50_g_mn_o2 example1.c 命令选项中的_v25表示是TMS320C2X,若是TMS320C5X,则选项为_v50。3. 根据实际应用编辑一个链接命令文件,如example.cmd。ME
23、MORYPAGE0:VECS: origin=0h len=30hPAGE0:PROG: orgin=30h len=0EFDOh /*程序空间*/PAGE1:DATA: origin=800h len=OE800h /*数据空间*/SECTIONS.vectors:VECS/*中断矢量*/.text:PROG PAGE0 /*代码*/.cinit: PROG PAGE0 /*C初始化表*/.switch:PROG PAGE0 /*switch语句表*/.bss:DATA PAGE1 /*变量*/.const:DATA PAGE1 /*常数变量*/.stack:DATA PAGE1 /*系统堆
24、栈*/ .cio: DATA PAGE1 /printf和有关函数 使用的缓存*/.system:DATA PAGE1 /*动态存储器*/ 4. 链接形成example.out: dsplnk example.cmd5. 用C源码调试器进行调试(模拟器、硬件仿真器等)。(1)C程序的结构及组成 / #include包含语句定义程序中使用的函数库对 应的. h头文件 #include “函数库1” #include #include “函数库3” / #define 定义程序中所有的宏替换 #define 宏替换名替换内容 /本程序的内部函数声明,这些函数一般放在main()函数的后面 函数类型
25、 函数名(函数参数列表); /中断服务程序(函数)的声明 interrupt void function_name (void); /全局变量声明 变量类型全局变量名; /主函数main() void main(void) /局部变量定义 for(;) /调用子函数来处理数据 /完成数据的输入和输出功能 /本程序的内部函数 函数类型 函数名(函数参数列表) /本函数的局部变量定义/本函数中的算法 /程序结束(2)C54xx相关的头文件 54xx硬件专用头文件位于C:tic5400dsk5402 include 目录下。可以分为两类: 一类头文件对DSP本身及外部接口电路所拥有的硬件资源进行描述
26、定义,如Regs.h 、Regs54xx.h。 另一类头文件对这些资源的设置和使用的接口函数库进行了描述,使用户可以不关心底层的驱动程序,直接调用库函数即可实现对硬件的控制,这些往往都是硬件开发人员所完成的,如CCS中的board.h 和mcbsp54.h. c5400dsk5402include目录中的部分头文件与dsk板库文件drv5402.lib和dsk5402.lib相关联。drv5402.lib和dsk5402.lib函数库在c5400dsk5402lib目录下,dsk5402.lib是一个主要的库函数,内部使用了drv5402.lib。 c5400cgtoolsinclude目录下
27、的头文件是5000系列DSP的通用头文件,与硬件无关,它们与运行时支持库rts.lib相关联, (3)头文件中存储器映射寄存器(MMR)的定义和访问54X DSP的片内寄存器资源是通过两个头文件Regs.h 和Regs54x.h进行定义的,这些头文件位于 c:tic5400dsk5402include目录下,在这两个文件中定义了C54x DSP中涉及到的所有寄存器及其所包含的控制和状态比特,这些定义是使用宏替换定义#define进行的。MMR的定义和访问 例如:串口控制寄存器中接收移位寄存器满标志位的比特域定义: # define RSRFULL 13 /RSRFULL标志比特位于寄存器的bi
28、t13位置 #define RSRFULL_SZ 1 /此标志的长度为1个比特(4)DSP I/O空间的访问方式1: 在C语言中访问DSP的I/O空间借助于关键字ioport来进行,注意,此关键字只为DSP 54xx的编译器所识别和使用。例如:当访问I/O空间在200H地址时,可以采取以下定义形式:ioport unsigned int port200; unsigned int test; test=port200; /读I/O端口,port200作为一个变量使用port200=test; /写I/O端口,port200作为一个变量使用DSP I/O空间的访问 方式2: 借助于库函数port
29、Read(port)和portWrite(port)对一段连续I/O端口进行读写。在portio.h头中定义了portRead()、portWrite()函数。 例如:#include portio.h /所需要的函数包含在portio.h中 #define portA 0x00/使用宏替换定义I/O端口的地址#define portB 0x01 DSP I/O空间的访问 或typedef enum/使用枚举定义I/O端口的地址 portA=0x00; portB=0x01; cpldReg, *pcpldReg. Variable=portRead(portA);/读I/O端口 portwr
30、ite (portB)=Value;/写I/O端口例: 用C语言编写一个TMS320C5X的输入输出程序,并用simulator进行调试。 /*本程序是TMS320C5X的一个I/O口输入和输出程序,程序从I/O口地址0x0读入8位数据并存入数组中,同时将另一数组的数值写至I/O口地址0x1*/include ioports.h /*包含ioports.h头文件*/define RD_PORT 0x00; /*定义输入I/O口*/define WR_PORT 0x01; /*定义输出I/O口*/int indata5,outdata5; /*定义全局数组*/main() int i; for(
31、i=0;i5;i+) outdata=i2; /*初始化outdata数组*/ for(i=0;i5;i+) /*循环5次*/ inport(RD_PORT,&indata); /*读I/O口*/ outport(WR_PORT,&outdata); /*写I/O口*/用TMS320C5X simulator调试I/O口时,将I/O口与一文件相关联。这里我们建立两个文件RD.DAT和WR.DAT,并将RD.DAT文件初始化为:0x00110x00220x00330x00440x0055上述程序运行结束后,可以观察数组indata及文件WR.DAT。正确的结果应为indata5=0x11,0x2
32、2,0x33,0x44,0x55,文件WR.DAT应为0x00000x00040x00080x00C00x0010 C语言编程例程(语音录放)51_CODEC(实验7) 主要学习 codec_c.c , codec_c.h , codec.s54 , rts.lib及其源码rts.src 考前复习(开卷)5. DSP混合编程程序设计虽然c编译器的优化功能可以使c代码的效率大大增加,但是在某些情况下,c代码的效率还是无法与手工编写的汇编代码的效率相比。这是因为,即使是最佳的c编译器,也无法在所有的情况下都能够最佳地利用DSP芯片所提供的各种资源。用c语言编写的中断程序虽然可读性很好,但由于在进入
33、中断程序后,有时不管程序中是否用到,中断程序都将寄存器进行保护,从而大大降低中断程序的效率。如果中断程序频繁被调用,那么即使是一条指令也是至关重要的。此外,用c语言实现DSP芯片的某些硬件控制也不如汇编程序方便,有些甚至无法用C语言实现。因此,在很多情下,DSP应用程序往往需要用c语言和汇编语言的混合编程方法来实现,以达到最佳利用DSP芯片软硬件资源的目的。C语言和汇编语言的混合编程方法 (1)独立编写c程序和汇编程序,分开编译或汇编,形成各自的目标代码模块,然后用链接器将c模块和汇编模块链接起来。例如,FFT程序一般采用汇编语言编写,形成目标代码模块,与c模块链接就可以在c程序中调用FFT程
34、序;(2)直接在C语言程序的相应位置嵌入汇编语句;(3)对C程序进行编译,生成相应的汇编程序 ,然后手动修改和优化。5.1 独立编程,再链接这是一种常用的c和汇编语言接口方法。采用这种方法时需注意的是在编写汇编语言和c语言时必须遵循有关的调用规则和寄存器规则。如果遵循了这些规则,那么c和汇编函数之间的接口是非常方便的。c程序既可以调用汇编程序,也可以访问汇编程序中定义的变量。同样,汇编程序也可以调用c函数或访问c程序中定义的变量。(1)注意的问题包括存储器、寄存器和函数调用等规则;汇编模块不能改变由c程序产生的.cinit块,如果改变其内容则会引起不可预测的后果。编译器在所有的标识符(函数名、
35、变量名等)前要加一下划杠“_”。因此,编写汇编语言程序时,必须在c程序可以访问的所有对象前加“_”。例如,在c程序中定义了变量x,如果在汇编程序中要使用,要标记为“_x” 。(1)注意的问题任何在汇编程序中定义的对象或函数,如果需要在C程序中访问或调用,则必须用汇编指令.global定义。同样,如果在c程序中定义的对象或函数,需要在汇编程序中访问或调用,在汇编程序中也必须用.gIobal指令定义。从C程序中访问汇编程序变量 从c程序中访问在汇编程序中定义的变量或常数需根据变量或常数定义的方式采取不同方法。总的来说,可以分为三种不同的情形:变量在.bss块中定义;变量不在.bss块定义;常数。对
36、于访问在.bss块中定义的变量,可用如下步骤实现:(1)采用.bss命令定义变量:(2)用.global命令定义为外部变量; (3)在变量名前加一下划线“_”;(4)在c程序中将变量说明为外部变量。采用上述方法后,在c程序中就可以访问这个变量在C中访问汇编变量的例子1从C程序中访问汇编程序变量对于访问不在.bss块中定义的变量,其方法复杂一些。在汇编中定义的常数表是这种情形一种常见的例子。在这种情况下,必须定义一个指向该变量的指针,然后在c程序中间接地访问这个变量。步骤是:(1)申明一个指向该表起始的全局标号。(2)可以为这个表定义一个独立的块,也可以在现有的块中定义。如果定义为一个独立的块,
37、则可以在链接时将它分配至任意可用的存储器空间。(3)在c程序中访问该表时,必须另外申明一个指向该表的指针。 在C中访问汇编变量的例子2从C程序中访问汇编常数对于在汇编中用.set和.global命令定义的全局常数,也可以从c程序中访问,不过访问的方法更复杂些。一般对于在c或汇编中定义的变量,符号实际上包含的是变量值的地址,而非变量值本身。然而,对于在汇编中定义的常数,符号包含的是常数的值。而编译器不能区分符号表中哪些是变量值,哪些是变量的地址。因此,在c程序中访问汇编中的常数不能直接使用常数的符号名,而应在常数名之前加一个地址操作符“”。如在汇编中的常数名为“_x”,则在C程序中的值应为“&x
38、”。 在C中访问汇编常数的例子在汇编程序中访问C程序变量 在编写独立的汇编程序时,经常需要访问在c程序中定义的全局变量或数组。下面的例子介绍了如何在汇编程序中访问c程序定义的变量和数组。5.2 在C程序中直接嵌入汇编语句优点:采用这种方法一方面可以在c程序中实现用c语言无法实现的一些硬件控制功能,如修改中断控制寄存器、中断使能成无效、读取状态寄存器和中断标志寄存器等。另一方面,也可以用这种方法在c程序中的关键部分用汇编语句代替c语句以优化程序。采用这种方法的一个缺点是比较容易破坏c环境,因为c编译器在编译嵌入了汇编语句的c程序时,并不检查或分析所嵌入的汇编语句。在c程序中嵌入汇编语句的例子:
39、嵌入汇编语句的方法比较简单,只须征汇编语句的左右加上一个双引号,用小括弧将汇编语句括住,在括弧前加上asm标识符即可,如下所示。 asm(“ 汇编语句 ”);需要特别注意的是,采用这种方法后,对程序进行编译时不能采用优化功能,否则将使程序产生不可预测的结果。混合编程实例(实验5)源代码文件:Mix.c addfun.s54主程序:main()C编程子程序:mix_func_c(wl,wr,val)C编程 mix_func(wl,wr,val)汇编编程变量的初始化: 主程序用C编写;寄存器的初始化:在主程序中嵌入汇编指令赋值核心算法:可用汇编编写(效率高);可用C编 写(开发时间短);在主程序中调用;堆栈的使用考前复习(开卷)